From 5d0009c763f2daa5daf21ca180f1fcbefd2ddbe9 Mon Sep 17 00:00:00 2001 From: Shu Date: Tue, 3 Nov 2020 23:04:43 +0100 Subject: [PATCH] QUIC protocol --- .vscode/launch.json | 20 + .vscode/tasks.json | 15 + CMakeLists.txt | 34 +- README.md | 4 +- TODO.md | 8 +- .../FastNoiseSIMD/ARM/cpu-features.c | 0 .../FastNoiseSIMD/ARM/cpu-features.h | 0 .../FastNoiseSIMD/FastNoiseSIMD.cpp | 0 .../FastNoiseSIMD/FastNoiseSIMD.h | 0 .../FastNoiseSIMD/FastNoiseSIMD_avx2.cpp | 0 .../FastNoiseSIMD/FastNoiseSIMD_avx512.cpp | 0 .../FastNoiseSIMD/FastNoiseSIMD_internal.cpp | 0 .../FastNoiseSIMD/FastNoiseSIMD_internal.h | 0 .../FastNoiseSIMD/FastNoiseSIMD_neon.cpp | 0 .../FastNoiseSIMD/FastNoiseSIMD_sse2.cpp | 0 .../FastNoiseSIMD/FastNoiseSIMD_sse41.cpp | 0 {include => deps}/FastNoiseSIMD/LICENSE | 0 {include => deps}/gl3w/GL/gl3w.h | 0 {include => deps}/gl3w/GL/glcorearb.h | 0 {include => deps}/gl3w/KHR/khrplatform.h | 0 {include => deps}/gl3w/UNLICENSE | 0 {include => deps}/gl3w/gl3w.c | 0 .../glfw/CMake/GenerateMappings.cmake | 0 .../glfw/CMake/MacOSXBundleInfo.plist.in | 0 .../glfw/CMake/i686-w64-mingw32-clang.cmake | 0 .../glfw/CMake/i686-w64-mingw32.cmake | 0 .../glfw/CMake/modules/FindEpollShim.cmake | 0 .../glfw/CMake/modules/FindOSMesa.cmake | 0 .../CMake/modules/FindWaylandProtocols.cmake | 0 .../glfw/CMake/modules/FindXKBCommon.cmake | 0 .../glfw/CMake/x86_64-w64-mingw32-clang.cmake | 0 .../glfw/CMake/x86_64-w64-mingw32.cmake | 0 {include => deps}/glfw/CMakeLists.txt | 0 {include => deps}/glfw/LICENSE.md | 0 {include => deps}/glfw/README.md | 0 .../glfw/cmake_uninstall.cmake.in | 0 {include => deps}/glfw/deps/getopt.c | 0 {include => deps}/glfw/deps/getopt.h | 0 {include => deps}/glfw/deps/glad/gl.h | 0 .../glfw/deps/glad/khrplatform.h | 0 .../glfw/deps/glad/vk_platform.h | 0 {include => deps}/glfw/deps/glad/vulkan.h | 0 {include => deps}/glfw/deps/glad_gl.c | 0 {include => deps}/glfw/deps/glad_vulkan.c | 0 {include => deps}/glfw/deps/linmath.h | 0 .../glfw/deps/mingw/_mingw_dxhelper.h | 0 {include => deps}/glfw/deps/mingw/dinput.h | 0 {include => deps}/glfw/deps/mingw/xinput.h | 0 {include => deps}/glfw/deps/nuklear.h | 0 .../glfw/deps/nuklear_glfw_gl2.h | 0 {include => deps}/glfw/deps/stb_image_write.h | 0 {include => deps}/glfw/deps/tinycthread.c | 0 {include => deps}/glfw/deps/tinycthread.h | 0 {include => deps}/glfw/deps/vs2008/stdint.h | 0 {include => deps}/glfw/include/GLFW/glfw3.h | 0 .../glfw/include/GLFW/glfw3native.h | 0 {include => deps}/glfw/src/CMakeLists.txt | 0 {include => deps}/glfw/src/cocoa_init.m | 0 {include => deps}/glfw/src/cocoa_joystick.h | 0 {include => deps}/glfw/src/cocoa_joystick.m | 0 {include => deps}/glfw/src/cocoa_monitor.m | 0 {include => deps}/glfw/src/cocoa_platform.h | 0 {include => deps}/glfw/src/cocoa_time.c | 0 {include => deps}/glfw/src/cocoa_window.m | 0 {include => deps}/glfw/src/context.c | 0 {include => deps}/glfw/src/egl_context.c | 0 {include => deps}/glfw/src/egl_context.h | 0 {include => deps}/glfw/src/glfw3.pc.in | 0 .../glfw/src/glfw3Config.cmake.in | 0 {include => deps}/glfw/src/glfw_config.h.in | 0 {include => deps}/glfw/src/glx_context.c | 0 {include => deps}/glfw/src/glx_context.h | 0 {include => deps}/glfw/src/init.c | 0 {include => deps}/glfw/src/input.c | 0 {include => deps}/glfw/src/internal.h | 0 {include => deps}/glfw/src/linux_joystick.c | 0 {include => deps}/glfw/src/linux_joystick.h | 0 {include => deps}/glfw/src/mappings.h | 0 {include => deps}/glfw/src/mappings.h.in | 0 {include => deps}/glfw/src/monitor.c | 0 {include => deps}/glfw/src/nsgl_context.h | 0 {include => deps}/glfw/src/nsgl_context.m | 0 {include => deps}/glfw/src/null_init.c | 0 {include => deps}/glfw/src/null_joystick.c | 0 {include => deps}/glfw/src/null_joystick.h | 0 {include => deps}/glfw/src/null_monitor.c | 0 {include => deps}/glfw/src/null_platform.h | 0 {include => deps}/glfw/src/null_window.c | 0 {include => deps}/glfw/src/osmesa_context.c | 0 {include => deps}/glfw/src/osmesa_context.h | 0 {include => deps}/glfw/src/posix_thread.c | 0 {include => deps}/glfw/src/posix_thread.h | 0 {include => deps}/glfw/src/posix_time.c | 0 {include => deps}/glfw/src/posix_time.h | 0 {include => deps}/glfw/src/vulkan.c | 0 {include => deps}/glfw/src/wgl_context.c | 0 {include => deps}/glfw/src/wgl_context.h | 0 {include => deps}/glfw/src/win32_init.c | 0 {include => deps}/glfw/src/win32_joystick.c | 0 {include => deps}/glfw/src/win32_joystick.h | 0 {include => deps}/glfw/src/win32_monitor.c | 0 {include => deps}/glfw/src/win32_platform.h | 0 {include => deps}/glfw/src/win32_thread.c | 0 {include => deps}/glfw/src/win32_time.c | 0 {include => deps}/glfw/src/win32_window.c | 0 {include => deps}/glfw/src/window.c | 0 {include => deps}/glfw/src/wl_init.c | 0 {include => deps}/glfw/src/wl_monitor.c | 0 {include => deps}/glfw/src/wl_platform.h | 0 {include => deps}/glfw/src/wl_window.c | 0 {include => deps}/glfw/src/x11_init.c | 0 {include => deps}/glfw/src/x11_monitor.c | 0 {include => deps}/glfw/src/x11_platform.h | 0 {include => deps}/glfw/src/x11_window.c | 0 {include => deps}/glfw/src/xkb_unicode.c | 0 {include => deps}/glfw/src/xkb_unicode.h | 0 {include => deps}/glm/CMakeLists.txt | 0 {include => deps}/glm/common.hpp | 0 {include => deps}/glm/copying.txt | 0 {include => deps}/glm/detail/_features.hpp | 0 {include => deps}/glm/detail/_fixes.hpp | 0 {include => deps}/glm/detail/_noise.hpp | 0 {include => deps}/glm/detail/_swizzle.hpp | 0 .../glm/detail/_swizzle_func.hpp | 0 {include => deps}/glm/detail/_vectorize.hpp | 0 .../glm/detail/compute_common.hpp | 0 .../glm/detail/compute_vector_relational.hpp | 0 {include => deps}/glm/detail/func_common.inl | 0 .../glm/detail/func_common_simd.inl | 0 .../glm/detail/func_exponential.inl | 0 .../glm/detail/func_exponential_simd.inl | 0 .../glm/detail/func_geometric.inl | 0 .../glm/detail/func_geometric_simd.inl | 0 {include => deps}/glm/detail/func_integer.inl | 0 .../glm/detail/func_integer_simd.inl | 0 {include => deps}/glm/detail/func_matrix.inl | 0 .../glm/detail/func_matrix_simd.inl | 0 {include => deps}/glm/detail/func_packing.inl | 0 .../glm/detail/func_packing_simd.inl | 0 .../glm/detail/func_trigonometric.inl | 0 .../glm/detail/func_trigonometric_simd.inl | 0 .../glm/detail/func_vector_relational.inl | 0 .../detail/func_vector_relational_simd.inl | 0 {include => deps}/glm/detail/glm.cpp | 0 {include => deps}/glm/detail/qualifier.hpp | 0 {include => deps}/glm/detail/setup.hpp | 0 {include => deps}/glm/detail/type_float.hpp | 0 {include => deps}/glm/detail/type_half.hpp | 0 {include => deps}/glm/detail/type_half.inl | 0 {include => deps}/glm/detail/type_mat2x2.hpp | 0 {include => deps}/glm/detail/type_mat2x2.inl | 0 {include => deps}/glm/detail/type_mat2x3.hpp | 0 {include => deps}/glm/detail/type_mat2x3.inl | 0 {include => deps}/glm/detail/type_mat2x4.hpp | 0 {include => deps}/glm/detail/type_mat2x4.inl | 0 {include => deps}/glm/detail/type_mat3x2.hpp | 0 {include => deps}/glm/detail/type_mat3x2.inl | 0 {include => deps}/glm/detail/type_mat3x3.hpp | 0 {include => deps}/glm/detail/type_mat3x3.inl | 0 {include => deps}/glm/detail/type_mat3x4.hpp | 0 {include => deps}/glm/detail/type_mat3x4.inl | 0 {include => deps}/glm/detail/type_mat4x2.hpp | 0 {include => deps}/glm/detail/type_mat4x2.inl | 0 {include => deps}/glm/detail/type_mat4x3.hpp | 0 {include => deps}/glm/detail/type_mat4x3.inl | 0 {include => deps}/glm/detail/type_mat4x4.hpp | 0 {include => deps}/glm/detail/type_mat4x4.inl | 0 .../glm/detail/type_mat4x4_simd.inl | 0 {include => deps}/glm/detail/type_quat.hpp | 0 {include => deps}/glm/detail/type_quat.inl | 0 .../glm/detail/type_quat_simd.inl | 0 {include => deps}/glm/detail/type_vec1.hpp | 0 {include => deps}/glm/detail/type_vec1.inl | 0 {include => deps}/glm/detail/type_vec2.hpp | 0 {include => deps}/glm/detail/type_vec2.inl | 0 {include => deps}/glm/detail/type_vec3.hpp | 0 {include => deps}/glm/detail/type_vec3.inl | 0 {include => deps}/glm/detail/type_vec4.hpp | 0 {include => deps}/glm/detail/type_vec4.inl | 0 .../glm/detail/type_vec4_simd.inl | 0 {include => deps}/glm/exponential.hpp | 0 {include => deps}/glm/ext.hpp | 0 .../glm/ext/matrix_clip_space.hpp | 0 .../glm/ext/matrix_clip_space.inl | 0 {include => deps}/glm/ext/matrix_common.hpp | 0 {include => deps}/glm/ext/matrix_common.inl | 0 .../glm/ext/matrix_double2x2.hpp | 0 .../glm/ext/matrix_double2x2_precision.hpp | 0 .../glm/ext/matrix_double2x3.hpp | 0 .../glm/ext/matrix_double2x3_precision.hpp | 0 .../glm/ext/matrix_double2x4.hpp | 0 .../glm/ext/matrix_double2x4_precision.hpp | 0 .../glm/ext/matrix_double3x2.hpp | 0 .../glm/ext/matrix_double3x2_precision.hpp | 0 .../glm/ext/matrix_double3x3.hpp | 0 .../glm/ext/matrix_double3x3_precision.hpp | 0 .../glm/ext/matrix_double3x4.hpp | 0 .../glm/ext/matrix_double3x4_precision.hpp | 0 .../glm/ext/matrix_double4x2.hpp | 0 .../glm/ext/matrix_double4x2_precision.hpp | 0 .../glm/ext/matrix_double4x3.hpp | 0 .../glm/ext/matrix_double4x3_precision.hpp | 0 .../glm/ext/matrix_double4x4.hpp | 0 .../glm/ext/matrix_double4x4_precision.hpp | 0 {include => deps}/glm/ext/matrix_float2x2.hpp | 0 .../glm/ext/matrix_float2x2_precision.hpp | 0 {include => deps}/glm/ext/matrix_float2x3.hpp | 0 .../glm/ext/matrix_float2x3_precision.hpp | 0 {include => deps}/glm/ext/matrix_float2x4.hpp | 0 .../glm/ext/matrix_float2x4_precision.hpp | 0 {include => deps}/glm/ext/matrix_float3x2.hpp | 0 .../glm/ext/matrix_float3x2_precision.hpp | 0 {include => deps}/glm/ext/matrix_float3x3.hpp | 0 .../glm/ext/matrix_float3x3_precision.hpp | 0 {include => deps}/glm/ext/matrix_float3x4.hpp | 0 .../glm/ext/matrix_float3x4_precision.hpp | 0 {include => deps}/glm/ext/matrix_float4x2.hpp | 0 .../glm/ext/matrix_float4x2_precision.hpp | 0 {include => deps}/glm/ext/matrix_float4x3.hpp | 0 .../glm/ext/matrix_float4x3_precision.hpp | 0 {include => deps}/glm/ext/matrix_float4x4.hpp | 0 .../glm/ext/matrix_float4x4_precision.hpp | 0 {include => deps}/glm/ext/matrix_int2x2.hpp | 0 .../glm/ext/matrix_int2x2_sized.hpp | 0 {include => deps}/glm/ext/matrix_int2x3.hpp | 0 .../glm/ext/matrix_int2x3_sized.hpp | 0 {include => deps}/glm/ext/matrix_int2x4.hpp | 0 .../glm/ext/matrix_int2x4_sized.hpp | 0 {include => deps}/glm/ext/matrix_int3x2.hpp | 0 .../glm/ext/matrix_int3x2_sized.hpp | 0 {include => deps}/glm/ext/matrix_int3x3.hpp | 0 .../glm/ext/matrix_int3x3_sized.hpp | 0 {include => deps}/glm/ext/matrix_int3x4.hpp | 0 .../glm/ext/matrix_int3x4_sized.hpp | 0 {include => deps}/glm/ext/matrix_int4x2.hpp | 0 .../glm/ext/matrix_int4x2_sized.hpp | 0 {include => deps}/glm/ext/matrix_int4x3.hpp | 0 .../glm/ext/matrix_int4x3_sized.hpp | 0 {include => deps}/glm/ext/matrix_int4x4.hpp | 0 .../glm/ext/matrix_int4x4_sized.hpp | 0 .../glm/ext/matrix_projection.hpp | 0 .../glm/ext/matrix_projection.inl | 0 .../glm/ext/matrix_relational.hpp | 0 .../glm/ext/matrix_relational.inl | 0 .../glm/ext/matrix_transform.hpp | 0 .../glm/ext/matrix_transform.inl | 0 {include => deps}/glm/ext/matrix_uint2x2.hpp | 0 .../glm/ext/matrix_uint2x2_sized.hpp | 0 {include => deps}/glm/ext/matrix_uint2x3.hpp | 0 .../glm/ext/matrix_uint2x3_sized.hpp | 0 {include => deps}/glm/ext/matrix_uint2x4.hpp | 0 .../glm/ext/matrix_uint2x4_sized.hpp | 0 {include => deps}/glm/ext/matrix_uint3x2.hpp | 0 .../glm/ext/matrix_uint3x2_sized.hpp | 0 {include => deps}/glm/ext/matrix_uint3x3.hpp | 0 .../glm/ext/matrix_uint3x3_sized.hpp | 0 {include => deps}/glm/ext/matrix_uint3x4.hpp | 0 .../glm/ext/matrix_uint3x4_sized.hpp | 0 {include => deps}/glm/ext/matrix_uint4x2.hpp | 0 .../glm/ext/matrix_uint4x2_sized.hpp | 0 {include => deps}/glm/ext/matrix_uint4x3.hpp | 0 .../glm/ext/matrix_uint4x3_sized.hpp | 0 {include => deps}/glm/ext/matrix_uint4x4.hpp | 0 .../glm/ext/matrix_uint4x4_sized.hpp | 0 .../glm/ext/quaternion_common.hpp | 0 .../glm/ext/quaternion_common.inl | 0 .../glm/ext/quaternion_common_simd.inl | 0 .../glm/ext/quaternion_double.hpp | 0 .../glm/ext/quaternion_double_precision.hpp | 0 .../glm/ext/quaternion_exponential.hpp | 0 .../glm/ext/quaternion_exponential.inl | 0 .../glm/ext/quaternion_float.hpp | 0 .../glm/ext/quaternion_float_precision.hpp | 0 .../glm/ext/quaternion_geometric.hpp | 0 .../glm/ext/quaternion_geometric.inl | 0 .../glm/ext/quaternion_relational.hpp | 0 .../glm/ext/quaternion_relational.inl | 0 .../glm/ext/quaternion_transform.hpp | 0 .../glm/ext/quaternion_transform.inl | 0 .../glm/ext/quaternion_trigonometric.hpp | 0 .../glm/ext/quaternion_trigonometric.inl | 0 {include => deps}/glm/ext/scalar_common.hpp | 0 {include => deps}/glm/ext/scalar_common.inl | 0 .../glm/ext/scalar_constants.hpp | 0 .../glm/ext/scalar_constants.inl | 0 .../glm/ext/scalar_int_sized.hpp | 0 {include => deps}/glm/ext/scalar_integer.hpp | 0 {include => deps}/glm/ext/scalar_integer.inl | 0 {include => deps}/glm/ext/scalar_packing.hpp | 0 {include => deps}/glm/ext/scalar_packing.inl | 0 .../glm/ext/scalar_relational.hpp | 0 .../glm/ext/scalar_relational.inl | 0 .../glm/ext/scalar_uint_sized.hpp | 0 {include => deps}/glm/ext/scalar_ulp.hpp | 0 {include => deps}/glm/ext/scalar_ulp.inl | 0 {include => deps}/glm/ext/vector_bool1.hpp | 0 .../glm/ext/vector_bool1_precision.hpp | 0 {include => deps}/glm/ext/vector_bool2.hpp | 0 .../glm/ext/vector_bool2_precision.hpp | 0 {include => deps}/glm/ext/vector_bool3.hpp | 0 .../glm/ext/vector_bool3_precision.hpp | 0 {include => deps}/glm/ext/vector_bool4.hpp | 0 .../glm/ext/vector_bool4_precision.hpp | 0 {include => deps}/glm/ext/vector_common.hpp | 0 {include => deps}/glm/ext/vector_common.inl | 0 {include => deps}/glm/ext/vector_double1.hpp | 0 .../glm/ext/vector_double1_precision.hpp | 0 {include => deps}/glm/ext/vector_double2.hpp | 0 .../glm/ext/vector_double2_precision.hpp | 0 {include => deps}/glm/ext/vector_double3.hpp | 0 .../glm/ext/vector_double3_precision.hpp | 0 {include => deps}/glm/ext/vector_double4.hpp | 0 .../glm/ext/vector_double4_precision.hpp | 0 {include => deps}/glm/ext/vector_float1.hpp | 0 .../glm/ext/vector_float1_precision.hpp | 0 {include => deps}/glm/ext/vector_float2.hpp | 0 .../glm/ext/vector_float2_precision.hpp | 0 {include => deps}/glm/ext/vector_float3.hpp | 0 .../glm/ext/vector_float3_precision.hpp | 0 {include => deps}/glm/ext/vector_float4.hpp | 0 .../glm/ext/vector_float4_precision.hpp | 0 {include => deps}/glm/ext/vector_int1.hpp | 0 .../glm/ext/vector_int1_sized.hpp | 0 {include => deps}/glm/ext/vector_int2.hpp | 0 .../glm/ext/vector_int2_sized.hpp | 0 {include => deps}/glm/ext/vector_int3.hpp | 0 .../glm/ext/vector_int3_sized.hpp | 0 {include => deps}/glm/ext/vector_int4.hpp | 0 .../glm/ext/vector_int4_sized.hpp | 0 {include => deps}/glm/ext/vector_integer.hpp | 0 {include => deps}/glm/ext/vector_integer.inl | 0 {include => deps}/glm/ext/vector_packing.hpp | 0 {include => deps}/glm/ext/vector_packing.inl | 0 .../glm/ext/vector_relational.hpp | 0 .../glm/ext/vector_relational.inl | 0 {include => deps}/glm/ext/vector_uint1.hpp | 0 .../glm/ext/vector_uint1_sized.hpp | 0 {include => deps}/glm/ext/vector_uint2.hpp | 0 .../glm/ext/vector_uint2_sized.hpp | 0 {include => deps}/glm/ext/vector_uint3.hpp | 0 .../glm/ext/vector_uint3_sized.hpp | 0 {include => deps}/glm/ext/vector_uint4.hpp | 0 .../glm/ext/vector_uint4_sized.hpp | 0 {include => deps}/glm/ext/vector_ulp.hpp | 0 {include => deps}/glm/ext/vector_ulp.inl | 0 {include => deps}/glm/fwd.hpp | 0 {include => deps}/glm/geometric.hpp | 0 {include => deps}/glm/glm.hpp | 0 {include => deps}/glm/gtc/bitfield.hpp | 0 {include => deps}/glm/gtc/bitfield.inl | 0 {include => deps}/glm/gtc/color_space.hpp | 0 {include => deps}/glm/gtc/color_space.inl | 0 {include => deps}/glm/gtc/constants.hpp | 0 {include => deps}/glm/gtc/constants.inl | 0 {include => deps}/glm/gtc/epsilon.hpp | 0 {include => deps}/glm/gtc/epsilon.inl | 0 {include => deps}/glm/gtc/integer.hpp | 0 {include => deps}/glm/gtc/integer.inl | 0 {include => deps}/glm/gtc/matrix_access.hpp | 0 {include => deps}/glm/gtc/matrix_access.inl | 0 {include => deps}/glm/gtc/matrix_integer.hpp | 0 {include => deps}/glm/gtc/matrix_inverse.hpp | 0 {include => deps}/glm/gtc/matrix_inverse.inl | 0 .../glm/gtc/matrix_transform.hpp | 0 .../glm/gtc/matrix_transform.inl | 0 {include => deps}/glm/gtc/noise.hpp | 0 {include => deps}/glm/gtc/noise.inl | 0 {include => deps}/glm/gtc/packing.hpp | 0 {include => deps}/glm/gtc/packing.inl | 0 {include => deps}/glm/gtc/quaternion.hpp | 0 {include => deps}/glm/gtc/quaternion.inl | 0 {include => deps}/glm/gtc/quaternion_simd.inl | 0 {include => deps}/glm/gtc/random.hpp | 0 {include => deps}/glm/gtc/random.inl | 0 {include => deps}/glm/gtc/reciprocal.hpp | 0 {include => deps}/glm/gtc/reciprocal.inl | 0 {include => deps}/glm/gtc/round.hpp | 0 {include => deps}/glm/gtc/round.inl | 0 {include => deps}/glm/gtc/type_aligned.hpp | 0 {include => deps}/glm/gtc/type_precision.hpp | 0 {include => deps}/glm/gtc/type_precision.inl | 0 {include => deps}/glm/gtc/type_ptr.hpp | 0 {include => deps}/glm/gtc/type_ptr.inl | 0 {include => deps}/glm/gtc/ulp.hpp | 0 {include => deps}/glm/gtc/ulp.inl | 0 {include => deps}/glm/gtc/vec1.hpp | 0 .../glm/gtx/associated_min_max.hpp | 0 .../glm/gtx/associated_min_max.inl | 0 {include => deps}/glm/gtx/bit.hpp | 0 {include => deps}/glm/gtx/bit.inl | 0 {include => deps}/glm/gtx/closest_point.hpp | 0 {include => deps}/glm/gtx/closest_point.inl | 0 {include => deps}/glm/gtx/color_encoding.hpp | 0 {include => deps}/glm/gtx/color_encoding.inl | 0 {include => deps}/glm/gtx/color_space.hpp | 0 {include => deps}/glm/gtx/color_space.inl | 0 .../glm/gtx/color_space_YCoCg.hpp | 0 .../glm/gtx/color_space_YCoCg.inl | 0 {include => deps}/glm/gtx/common.hpp | 0 {include => deps}/glm/gtx/common.inl | 0 {include => deps}/glm/gtx/compatibility.hpp | 0 {include => deps}/glm/gtx/compatibility.inl | 0 {include => deps}/glm/gtx/component_wise.hpp | 0 {include => deps}/glm/gtx/component_wise.inl | 0 {include => deps}/glm/gtx/dual_quaternion.hpp | 0 {include => deps}/glm/gtx/dual_quaternion.inl | 0 {include => deps}/glm/gtx/easing.hpp | 0 {include => deps}/glm/gtx/easing.inl | 0 {include => deps}/glm/gtx/euler_angles.hpp | 0 {include => deps}/glm/gtx/euler_angles.inl | 0 {include => deps}/glm/gtx/extend.hpp | 0 {include => deps}/glm/gtx/extend.inl | 0 .../glm/gtx/extended_min_max.hpp | 0 .../glm/gtx/extended_min_max.inl | 0 .../glm/gtx/exterior_product.hpp | 0 .../glm/gtx/exterior_product.inl | 0 .../glm/gtx/fast_exponential.hpp | 0 .../glm/gtx/fast_exponential.inl | 0 .../glm/gtx/fast_square_root.hpp | 0 .../glm/gtx/fast_square_root.inl | 0 .../glm/gtx/fast_trigonometry.hpp | 0 .../glm/gtx/fast_trigonometry.inl | 0 {include => deps}/glm/gtx/float_notmalize.inl | 0 {include => deps}/glm/gtx/functions.hpp | 0 {include => deps}/glm/gtx/functions.inl | 0 {include => deps}/glm/gtx/gradient_paint.hpp | 0 {include => deps}/glm/gtx/gradient_paint.inl | 0 .../glm/gtx/handed_coordinate_space.hpp | 0 .../glm/gtx/handed_coordinate_space.inl | 0 {include => deps}/glm/gtx/hash.hpp | 0 {include => deps}/glm/gtx/hash.inl | 0 {include => deps}/glm/gtx/integer.hpp | 0 {include => deps}/glm/gtx/integer.inl | 0 {include => deps}/glm/gtx/intersect.hpp | 0 {include => deps}/glm/gtx/intersect.inl | 0 {include => deps}/glm/gtx/io.hpp | 0 {include => deps}/glm/gtx/io.inl | 0 {include => deps}/glm/gtx/log_base.hpp | 0 {include => deps}/glm/gtx/log_base.inl | 0 .../glm/gtx/matrix_cross_product.hpp | 0 .../glm/gtx/matrix_cross_product.inl | 0 .../glm/gtx/matrix_decompose.hpp | 0 .../glm/gtx/matrix_decompose.inl | 0 .../glm/gtx/matrix_factorisation.hpp | 0 .../glm/gtx/matrix_factorisation.inl | 0 .../glm/gtx/matrix_interpolation.hpp | 0 .../glm/gtx/matrix_interpolation.inl | 0 .../glm/gtx/matrix_major_storage.hpp | 0 .../glm/gtx/matrix_major_storage.inl | 0 .../glm/gtx/matrix_operation.hpp | 0 .../glm/gtx/matrix_operation.inl | 0 {include => deps}/glm/gtx/matrix_query.hpp | 0 {include => deps}/glm/gtx/matrix_query.inl | 0 .../glm/gtx/matrix_transform_2d.hpp | 0 .../glm/gtx/matrix_transform_2d.inl | 0 {include => deps}/glm/gtx/mixed_product.hpp | 0 {include => deps}/glm/gtx/mixed_product.inl | 0 {include => deps}/glm/gtx/norm.hpp | 0 {include => deps}/glm/gtx/norm.inl | 0 {include => deps}/glm/gtx/normal.hpp | 0 {include => deps}/glm/gtx/normal.inl | 0 {include => deps}/glm/gtx/normalize_dot.hpp | 0 {include => deps}/glm/gtx/normalize_dot.inl | 0 .../glm/gtx/number_precision.hpp | 0 .../glm/gtx/number_precision.inl | 0 {include => deps}/glm/gtx/optimum_pow.hpp | 0 {include => deps}/glm/gtx/optimum_pow.inl | 0 {include => deps}/glm/gtx/orthonormalize.hpp | 0 {include => deps}/glm/gtx/orthonormalize.inl | 0 {include => deps}/glm/gtx/perpendicular.hpp | 0 {include => deps}/glm/gtx/perpendicular.inl | 0 .../glm/gtx/polar_coordinates.hpp | 0 .../glm/gtx/polar_coordinates.inl | 0 {include => deps}/glm/gtx/projection.hpp | 0 {include => deps}/glm/gtx/projection.inl | 0 {include => deps}/glm/gtx/quaternion.hpp | 0 {include => deps}/glm/gtx/quaternion.inl | 0 {include => deps}/glm/gtx/range.hpp | 0 {include => deps}/glm/gtx/raw_data.hpp | 0 {include => deps}/glm/gtx/raw_data.inl | 0 .../glm/gtx/rotate_normalized_axis.hpp | 0 .../glm/gtx/rotate_normalized_axis.inl | 0 {include => deps}/glm/gtx/rotate_vector.hpp | 0 {include => deps}/glm/gtx/rotate_vector.inl | 0 .../glm/gtx/scalar_multiplication.hpp | 0 .../glm/gtx/scalar_relational.hpp | 0 .../glm/gtx/scalar_relational.inl | 0 {include => deps}/glm/gtx/spline.hpp | 0 {include => deps}/glm/gtx/spline.inl | 0 {include => deps}/glm/gtx/std_based_type.hpp | 0 {include => deps}/glm/gtx/std_based_type.inl | 0 {include => deps}/glm/gtx/string_cast.hpp | 0 {include => deps}/glm/gtx/string_cast.inl | 0 {include => deps}/glm/gtx/texture.hpp | 0 {include => deps}/glm/gtx/texture.inl | 0 {include => deps}/glm/gtx/transform.hpp | 0 {include => deps}/glm/gtx/transform.inl | 0 {include => deps}/glm/gtx/transform2.hpp | 0 {include => deps}/glm/gtx/transform2.inl | 0 {include => deps}/glm/gtx/type_aligned.hpp | 0 {include => deps}/glm/gtx/type_aligned.inl | 0 {include => deps}/glm/gtx/type_trait.hpp | 0 {include => deps}/glm/gtx/type_trait.inl | 0 {include => deps}/glm/gtx/vec_swizzle.hpp | 0 {include => deps}/glm/gtx/vector_angle.hpp | 0 {include => deps}/glm/gtx/vector_angle.inl | 0 {include => deps}/glm/gtx/vector_query.hpp | 0 {include => deps}/glm/gtx/vector_query.inl | 0 {include => deps}/glm/gtx/wrap.hpp | 0 {include => deps}/glm/gtx/wrap.inl | 0 {include => deps}/glm/integer.hpp | 0 {include => deps}/glm/mat2x2.hpp | 0 {include => deps}/glm/mat2x3.hpp | 0 {include => deps}/glm/mat2x4.hpp | 0 {include => deps}/glm/mat3x2.hpp | 0 {include => deps}/glm/mat3x3.hpp | 0 {include => deps}/glm/mat3x4.hpp | 0 {include => deps}/glm/mat4x2.hpp | 0 {include => deps}/glm/mat4x3.hpp | 0 {include => deps}/glm/mat4x4.hpp | 0 {include => deps}/glm/matrix.hpp | 0 {include => deps}/glm/packing.hpp | 0 {include => deps}/glm/simd/common.h | 0 {include => deps}/glm/simd/exponential.h | 0 {include => deps}/glm/simd/geometric.h | 0 {include => deps}/glm/simd/integer.h | 0 {include => deps}/glm/simd/matrix.h | 0 {include => deps}/glm/simd/neon.h | 0 {include => deps}/glm/simd/packing.h | 0 {include => deps}/glm/simd/platform.h | 0 {include => deps}/glm/simd/trigonometric.h | 0 .../glm/simd/vector_relational.h | 0 {include => deps}/glm/trigonometric.hpp | 0 {include => deps}/glm/vec2.hpp | 0 {include => deps}/glm/vec3.hpp | 0 {include => deps}/glm/vec4.hpp | 0 {include => deps}/glm/vector_relational.hpp | 0 {include => deps}/imgui/LICENSE.txt | 0 {include => deps}/imgui/imconfig.h | 0 {include => deps}/imgui/imgui.cpp | 0 {include => deps}/imgui/imgui.h | 0 {include => deps}/imgui/imgui_demo.cpp | 0 {include => deps}/imgui/imgui_draw.cpp | 0 {include => deps}/imgui/imgui_impl_glfw.cpp | 0 {include => deps}/imgui/imgui_impl_glfw.h | 0 .../imgui/imgui_impl_opengl3.cpp | 0 {include => deps}/imgui/imgui_impl_opengl3.h | 0 {include => deps}/imgui/imgui_impl_vulkan.cpp | 0 {include => deps}/imgui/imgui_impl_vulkan.h | 0 {include => deps}/imgui/imgui_internal.h | 0 {include => deps}/imgui/imgui_widgets.cpp | 0 {include => deps}/imgui/imstb_rectpack.h | 0 {include => deps}/imgui/imstb_textedit.h | 0 {include => deps}/imgui/imstb_truetype.h | 0 {include => deps}/libguarded/LICENSE | 0 {include => deps}/libguarded/mutex_guarded.h | 0 .../libguarded/shared_mutex_guarded.h | 0 .../meshoptimizer/CONTRIBUTING.md | 0 {include => deps}/meshoptimizer/LICENSE.md | 0 {include => deps}/meshoptimizer/README.md | 0 {include => deps}/meshoptimizer/allocator.cpp | 0 .../meshoptimizer/clusterizer.cpp | 0 .../meshoptimizer/extern/basisu_format.h | 0 .../meshoptimizer/extern/cgltf.h | 0 .../meshoptimizer/extern/fast_obj.h | 0 .../meshoptimizer/extern/khr_df.h | 0 .../meshoptimizer/indexcodec.cpp | 0 .../meshoptimizer/indexgenerator.cpp | 0 .../meshoptimizer/meshoptimizer.h | 0 .../meshoptimizer/overdrawanalyzer.cpp | 0 .../meshoptimizer/overdrawoptimizer.cpp | 0 .../meshoptimizer/simplifier.cpp | 0 .../meshoptimizer/spatialorder.cpp | 0 .../meshoptimizer/stripifier.cpp | 0 .../meshoptimizer/vcacheanalyzer.cpp | 0 .../meshoptimizer/vcacheoptimizer.cpp | 0 .../meshoptimizer/vertexcodec.cpp | 0 .../meshoptimizer/vertexfilter.cpp | 0 .../meshoptimizer/vfetchanalyzer.cpp | 0 .../meshoptimizer/vfetchoptimizer.cpp | 0 {include => deps}/mini-yaml/LICENSE | 0 {include => deps}/mini-yaml/README.md | 0 {include => deps}/mini-yaml/Yaml.cpp | 0 {include => deps}/mini-yaml/Yaml.hpp | 0 deps/picoquic/CMakeLists.txt | 60 + deps/picoquic/LICENSE | 21 + deps/picoquic/README.md | 150 + deps/picoquic/ReadMe.txt | 29 + deps/picoquic/bbr.c | 1117 + deps/picoquic/bytestream.c | 437 + deps/picoquic/bytestream.h | 91 + deps/picoquic/cc_common.c | 155 + deps/picoquic/cc_common.h | 86 + deps/picoquic/cidset.h | 58 + deps/picoquic/cubic.c | 685 + deps/picoquic/fastcc.c | 327 + deps/picoquic/frames.c | 4092 +++ deps/picoquic/intformat.c | 160 + deps/picoquic/logger.c | 2186 ++ deps/picoquic/logwriter.c | 1147 + deps/picoquic/newreno.c | 298 + deps/picoquic/packet.c | 2222 ++ deps/picoquic/picohash.c | 166 + deps/picoquic/picohash.h | 73 + deps/picoquic/picoquic.h | 950 + deps/picoquic/picoquic.vcxproj | 199 + deps/picoquic/picoquic.vcxproj.filters | 144 + deps/picoquic/picoquic_binlog.h | 111 + deps/picoquic/picoquic_internal.h | 1434 + deps/picoquic/picoquic_logger.h | 43 + deps/picoquic/picoquic_packet_loop.h | 62 + deps/picoquic/picoquic_unified_log.h | 189 + deps/picoquic/picoquic_utils.h | 310 + deps/picoquic/picosocks.c | 1281 + deps/picoquic/picosocks.h | 246 + deps/picoquic/picosplay.c | 329 + deps/picoquic/picosplay.h | 66 + deps/picoquic/picotls/CMakeLists.txt | 83 + deps/picoquic/picotls/README.md | 82 + deps/picoquic/picotls/WindowsPort.md | 34 + deps/picoquic/picotls/deps/cifra/.travis.yml | 29 + deps/picoquic/picotls/deps/cifra/COPYING | 121 + deps/picoquic/picotls/deps/cifra/README.md | 246 + .../picotls/deps/cifra/curve25519-shootout.md | 16 + deps/picoquic/picotls/deps/cifra/doc/Makefile | 3 + deps/picoquic/picotls/deps/cifra/doc/build.py | 274 + deps/picoquic/picotls/deps/cifra/doc/conf.py | 263 + .../picoquic/picotls/deps/cifra/doc/index.rst | 33 + .../picotls/deps/cifra/extra_vecs/.gitignore | 1 + .../picotls/deps/cifra/extra_vecs/Makefile | 13 + .../picotls/deps/cifra/extra_vecs/README.md | 2 + .../deps/cifra/extra_vecs/openssl-hash.c | 60 + .../deps/cifra/extra_vecs/python-hash.py | 38 + .../picotls/deps/cifra/shitlisp/.gitignore | 5 + .../picotls/deps/cifra/shitlisp/Makefile | 17 + .../picotls/deps/cifra/shitlisp/sl-cifra.c | 206 + .../picotls/deps/cifra/shitlisp/test-aes.sl | 91 + .../deps/cifra/shitlisp/test-pbkdf2.sl | 36 + .../deps/cifra/shitlisp/test-sha224.sl | 41 + .../deps/cifra/shitlisp/test-sha256.sl | 62 + .../deps/cifra/shitlisp/test-sha384.sl | 67 + .../deps/cifra/shitlisp/test-sha512.sl | 67 + .../picotls/deps/cifra/src/.gitignore | 15 + deps/picoquic/picotls/deps/cifra/src/Makefile | 54 + deps/picoquic/picotls/deps/cifra/src/aes.c | 419 + deps/picoquic/picotls/deps/cifra/src/aes.h | 152 + .../picotls/deps/cifra/src/arm/.gitignore | 3 + .../picotls/deps/cifra/src/arm/Makefile | 184 + .../picotls/deps/cifra/src/arm/analyse.py | 207 + .../picotls/deps/cifra/src/arm/boot.c | 144 + .../deps/cifra/src/arm/curve25519-results.txt | 22 + .../picotls/deps/cifra/src/arm/ext/cutest.h | 55 + .../deps/cifra/src/arm/linkscript.efm32.ld | 8 + .../cifra/src/arm/linkscript.lm3s6965evb.ld | 7 + .../deps/cifra/src/arm/linkscript.qemucm3.ld | 8 + .../deps/cifra/src/arm/linkscript.std.ld | 172 + .../deps/cifra/src/arm/linkscript.stm32f0.ld | 8 + .../deps/cifra/src/arm/linkscript.stm32f1.ld | 8 + .../deps/cifra/src/arm/linkscript.stm32f3.ld | 8 + .../picotls/deps/cifra/src/arm/main.c | 447 + .../picotls/deps/cifra/src/arm/memcpy.s | 49 + .../picotls/deps/cifra/src/arm/memset.s | 50 + .../picotls/deps/cifra/src/arm/merge.py | 26 + .../deps/cifra/src/arm/openocd.efm32.cfg | 3 + .../deps/cifra/src/arm/openocd.stm32f0.cfg | 3 + .../deps/cifra/src/arm/openocd.stm32f1.cfg | 3 + .../deps/cifra/src/arm/openocd.stm32f3.cfg | 3 + .../picotls/deps/cifra/src/arm/report.py | 276 + .../picotls/deps/cifra/src/arm/semihost.c | 170 + .../picotls/deps/cifra/src/arm/semihost.h | 40 + .../picotls/deps/cifra/src/arm/semihost.s | 15 + .../cifra/src/arm/unacl/cortex_m0_mpy121666.s | 199 + .../src/arm/unacl/cortex_m0_reduce25519.s | 176 + .../picotls/deps/cifra/src/arm/unacl/mul.s | 1109 + .../deps/cifra/src/arm/unacl/scalarmult.c | 761 + .../picotls/deps/cifra/src/arm/unacl/sqr.s | 777 + deps/picoquic/picotls/deps/cifra/src/bitops.h | 310 + .../picotls/deps/cifra/src/blockwise.c | 195 + .../picotls/deps/cifra/src/blockwise.h | 147 + deps/picoquic/picotls/deps/cifra/src/cbcmac.c | 79 + deps/picoquic/picotls/deps/cifra/src/ccm.c | 193 + .../picotls/deps/cifra/src/cf_config.h | 59 + .../picotls/deps/cifra/src/chacha20.c | 161 + .../picotls/deps/cifra/src/chacha20poly1305.c | 148 + .../picotls/deps/cifra/src/chacha20poly1305.h | 73 + deps/picoquic/picotls/deps/cifra/src/chash.c | 28 + deps/picoquic/picotls/deps/cifra/src/chash.h | 137 + deps/picoquic/picotls/deps/cifra/src/cmac.c | 150 + .../picotls/deps/cifra/src/curve25519.c | 29 + .../picotls/deps/cifra/src/curve25519.donna.c | 867 + .../picotls/deps/cifra/src/curve25519.h | 42 + .../deps/cifra/src/curve25519.naclref.c | 273 + .../deps/cifra/src/curve25519.tweetnacl.c | 235 + deps/picoquic/picotls/deps/cifra/src/drbg.c | 434 + deps/picoquic/picotls/deps/cifra/src/drbg.h | 191 + deps/picoquic/picotls/deps/cifra/src/eax.c | 116 + .../picotls/deps/cifra/src/ext/cutest.h | 620 + .../picotls/deps/cifra/src/ext/handy.h | 66 + deps/picoquic/picotls/deps/cifra/src/gcm.c | 249 + deps/picoquic/picotls/deps/cifra/src/gf128.c | 114 + deps/picoquic/picotls/deps/cifra/src/gf128.h | 55 + deps/picoquic/picotls/deps/cifra/src/hmac.c | 106 + deps/picoquic/picotls/deps/cifra/src/hmac.h | 78 + deps/picoquic/picotls/deps/cifra/src/modes.c | 99 + deps/picoquic/picotls/deps/cifra/src/modes.h | 587 + deps/picoquic/picotls/deps/cifra/src/norx.c | 410 + deps/picoquic/picotls/deps/cifra/src/norx.h | 84 + deps/picoquic/picotls/deps/cifra/src/ocb.c | 404 + deps/picoquic/picotls/deps/cifra/src/pbkdf2.c | 84 + deps/picoquic/picotls/deps/cifra/src/pbkdf2.h | 47 + .../picotls/deps/cifra/src/poly1305.c | 221 + .../picotls/deps/cifra/src/poly1305.h | 91 + .../picotls/deps/cifra/src/poly1305.py | 127 + deps/picoquic/picotls/deps/cifra/src/prp.h | 64 + .../picoquic/picotls/deps/cifra/src/salsa20.c | 164 + .../picoquic/picotls/deps/cifra/src/salsa20.h | 140 + deps/picoquic/picotls/deps/cifra/src/sha1.c | 150 + deps/picoquic/picotls/deps/cifra/src/sha1.h | 91 + deps/picoquic/picotls/deps/cifra/src/sha2.h | 235 + deps/picoquic/picotls/deps/cifra/src/sha256.c | 232 + deps/picoquic/picotls/deps/cifra/src/sha3.c | 444 + deps/picoquic/picotls/deps/cifra/src/sha3.h | 180 + deps/picoquic/picotls/deps/cifra/src/sha512.c | 249 + .../picoquic/picotls/deps/cifra/src/tassert.h | 32 + .../picoquic/picotls/deps/cifra/src/testaes.c | 258 + .../deps/cifra/src/testchacha20poly1305.c | 91 + .../picotls/deps/cifra/src/testcurve25519.c | 56 + .../picotls/deps/cifra/src/testdrbg.c | 206 + .../picotls/deps/cifra/src/testmodes.c | 890 + .../picotls/deps/cifra/src/testnorx.c | 118 + .../deps/cifra/src/testnorx.katdata.inc | 4961 +++ .../picotls/deps/cifra/src/testpoly1305.c | 102 + .../picotls/deps/cifra/src/testsalsa20.c | 210 + .../picoquic/picotls/deps/cifra/src/testsha.h | 213 + .../picotls/deps/cifra/src/testsha1.c | 55 + .../picotls/deps/cifra/src/testsha2.c | 224 + .../picotls/deps/cifra/src/testsha3.c | 104 + .../picotls/deps/cifra/src/testutil.h | 61 + .../picotls/deps/micro-ecc/.gitignore | 8 + .../picotls/deps/micro-ecc/LICENSE.txt | 21 + .../picoquic/picotls/deps/micro-ecc/README.md | 41 + .../picotls/deps/micro-ecc/asm_arm.inc | 820 + .../deps/micro-ecc/asm_arm_mult_square.inc | 2311 ++ .../micro-ecc/asm_arm_mult_square_umaal.inc | 1202 + .../picotls/deps/micro-ecc/asm_avr.inc | 1089 + .../deps/micro-ecc/asm_avr_mult_square.inc | 26311 ++++++++++++++++ .../picotls/deps/micro-ecc/curve-specific.inc | 1248 + .../picotls/deps/micro-ecc/emk_project.py | 127 + .../picotls/deps/micro-ecc/emk_rules.py | 3 + .../deps/micro-ecc/platform-specific.inc | 67 + .../deps/micro-ecc/scripts/mult_arm.py | 188 + .../deps/micro-ecc/scripts/mult_avr.py | 203 + .../deps/micro-ecc/scripts/mult_avr_extra.py | 143 + .../deps/micro-ecc/scripts/square_arm.py | 242 + .../deps/micro-ecc/scripts/square_avr.py | 327 + .../deps/micro-ecc/test/ecc_test/ecc_test.ino | 85 + .../picotls/deps/micro-ecc/test/emk_rules.py | 4 + .../deps/micro-ecc/test/test_compress.c | 79 + .../deps/micro-ecc/test/test_compute.c | 81 + .../picotls/deps/micro-ecc/test/test_ecdh.c | 90 + .../picotls/deps/micro-ecc/test/test_ecdsa.c | 59 + .../test/test_ecdsa_deterministic.c.example | 93 + deps/picoquic/picotls/deps/micro-ecc/types.h | 108 + deps/picoquic/picotls/deps/micro-ecc/uECC.c | 1634 + deps/picoquic/picotls/deps/micro-ecc/uECC.h | 362 + .../picotls/deps/micro-ecc/uECC_vli.h | 172 + deps/picoquic/picotls/include/picotls.h | 1525 + deps/picoquic/picotls/include/picotls/asn1.h | 64 + .../include/picotls/certificate_compression.h | 59 + deps/picoquic/picotls/include/picotls/ffx.h | 138 + .../picoquic/picotls/include/picotls/fusion.h | 99 + .../picotls/include/picotls/minicrypto.h | 70 + .../picotls/include/picotls/openssl.h | 125 + .../picotls/include/picotls/pembase64.h | 44 + .../picotls/include/picotls/ptlsbcrypt.h | 53 + deps/picoquic/picotls/lib/asn1.c | 297 + .../picotls/lib/certificate_compression.c | 149 + deps/picoquic/picotls/lib/cifra.c | 27 + deps/picoquic/picotls/lib/cifra/aes-common.h | 178 + deps/picoquic/picotls/lib/cifra/aes128.c | 61 + deps/picoquic/picotls/lib/cifra/aes256.c | 61 + deps/picoquic/picotls/lib/cifra/chacha20.c | 219 + deps/picoquic/picotls/lib/cifra/random.c | 122 + deps/picoquic/picotls/lib/cifra/x25519.c | 119 + deps/picoquic/picotls/lib/ffx.c | 207 + deps/picoquic/picotls/lib/fusion.c | 1050 + deps/picoquic/picotls/lib/minicrypto-pem.c | 350 + deps/picoquic/picotls/lib/openssl.c | 1491 + deps/picoquic/picotls/lib/pembase64.c | 370 + deps/picoquic/picotls/lib/picotls.c | 5517 ++++ deps/picoquic/picotls/lib/ptlsbcrypt.c | 803 + deps/picoquic/picotls/lib/uecc.c | 191 + .../picotlsvs/bcrypt-test/bcrypt-test.c | 632 + .../picotlsvs/bcrypt-test/bcrypt-test.vcxproj | 171 + .../bcrypt-test/bcrypt-test.vcxproj.filters | 22 + .../bcrypt-test/bcrypt-test.vcxproj.user | 4 + .../picotls/picotlsvs/cifra/ReadMe.txt | 29 + .../picotls/picotlsvs/cifra/cifra.vcxproj | 188 + .../picotlsvs/cifra/cifra.vcxproj.filters | 150 + .../picotlsvs/fusiontest/fusiontest.vcxproj | 166 + .../fusiontest/fusiontest.vcxproj.filters | 25 + .../fusiontest/fusiontest.vcxproj.user | 4 + .../picotls/picotlsvs/microecc/ReadMe.txt | 29 + .../picotlsvs/microecc/microecc.vcxproj | 140 + .../microecc/microecc.vcxproj.filters | 25 + deps/picoquic/picotls/picotlsvs/openssl.cnf | 35 + .../picotls-bcrypt/picotls-bcrypt.vcxproj | 142 + .../picotls-bcrypt.vcxproj.filters | 27 + .../picotls-bcrypt.vcxproj.user | 4 + .../picotls-core/picotls-core.vcxproj | 164 + .../picotls-core/picotls-core.vcxproj.filters | 33 + .../picotls-core/picotls-core.vcxproj.user | 4 + .../picotlsvs/picotls-core/targetver.h | 8 + .../picotls/picotlsvs/picotls-esni/getopt.c | 103 + .../picotls/picotlsvs/picotls-esni/getopt.h | 19 + .../picotls-esni/picotls-esni.vcxproj | 175 + .../picotls-esni/picotls-esni.vcxproj.filters | 30 + .../picotls-esni/picotls-esni.vcxproj.user | 8 + .../picotls-fusion/picotls-fusion.vcxproj | 150 + .../picotls-fusion.vcxproj.filters | 22 + .../picotls-fusion.vcxproj.user | 4 + .../picotls-minicrypto-deps.vcxproj | 176 + .../picotls-minicrypto-deps.vcxproj.filters | 69 + .../picotls-minicrypto-deps.vcxproj.user | 4 + .../picotls-minicrypto-deps/targetver.h | 8 + .../picotls-minicrypto.vcxproj | 171 + .../picotls-minicrypto.vcxproj.filters | 54 + .../picotls-minicrypto.vcxproj.user | 4 + .../picotlsvs/picotls-minicrypto/targetver.h | 8 + .../picotls-openssl/picotls-openssl.vcxproj | 162 + .../picotls-openssl.vcxproj.filters | 27 + .../picotls-openssl.vcxproj.user | 4 + .../picotlsvs/picotls-openssl/targetver.h | 8 + .../picotls/picotlsvs/picotls/ReadMe.txt | 37 + .../picotls/picotlsvs/picotls/picotls.vcxproj | 177 + .../picotlsvs/picotls/picotls.vcxproj.filters | 129 + .../picotlsvs/picotls/picotls.vcxproj.user | 4 + .../picotls/picotlsvs/picotls/targetver.h | 8 + .../picotls/picotlsvs/picotls/wincompat.h | 37 + .../picotls/picotlsvs/picotls/wintimeofday.c | 73 + deps/picoquic/picotls/picotlsvs/picotlsvs.sln | 174 + .../picotls/picotlsvs/picotlsvs/ReadMe.txt | 40 + .../picotls/picotlsvs/picotlsvs/cert.pem | 19 + .../picotls/picotlsvs/picotlsvs/ec_cert.pem | 62 + .../picotls/picotlsvs/picotlsvs/ec_key.pem | 5 + .../picotlsvs/picotlsvs/key-test-1.pem | 5 + .../picotlsvs/picotlsvs/key-test-2.pem | 9 + .../picotlsvs/picotlsvs/key-test-3.pem | 5 + .../picotlsvs/picotlsvs/key-test-4.pem | 5 + .../picotls/picotlsvs/picotlsvs/key.pem | 28 + .../picotls/picotlsvs/picotlsvs/myec1.pem | 5 + .../picotls/picotlsvs/picotlsvs/openssl.cnf | 35 + .../picotls/picotlsvs/picotlsvs/picotlsvs.c | 699 + .../picotlsvs/picotlsvs/picotlsvs.vcxproj | 161 + .../picotlsvs/picotlsvs.vcxproj.filters | 30 + .../picotlsvs/picotlsvs.vcxproj.user | 7 + .../picotls/picotlsvs/picotlsvs/targetver.h | 8 + .../pioctls-core/pioctls-core.vcxproj | 164 + .../pioctls-core/pioctls-core.vcxproj.filters | 30 + .../pioctls-core/pioctls-core.vcxproj.user | 4 + .../picotls/picotlsvs/pioctls-core/stdafx.cpp | 1 + .../picotls/picotlsvs/pioctls-core/stdafx.h | 14 + .../picotlsvs/pioctls-core/targetver.h | 8 + .../picotlsvs/ptlsbench/ptlsbench.vcxproj | 171 + .../ptlsbench/ptlsbench.vcxproj.filters | 22 + .../ptlsbench/ptlsbench.vcxproj.user | 11 + .../picotlsvs/testopenssl/testopenssl.vcxproj | 168 + .../testopenssl/testopenssl.vcxproj.filters | 41 + deps/picoquic/picotls/src/esni.c | 240 + deps/picoquic/quicctx.c | 3805 +++ deps/picoquic/sacks.c | 233 + deps/picoquic/sender.c | 3926 +++ deps/picoquic/sim_link.c | 299 + deps/picoquic/sockloop.c | 382 + deps/picoquic/spinbit.c | 85 + deps/picoquic/ticket_store.c | 405 + deps/picoquic/tls_api.c | 3312 ++ deps/picoquic/tls_api.h | 199 + deps/picoquic/token_store.c | 354 + deps/picoquic/transport.c | 825 + deps/picoquic/unified_log.c | 237 + deps/picoquic/util.c | 1132 + deps/picoquic/wincompat.h | 53 + deps/picoquic/winsockloop.c | 755 + {include => deps}/robin_hood/robin_hood.h | 0 {include => deps}/toml++/toml.h | 0 {include => deps}/toml++/toml_array.h | 0 {include => deps}/toml++/toml_array.hpp | 0 {include => deps}/toml++/toml_common.h | 0 {include => deps}/toml++/toml_date_time.h | 0 .../toml++/toml_default_formatter.h | 0 .../toml++/toml_default_formatter.hpp | 0 {include => deps}/toml++/toml_formatter.h | 0 .../toml++/toml_instantiations.hpp | 0 .../toml++/toml_json_formatter.h | 0 .../toml++/toml_json_formatter.hpp | 0 {include => deps}/toml++/toml_node.h | 0 {include => deps}/toml++/toml_node.hpp | 0 {include => deps}/toml++/toml_node_view.h | 0 {include => deps}/toml++/toml_parse_error.h | 0 {include => deps}/toml++/toml_parser.h | 0 {include => deps}/toml++/toml_parser.hpp | 0 {include => deps}/toml++/toml_preprocessor.h | 0 .../toml++/toml_print_to_stream.h | 0 {include => deps}/toml++/toml_table.h | 0 {include => deps}/toml++/toml_table.hpp | 0 {include => deps}/toml++/toml_utf8.h | 0 .../toml++/toml_utf8_generated.h | 0 {include => deps}/toml++/toml_utf8_streams.h | 0 {include => deps}/toml++/toml_value.h | 0 {include => deps}/toml++/toml_version.h | 0 {include => deps}/tracy/AUTHORS | 0 {include => deps}/tracy/LICENSE | 0 {include => deps}/tracy/README.md | 0 {include => deps}/tracy/Tracy.hpp | 0 {include => deps}/tracy/TracyC.h | 0 {include => deps}/tracy/TracyClient.cpp | 0 {include => deps}/tracy/TracyOpenGL.hpp | 0 {include => deps}/tracy/TracyVulkan.hpp | 0 .../tracy/client/TracyArmCpuTable.hpp | 0 .../tracy/client/TracyCallstack.cpp | 0 .../tracy/client/TracyCallstack.h | 0 .../tracy/client/TracyCallstack.hpp | 0 {include => deps}/tracy/client/TracyDxt1.cpp | 0 {include => deps}/tracy/client/TracyDxt1.hpp | 0 .../tracy/client/TracyFastVector.hpp | 0 {include => deps}/tracy/client/TracyLock.hpp | 0 .../tracy/client/TracyProfiler.cpp | 0 .../tracy/client/TracyProfiler.hpp | 0 .../tracy/client/TracyScoped.hpp | 0 .../tracy/client/TracySysTime.cpp | 0 .../tracy/client/TracySysTime.hpp | 0 .../tracy/client/TracySysTrace.cpp | 0 .../tracy/client/TracySysTrace.hpp | 0 .../tracy/client/TracySysTracePayload.hpp | 0 .../tracy/client/TracyThread.hpp | 0 .../tracy/client/tracy_concurrentqueue.h | 0 .../tracy/client/tracy_rpmalloc.cpp | 0 .../tracy/client/tracy_rpmalloc.hpp | 0 {include => deps}/tracy/common/TracyAlign.hpp | 0 {include => deps}/tracy/common/TracyAlloc.hpp | 0 {include => deps}/tracy/common/TracyApi.h | 0 {include => deps}/tracy/common/TracyColor.hpp | 0 .../tracy/common/TracyForceInline.hpp | 0 {include => deps}/tracy/common/TracyMutex.hpp | 0 .../tracy/common/TracyProtocol.hpp | 0 {include => deps}/tracy/common/TracyQueue.hpp | 0 .../tracy/common/TracySocket.cpp | 0 .../tracy/common/TracySocket.hpp | 0 .../tracy/common/TracySystem.cpp | 0 .../tracy/common/TracySystem.hpp | 0 {include => deps}/tracy/common/tracy_lz4.cpp | 0 {include => deps}/tracy/common/tracy_lz4.hpp | 0 .../tracy/common/tracy_lz4hc.cpp | 0 .../tracy/common/tracy_lz4hc.hpp | 0 {include => deps}/tracy/libbacktrace/LICENSE | 0 .../tracy/libbacktrace/alloc.cpp | 0 .../tracy/libbacktrace/backtrace.hpp | 0 {include => deps}/tracy/libbacktrace/config.h | 0 .../tracy/libbacktrace/dwarf.cpp | 0 {include => deps}/tracy/libbacktrace/elf.cpp | 0 .../tracy/libbacktrace/fileline.cpp | 0 .../tracy/libbacktrace/filenames.hpp | 0 .../tracy/libbacktrace/internal.hpp | 0 .../tracy/libbacktrace/macho.cpp | 0 .../tracy/libbacktrace/mmapio.cpp | 0 .../tracy/libbacktrace/posix.cpp | 0 {include => deps}/tracy/libbacktrace/sort.cpp | 0 .../tracy/libbacktrace/state.cpp | 0 {include => deps}/volk/volk.c | 0 {include => deps}/volk/volk.h | 0 {include => deps}/zstd/CMakeLists.txt | 0 {include => deps}/zstd/common/bitstream.h | 0 {include => deps}/zstd/common/compiler.h | 0 {include => deps}/zstd/common/cpu.h | 0 {include => deps}/zstd/common/debug.c | 0 {include => deps}/zstd/common/debug.h | 0 .../zstd/common/entropy_common.c | 0 {include => deps}/zstd/common/error_private.c | 0 {include => deps}/zstd/common/error_private.h | 0 {include => deps}/zstd/common/fse.h | 0 .../zstd/common/fse_decompress.c | 0 {include => deps}/zstd/common/huf.h | 0 {include => deps}/zstd/common/mem.h | 0 {include => deps}/zstd/common/pool.c | 0 {include => deps}/zstd/common/pool.h | 0 {include => deps}/zstd/common/threading.c | 0 {include => deps}/zstd/common/threading.h | 0 {include => deps}/zstd/common/xxhash.c | 0 {include => deps}/zstd/common/xxhash.h | 0 {include => deps}/zstd/common/zstd_common.c | 0 {include => deps}/zstd/common/zstd_errors.h | 0 {include => deps}/zstd/common/zstd_internal.h | 0 .../zstd/compress/fse_compress.c | 0 {include => deps}/zstd/compress/hist.c | 0 {include => deps}/zstd/compress/hist.h | 0 .../zstd/compress/huf_compress.c | 0 .../zstd/compress/zstd_compress.c | 0 .../zstd/compress/zstd_compress_internal.h | 0 .../zstd/compress/zstd_compress_literals.c | 0 .../zstd/compress/zstd_compress_literals.h | 0 .../zstd/compress/zstd_compress_sequences.c | 0 .../zstd/compress/zstd_compress_sequences.h | 0 .../zstd/compress/zstd_compress_superblock.c | 0 .../zstd/compress/zstd_compress_superblock.h | 0 {include => deps}/zstd/compress/zstd_cwksp.h | 0 .../zstd/compress/zstd_double_fast.c | 0 .../zstd/compress/zstd_double_fast.h | 0 {include => deps}/zstd/compress/zstd_fast.c | 0 {include => deps}/zstd/compress/zstd_fast.h | 0 {include => deps}/zstd/compress/zstd_lazy.c | 0 {include => deps}/zstd/compress/zstd_lazy.h | 0 {include => deps}/zstd/compress/zstd_ldm.c | 0 {include => deps}/zstd/compress/zstd_ldm.h | 0 {include => deps}/zstd/compress/zstd_opt.c | 0 {include => deps}/zstd/compress/zstd_opt.h | 0 .../zstd/compress/zstdmt_compress.c | 0 .../zstd/compress/zstdmt_compress.h | 0 .../zstd/decompress/huf_decompress.c | 0 .../zstd/decompress/zstd_ddict.c | 0 .../zstd/decompress/zstd_ddict.h | 0 .../zstd/decompress/zstd_decompress.c | 0 .../zstd/decompress/zstd_decompress_block.c | 0 .../zstd/decompress/zstd_decompress_block.h | 0 .../decompress/zstd_decompress_internal.h | 0 {include => deps}/zstd/dictBuilder/cover.c | 0 {include => deps}/zstd/dictBuilder/cover.h | 0 .../zstd/dictBuilder/divsufsort.c | 0 .../zstd/dictBuilder/divsufsort.h | 0 .../zstd/dictBuilder/fastcover.c | 0 {include => deps}/zstd/dictBuilder/zdict.c | 0 {include => deps}/zstd/dictBuilder/zdict.h | 0 {include => deps}/zstd/zstd.h | 0 include/enet/CMakeLists.txt | 92 - include/enet/ChangeLog | 196 - include/enet/LICENSE | 7 - include/enet/Makefile.am | 22 - include/enet/README | 15 - include/enet/callbacks.c | 53 - include/enet/compress.c | 654 - include/enet/configure.ac | 28 - include/enet/host.c | 491 - include/enet/include/enet/callbacks.h | 27 - include/enet/include/enet/enet.h | 612 - include/enet/include/enet/list.h | 43 - include/enet/include/enet/protocol.h | 198 - include/enet/include/enet/time.h | 18 - include/enet/include/enet/types.h | 13 - include/enet/include/enet/unix.h | 48 - include/enet/include/enet/utility.h | 13 - include/enet/include/enet/win32.h | 57 - include/enet/libenet.pc.in | 10 - include/enet/list.c | 75 - include/enet/packet.c | 165 - include/enet/peer.c | 1003 - include/enet/premake4.lua | 59 - include/enet/protocol.c | 1877 -- include/enet/unix.c | 615 - include/enet/win32.c | 442 - resource/content/cert.pem | 19 + resource/content/key.pem | 28 + src/client.cpp | 4 - src/client/Client.cpp | 2 +- src/client/config.hpp | 4 +- src/client/net/Client.cpp | 188 + src/client/net/Client.hpp | 36 + src/client/world/DistantUniverse.cpp | 378 +- src/client/world/DistantUniverse.hpp | 11 +- src/client/world/LocalUniverse.hpp | 2 +- src/client/world/Universe.hpp | 6 +- src/core/data/mem.hpp | 90 + src/core/net/Client.hpp | 134 - src/core/net/Context.cpp | 397 + src/core/net/Context.hpp | 161 + src/core/net/PacketView.hpp | 94 - src/core/net/Server.hpp | 163 - src/core/net/data.hpp | 113 +- src/core/net/io.hpp | 107 + src/core/world/raycast.hpp | 1 + src/main.cpp | 4 - src/server.cpp | 4 - src/server/Server.cpp | 1 + src/server/config.hpp | 14 +- src/server/net/Server.cpp | 190 + src/server/net/Server.hpp | 72 + src/server/world/Universe.cpp | 349 +- src/server/world/Universe.hpp | 17 +- 1087 files changed, 117375 insertions(+), 7715 deletions(-) rename {include => deps}/FastNoiseSIMD/ARM/cpu-features.c (100%) rename {include => deps}/FastNoiseSIMD/ARM/cpu-features.h (100%) rename {include => deps}/FastNoiseSIMD/FastNoiseSIMD.cpp (100%) rename {include => deps}/FastNoiseSIMD/FastNoiseSIMD.h (100%) rename {include => deps}/FastNoiseSIMD/FastNoiseSIMD_avx2.cpp (100%) rename {include => deps}/FastNoiseSIMD/FastNoiseSIMD_avx512.cpp (100%) rename {include => deps}/FastNoiseSIMD/FastNoiseSIMD_internal.cpp (100%) rename {include => deps}/FastNoiseSIMD/FastNoiseSIMD_internal.h (100%) rename {include => deps}/FastNoiseSIMD/FastNoiseSIMD_neon.cpp (100%) rename {include => deps}/FastNoiseSIMD/FastNoiseSIMD_sse2.cpp (100%) rename {include => deps}/FastNoiseSIMD/FastNoiseSIMD_sse41.cpp (100%) rename {include => deps}/FastNoiseSIMD/LICENSE (100%) rename {include => deps}/gl3w/GL/gl3w.h (100%) rename {include => deps}/gl3w/GL/glcorearb.h (100%) rename {include => deps}/gl3w/KHR/khrplatform.h (100%) rename {include => deps}/gl3w/UNLICENSE (100%) rename {include => deps}/gl3w/gl3w.c (100%) rename {include => deps}/glfw/CMake/GenerateMappings.cmake (100%) rename {include => deps}/glfw/CMake/MacOSXBundleInfo.plist.in (100%) rename {include => deps}/glfw/CMake/i686-w64-mingw32-clang.cmake (100%) rename {include => deps}/glfw/CMake/i686-w64-mingw32.cmake (100%) rename {include => deps}/glfw/CMake/modules/FindEpollShim.cmake (100%) rename {include => deps}/glfw/CMake/modules/FindOSMesa.cmake (100%) rename {include => deps}/glfw/CMake/modules/FindWaylandProtocols.cmake (100%) rename {include => deps}/glfw/CMake/modules/FindXKBCommon.cmake (100%) rename {include => deps}/glfw/CMake/x86_64-w64-mingw32-clang.cmake (100%) rename {include => deps}/glfw/CMake/x86_64-w64-mingw32.cmake (100%) rename {include => deps}/glfw/CMakeLists.txt (100%) rename {include => deps}/glfw/LICENSE.md (100%) rename {include => deps}/glfw/README.md (100%) rename {include => deps}/glfw/cmake_uninstall.cmake.in (100%) rename {include => deps}/glfw/deps/getopt.c (100%) rename {include => deps}/glfw/deps/getopt.h (100%) rename {include => deps}/glfw/deps/glad/gl.h (100%) rename {include => deps}/glfw/deps/glad/khrplatform.h (100%) rename {include => deps}/glfw/deps/glad/vk_platform.h (100%) rename {include => deps}/glfw/deps/glad/vulkan.h (100%) rename {include => deps}/glfw/deps/glad_gl.c (100%) rename {include => deps}/glfw/deps/glad_vulkan.c (100%) rename {include => deps}/glfw/deps/linmath.h (100%) rename {include => deps}/glfw/deps/mingw/_mingw_dxhelper.h (100%) rename {include => deps}/glfw/deps/mingw/dinput.h (100%) rename {include => deps}/glfw/deps/mingw/xinput.h (100%) rename {include => deps}/glfw/deps/nuklear.h (100%) rename {include => deps}/glfw/deps/nuklear_glfw_gl2.h (100%) rename {include => deps}/glfw/deps/stb_image_write.h (100%) rename {include => deps}/glfw/deps/tinycthread.c (100%) rename {include => deps}/glfw/deps/tinycthread.h (100%) rename {include => deps}/glfw/deps/vs2008/stdint.h (100%) rename {include => deps}/glfw/include/GLFW/glfw3.h (100%) rename {include => deps}/glfw/include/GLFW/glfw3native.h (100%) rename {include => deps}/glfw/src/CMakeLists.txt (100%) rename {include => deps}/glfw/src/cocoa_init.m (100%) rename {include => deps}/glfw/src/cocoa_joystick.h (100%) rename {include => deps}/glfw/src/cocoa_joystick.m (100%) rename {include => deps}/glfw/src/cocoa_monitor.m (100%) rename {include => deps}/glfw/src/cocoa_platform.h (100%) rename {include => deps}/glfw/src/cocoa_time.c (100%) rename {include => deps}/glfw/src/cocoa_window.m (100%) rename {include => deps}/glfw/src/context.c (100%) rename {include => deps}/glfw/src/egl_context.c (100%) rename {include => deps}/glfw/src/egl_context.h (100%) rename {include => deps}/glfw/src/glfw3.pc.in (100%) rename {include => deps}/glfw/src/glfw3Config.cmake.in (100%) rename {include => deps}/glfw/src/glfw_config.h.in (100%) rename {include => deps}/glfw/src/glx_context.c (100%) rename {include => deps}/glfw/src/glx_context.h (100%) rename {include => deps}/glfw/src/init.c (100%) rename {include => deps}/glfw/src/input.c (100%) rename {include => deps}/glfw/src/internal.h (100%) rename {include => deps}/glfw/src/linux_joystick.c (100%) rename {include => deps}/glfw/src/linux_joystick.h (100%) rename {include => deps}/glfw/src/mappings.h (100%) rename {include => deps}/glfw/src/mappings.h.in (100%) rename {include => deps}/glfw/src/monitor.c (100%) rename {include => deps}/glfw/src/nsgl_context.h (100%) rename {include => deps}/glfw/src/nsgl_context.m (100%) rename {include => deps}/glfw/src/null_init.c (100%) rename {include => deps}/glfw/src/null_joystick.c (100%) rename {include => deps}/glfw/src/null_joystick.h (100%) rename {include => deps}/glfw/src/null_monitor.c (100%) rename {include => deps}/glfw/src/null_platform.h (100%) rename {include => deps}/glfw/src/null_window.c (100%) rename {include => deps}/glfw/src/osmesa_context.c (100%) rename {include => deps}/glfw/src/osmesa_context.h (100%) rename {include => deps}/glfw/src/posix_thread.c (100%) rename {include => deps}/glfw/src/posix_thread.h (100%) rename {include => deps}/glfw/src/posix_time.c (100%) rename {include => deps}/glfw/src/posix_time.h (100%) rename {include => deps}/glfw/src/vulkan.c (100%) rename {include => deps}/glfw/src/wgl_context.c (100%) rename {include => deps}/glfw/src/wgl_context.h (100%) rename {include => deps}/glfw/src/win32_init.c (100%) rename {include => deps}/glfw/src/win32_joystick.c (100%) rename {include => deps}/glfw/src/win32_joystick.h (100%) rename {include => deps}/glfw/src/win32_monitor.c (100%) rename {include => deps}/glfw/src/win32_platform.h (100%) rename {include => deps}/glfw/src/win32_thread.c (100%) rename {include => deps}/glfw/src/win32_time.c (100%) rename {include => deps}/glfw/src/win32_window.c (100%) rename {include => deps}/glfw/src/window.c (100%) rename {include => deps}/glfw/src/wl_init.c (100%) rename {include => deps}/glfw/src/wl_monitor.c (100%) rename {include => deps}/glfw/src/wl_platform.h (100%) rename {include => deps}/glfw/src/wl_window.c (100%) rename {include => deps}/glfw/src/x11_init.c (100%) rename {include => deps}/glfw/src/x11_monitor.c (100%) rename {include => deps}/glfw/src/x11_platform.h (100%) rename {include => deps}/glfw/src/x11_window.c (100%) rename {include => deps}/glfw/src/xkb_unicode.c (100%) rename {include => deps}/glfw/src/xkb_unicode.h (100%) rename {include => deps}/glm/CMakeLists.txt (100%) rename {include => deps}/glm/common.hpp (100%) rename {include => deps}/glm/copying.txt (100%) rename {include => deps}/glm/detail/_features.hpp (100%) rename {include => deps}/glm/detail/_fixes.hpp (100%) rename {include => deps}/glm/detail/_noise.hpp (100%) rename {include => deps}/glm/detail/_swizzle.hpp (100%) rename {include => deps}/glm/detail/_swizzle_func.hpp (100%) rename {include => deps}/glm/detail/_vectorize.hpp (100%) rename {include => deps}/glm/detail/compute_common.hpp (100%) rename {include => deps}/glm/detail/compute_vector_relational.hpp (100%) rename {include => deps}/glm/detail/func_common.inl (100%) rename {include => deps}/glm/detail/func_common_simd.inl (100%) rename {include => deps}/glm/detail/func_exponential.inl (100%) rename {include => deps}/glm/detail/func_exponential_simd.inl (100%) rename {include => deps}/glm/detail/func_geometric.inl (100%) rename {include => deps}/glm/detail/func_geometric_simd.inl (100%) rename {include => deps}/glm/detail/func_integer.inl (100%) rename {include => deps}/glm/detail/func_integer_simd.inl (100%) rename {include => deps}/glm/detail/func_matrix.inl (100%) rename {include => deps}/glm/detail/func_matrix_simd.inl (100%) rename {include => deps}/glm/detail/func_packing.inl (100%) rename {include => deps}/glm/detail/func_packing_simd.inl (100%) rename {include => deps}/glm/detail/func_trigonometric.inl (100%) rename {include => deps}/glm/detail/func_trigonometric_simd.inl (100%) rename {include => deps}/glm/detail/func_vector_relational.inl (100%) rename {include => deps}/glm/detail/func_vector_relational_simd.inl (100%) rename {include => deps}/glm/detail/glm.cpp (100%) rename {include => deps}/glm/detail/qualifier.hpp (100%) rename {include => deps}/glm/detail/setup.hpp (100%) rename {include => deps}/glm/detail/type_float.hpp (100%) rename {include => deps}/glm/detail/type_half.hpp (100%) rename {include => deps}/glm/detail/type_half.inl (100%) rename {include => deps}/glm/detail/type_mat2x2.hpp (100%) rename {include => deps}/glm/detail/type_mat2x2.inl (100%) rename {include => deps}/glm/detail/type_mat2x3.hpp (100%) rename {include => deps}/glm/detail/type_mat2x3.inl (100%) rename {include => deps}/glm/detail/type_mat2x4.hpp (100%) rename {include => deps}/glm/detail/type_mat2x4.inl (100%) rename {include => deps}/glm/detail/type_mat3x2.hpp (100%) rename {include => deps}/glm/detail/type_mat3x2.inl (100%) rename {include => deps}/glm/detail/type_mat3x3.hpp (100%) rename {include => deps}/glm/detail/type_mat3x3.inl (100%) rename {include => deps}/glm/detail/type_mat3x4.hpp (100%) rename {include => deps}/glm/detail/type_mat3x4.inl (100%) rename {include => deps}/glm/detail/type_mat4x2.hpp (100%) rename {include => deps}/glm/detail/type_mat4x2.inl (100%) rename {include => deps}/glm/detail/type_mat4x3.hpp (100%) rename {include => deps}/glm/detail/type_mat4x3.inl (100%) rename {include => deps}/glm/detail/type_mat4x4.hpp (100%) rename {include => deps}/glm/detail/type_mat4x4.inl (100%) rename {include => deps}/glm/detail/type_mat4x4_simd.inl (100%) rename {include => deps}/glm/detail/type_quat.hpp (100%) rename {include => deps}/glm/detail/type_quat.inl (100%) rename {include => deps}/glm/detail/type_quat_simd.inl (100%) rename {include => deps}/glm/detail/type_vec1.hpp (100%) rename {include => deps}/glm/detail/type_vec1.inl (100%) rename {include => deps}/glm/detail/type_vec2.hpp (100%) rename {include => deps}/glm/detail/type_vec2.inl (100%) rename {include => deps}/glm/detail/type_vec3.hpp (100%) rename {include => deps}/glm/detail/type_vec3.inl (100%) rename {include => deps}/glm/detail/type_vec4.hpp (100%) rename {include => deps}/glm/detail/type_vec4.inl (100%) rename {include => deps}/glm/detail/type_vec4_simd.inl (100%) rename {include => deps}/glm/exponential.hpp (100%) rename {include => deps}/glm/ext.hpp (100%) rename {include => deps}/glm/ext/matrix_clip_space.hpp (100%) rename {include => deps}/glm/ext/matrix_clip_space.inl (100%) rename {include => deps}/glm/ext/matrix_common.hpp (100%) rename {include => deps}/glm/ext/matrix_common.inl (100%) rename {include => deps}/glm/ext/matrix_double2x2.hpp (100%) rename {include => deps}/glm/ext/matrix_double2x2_precision.hpp (100%) rename {include => deps}/glm/ext/matrix_double2x3.hpp (100%) rename {include => deps}/glm/ext/matrix_double2x3_precision.hpp (100%) rename {include => deps}/glm/ext/matrix_double2x4.hpp (100%) rename {include => deps}/glm/ext/matrix_double2x4_precision.hpp (100%) rename {include => deps}/glm/ext/matrix_double3x2.hpp (100%) rename {include => deps}/glm/ext/matrix_double3x2_precision.hpp (100%) rename {include => deps}/glm/ext/matrix_double3x3.hpp (100%) rename {include => deps}/glm/ext/matrix_double3x3_precision.hpp (100%) rename {include => deps}/glm/ext/matrix_double3x4.hpp (100%) rename {include => deps}/glm/ext/matrix_double3x4_precision.hpp (100%) rename {include => deps}/glm/ext/matrix_double4x2.hpp (100%) rename {include => deps}/glm/ext/matrix_double4x2_precision.hpp (100%) rename {include => deps}/glm/ext/matrix_double4x3.hpp (100%) rename {include => deps}/glm/ext/matrix_double4x3_precision.hpp (100%) rename {include => deps}/glm/ext/matrix_double4x4.hpp (100%) rename {include => deps}/glm/ext/matrix_double4x4_precision.hpp (100%) rename {include => deps}/glm/ext/matrix_float2x2.hpp (100%) rename {include => deps}/glm/ext/matrix_float2x2_precision.hpp (100%) rename {include => deps}/glm/ext/matrix_float2x3.hpp (100%) rename {include => deps}/glm/ext/matrix_float2x3_precision.hpp (100%) rename {include => deps}/glm/ext/matrix_float2x4.hpp (100%) rename {include => deps}/glm/ext/matrix_float2x4_precision.hpp (100%) rename {include => deps}/glm/ext/matrix_float3x2.hpp (100%) rename {include => deps}/glm/ext/matrix_float3x2_precision.hpp (100%) rename {include => deps}/glm/ext/matrix_float3x3.hpp (100%) rename {include => deps}/glm/ext/matrix_float3x3_precision.hpp (100%) rename {include => deps}/glm/ext/matrix_float3x4.hpp (100%) rename {include => deps}/glm/ext/matrix_float3x4_precision.hpp (100%) rename {include => deps}/glm/ext/matrix_float4x2.hpp (100%) rename {include => deps}/glm/ext/matrix_float4x2_precision.hpp (100%) rename {include => deps}/glm/ext/matrix_float4x3.hpp (100%) rename {include => deps}/glm/ext/matrix_float4x3_precision.hpp (100%) rename {include => deps}/glm/ext/matrix_float4x4.hpp (100%) rename {include => deps}/glm/ext/matrix_float4x4_precision.hpp (100%) rename {include => deps}/glm/ext/matrix_int2x2.hpp (100%) rename {include => deps}/glm/ext/matrix_int2x2_sized.hpp (100%) rename {include => deps}/glm/ext/matrix_int2x3.hpp (100%) rename {include => deps}/glm/ext/matrix_int2x3_sized.hpp (100%) rename {include => deps}/glm/ext/matrix_int2x4.hpp (100%) rename {include => deps}/glm/ext/matrix_int2x4_sized.hpp (100%) rename {include => deps}/glm/ext/matrix_int3x2.hpp (100%) rename {include => deps}/glm/ext/matrix_int3x2_sized.hpp (100%) rename {include => deps}/glm/ext/matrix_int3x3.hpp (100%) rename {include => deps}/glm/ext/matrix_int3x3_sized.hpp (100%) rename {include => deps}/glm/ext/matrix_int3x4.hpp (100%) rename {include => deps}/glm/ext/matrix_int3x4_sized.hpp (100%) rename {include => deps}/glm/ext/matrix_int4x2.hpp (100%) rename {include => deps}/glm/ext/matrix_int4x2_sized.hpp (100%) rename {include => deps}/glm/ext/matrix_int4x3.hpp (100%) rename {include => deps}/glm/ext/matrix_int4x3_sized.hpp (100%) rename {include => deps}/glm/ext/matrix_int4x4.hpp (100%) rename {include => deps}/glm/ext/matrix_int4x4_sized.hpp (100%) rename {include => deps}/glm/ext/matrix_projection.hpp (100%) rename {include => deps}/glm/ext/matrix_projection.inl (100%) rename {include => deps}/glm/ext/matrix_relational.hpp (100%) rename {include => deps}/glm/ext/matrix_relational.inl (100%) rename {include => deps}/glm/ext/matrix_transform.hpp (100%) rename {include => deps}/glm/ext/matrix_transform.inl (100%) rename {include => deps}/glm/ext/matrix_uint2x2.hpp (100%) rename {include => deps}/glm/ext/matrix_uint2x2_sized.hpp (100%) rename {include => deps}/glm/ext/matrix_uint2x3.hpp (100%) rename {include => deps}/glm/ext/matrix_uint2x3_sized.hpp (100%) rename {include => deps}/glm/ext/matrix_uint2x4.hpp (100%) rename {include => deps}/glm/ext/matrix_uint2x4_sized.hpp (100%) rename {include => deps}/glm/ext/matrix_uint3x2.hpp (100%) rename {include => deps}/glm/ext/matrix_uint3x2_sized.hpp (100%) rename {include => deps}/glm/ext/matrix_uint3x3.hpp (100%) rename {include => deps}/glm/ext/matrix_uint3x3_sized.hpp (100%) rename {include => deps}/glm/ext/matrix_uint3x4.hpp (100%) rename {include => deps}/glm/ext/matrix_uint3x4_sized.hpp (100%) rename {include => deps}/glm/ext/matrix_uint4x2.hpp (100%) rename {include => deps}/glm/ext/matrix_uint4x2_sized.hpp (100%) rename {include => deps}/glm/ext/matrix_uint4x3.hpp (100%) rename {include => deps}/glm/ext/matrix_uint4x3_sized.hpp (100%) rename {include => deps}/glm/ext/matrix_uint4x4.hpp (100%) rename {include => deps}/glm/ext/matrix_uint4x4_sized.hpp (100%) rename {include => deps}/glm/ext/quaternion_common.hpp (100%) rename {include => deps}/glm/ext/quaternion_common.inl (100%) rename {include => deps}/glm/ext/quaternion_common_simd.inl (100%) rename {include => deps}/glm/ext/quaternion_double.hpp (100%) rename {include => deps}/glm/ext/quaternion_double_precision.hpp (100%) rename {include => deps}/glm/ext/quaternion_exponential.hpp (100%) rename {include => deps}/glm/ext/quaternion_exponential.inl (100%) rename {include => deps}/glm/ext/quaternion_float.hpp (100%) rename {include => deps}/glm/ext/quaternion_float_precision.hpp (100%) rename {include => deps}/glm/ext/quaternion_geometric.hpp (100%) rename {include => deps}/glm/ext/quaternion_geometric.inl (100%) rename {include => deps}/glm/ext/quaternion_relational.hpp (100%) rename {include => deps}/glm/ext/quaternion_relational.inl (100%) rename {include => deps}/glm/ext/quaternion_transform.hpp (100%) rename {include => deps}/glm/ext/quaternion_transform.inl (100%) rename {include => deps}/glm/ext/quaternion_trigonometric.hpp (100%) rename {include => deps}/glm/ext/quaternion_trigonometric.inl (100%) rename {include => deps}/glm/ext/scalar_common.hpp (100%) rename {include => deps}/glm/ext/scalar_common.inl (100%) rename {include => deps}/glm/ext/scalar_constants.hpp (100%) rename {include => deps}/glm/ext/scalar_constants.inl (100%) rename {include => deps}/glm/ext/scalar_int_sized.hpp (100%) rename {include => deps}/glm/ext/scalar_integer.hpp (100%) rename {include => deps}/glm/ext/scalar_integer.inl (100%) rename {include => deps}/glm/ext/scalar_packing.hpp (100%) rename {include => deps}/glm/ext/scalar_packing.inl (100%) rename {include => deps}/glm/ext/scalar_relational.hpp (100%) rename {include => deps}/glm/ext/scalar_relational.inl (100%) rename {include => deps}/glm/ext/scalar_uint_sized.hpp (100%) rename {include => deps}/glm/ext/scalar_ulp.hpp (100%) rename {include => deps}/glm/ext/scalar_ulp.inl (100%) rename {include => deps}/glm/ext/vector_bool1.hpp (100%) rename {include => deps}/glm/ext/vector_bool1_precision.hpp (100%) rename {include => deps}/glm/ext/vector_bool2.hpp (100%) rename {include => deps}/glm/ext/vector_bool2_precision.hpp (100%) rename {include => deps}/glm/ext/vector_bool3.hpp (100%) rename {include => deps}/glm/ext/vector_bool3_precision.hpp (100%) rename {include => deps}/glm/ext/vector_bool4.hpp (100%) rename {include => deps}/glm/ext/vector_bool4_precision.hpp (100%) rename {include => deps}/glm/ext/vector_common.hpp (100%) rename {include => deps}/glm/ext/vector_common.inl (100%) rename {include => deps}/glm/ext/vector_double1.hpp (100%) rename {include => deps}/glm/ext/vector_double1_precision.hpp (100%) rename {include => deps}/glm/ext/vector_double2.hpp (100%) rename {include => deps}/glm/ext/vector_double2_precision.hpp (100%) rename {include => deps}/glm/ext/vector_double3.hpp (100%) rename {include => deps}/glm/ext/vector_double3_precision.hpp (100%) rename {include => deps}/glm/ext/vector_double4.hpp (100%) rename {include => deps}/glm/ext/vector_double4_precision.hpp (100%) rename {include => deps}/glm/ext/vector_float1.hpp (100%) rename {include => deps}/glm/ext/vector_float1_precision.hpp (100%) rename {include => deps}/glm/ext/vector_float2.hpp (100%) rename {include => deps}/glm/ext/vector_float2_precision.hpp (100%) rename {include => deps}/glm/ext/vector_float3.hpp (100%) rename {include => deps}/glm/ext/vector_float3_precision.hpp (100%) rename {include => deps}/glm/ext/vector_float4.hpp (100%) rename {include => deps}/glm/ext/vector_float4_precision.hpp (100%) rename {include => deps}/glm/ext/vector_int1.hpp (100%) rename {include => deps}/glm/ext/vector_int1_sized.hpp (100%) rename {include => deps}/glm/ext/vector_int2.hpp (100%) rename {include => deps}/glm/ext/vector_int2_sized.hpp (100%) rename {include => deps}/glm/ext/vector_int3.hpp (100%) rename {include => deps}/glm/ext/vector_int3_sized.hpp (100%) rename {include => deps}/glm/ext/vector_int4.hpp (100%) rename {include => deps}/glm/ext/vector_int4_sized.hpp (100%) rename {include => deps}/glm/ext/vector_integer.hpp (100%) rename {include => deps}/glm/ext/vector_integer.inl (100%) rename {include => deps}/glm/ext/vector_packing.hpp (100%) rename {include => deps}/glm/ext/vector_packing.inl (100%) rename {include => deps}/glm/ext/vector_relational.hpp (100%) rename {include => deps}/glm/ext/vector_relational.inl (100%) rename {include => deps}/glm/ext/vector_uint1.hpp (100%) rename {include => deps}/glm/ext/vector_uint1_sized.hpp (100%) rename {include => deps}/glm/ext/vector_uint2.hpp (100%) rename {include => deps}/glm/ext/vector_uint2_sized.hpp (100%) rename {include => deps}/glm/ext/vector_uint3.hpp (100%) rename {include => deps}/glm/ext/vector_uint3_sized.hpp (100%) rename {include => deps}/glm/ext/vector_uint4.hpp (100%) rename {include => deps}/glm/ext/vector_uint4_sized.hpp (100%) rename {include => deps}/glm/ext/vector_ulp.hpp (100%) rename {include => deps}/glm/ext/vector_ulp.inl (100%) rename {include => deps}/glm/fwd.hpp (100%) rename {include => deps}/glm/geometric.hpp (100%) rename {include => deps}/glm/glm.hpp (100%) rename {include => deps}/glm/gtc/bitfield.hpp (100%) rename {include => deps}/glm/gtc/bitfield.inl (100%) rename {include => deps}/glm/gtc/color_space.hpp (100%) rename {include => deps}/glm/gtc/color_space.inl (100%) rename {include => deps}/glm/gtc/constants.hpp (100%) rename {include => deps}/glm/gtc/constants.inl (100%) rename {include => deps}/glm/gtc/epsilon.hpp (100%) rename {include => deps}/glm/gtc/epsilon.inl (100%) rename {include => deps}/glm/gtc/integer.hpp (100%) rename {include => deps}/glm/gtc/integer.inl (100%) rename {include => deps}/glm/gtc/matrix_access.hpp (100%) rename {include => deps}/glm/gtc/matrix_access.inl (100%) rename {include => deps}/glm/gtc/matrix_integer.hpp (100%) rename {include => deps}/glm/gtc/matrix_inverse.hpp (100%) rename {include => deps}/glm/gtc/matrix_inverse.inl (100%) rename {include => deps}/glm/gtc/matrix_transform.hpp (100%) rename {include => deps}/glm/gtc/matrix_transform.inl (100%) rename {include => deps}/glm/gtc/noise.hpp (100%) rename {include => deps}/glm/gtc/noise.inl (100%) rename {include => deps}/glm/gtc/packing.hpp (100%) rename {include => deps}/glm/gtc/packing.inl (100%) rename {include => deps}/glm/gtc/quaternion.hpp (100%) rename {include => deps}/glm/gtc/quaternion.inl (100%) rename {include => deps}/glm/gtc/quaternion_simd.inl (100%) rename {include => deps}/glm/gtc/random.hpp (100%) rename {include => deps}/glm/gtc/random.inl (100%) rename {include => deps}/glm/gtc/reciprocal.hpp (100%) rename {include => deps}/glm/gtc/reciprocal.inl (100%) rename {include => deps}/glm/gtc/round.hpp (100%) rename {include => deps}/glm/gtc/round.inl (100%) rename {include => deps}/glm/gtc/type_aligned.hpp (100%) rename {include => deps}/glm/gtc/type_precision.hpp (100%) rename {include => deps}/glm/gtc/type_precision.inl (100%) rename {include => deps}/glm/gtc/type_ptr.hpp (100%) rename {include => deps}/glm/gtc/type_ptr.inl (100%) rename {include => deps}/glm/gtc/ulp.hpp (100%) rename {include => deps}/glm/gtc/ulp.inl (100%) rename {include => deps}/glm/gtc/vec1.hpp (100%) rename {include => deps}/glm/gtx/associated_min_max.hpp (100%) rename {include => deps}/glm/gtx/associated_min_max.inl (100%) rename {include => deps}/glm/gtx/bit.hpp (100%) rename {include => deps}/glm/gtx/bit.inl (100%) rename {include => deps}/glm/gtx/closest_point.hpp (100%) rename {include => deps}/glm/gtx/closest_point.inl (100%) rename {include => deps}/glm/gtx/color_encoding.hpp (100%) rename {include => deps}/glm/gtx/color_encoding.inl (100%) rename {include => deps}/glm/gtx/color_space.hpp (100%) rename {include => deps}/glm/gtx/color_space.inl (100%) rename {include => deps}/glm/gtx/color_space_YCoCg.hpp (100%) rename {include => deps}/glm/gtx/color_space_YCoCg.inl (100%) rename {include => deps}/glm/gtx/common.hpp (100%) rename {include => deps}/glm/gtx/common.inl (100%) rename {include => deps}/glm/gtx/compatibility.hpp (100%) rename {include => deps}/glm/gtx/compatibility.inl (100%) rename {include => deps}/glm/gtx/component_wise.hpp (100%) rename {include => deps}/glm/gtx/component_wise.inl (100%) rename {include => deps}/glm/gtx/dual_quaternion.hpp (100%) rename {include => deps}/glm/gtx/dual_quaternion.inl (100%) rename {include => deps}/glm/gtx/easing.hpp (100%) rename {include => deps}/glm/gtx/easing.inl (100%) rename {include => deps}/glm/gtx/euler_angles.hpp (100%) rename {include => deps}/glm/gtx/euler_angles.inl (100%) rename {include => deps}/glm/gtx/extend.hpp (100%) rename {include => deps}/glm/gtx/extend.inl (100%) rename {include => deps}/glm/gtx/extended_min_max.hpp (100%) rename {include => deps}/glm/gtx/extended_min_max.inl (100%) rename {include => deps}/glm/gtx/exterior_product.hpp (100%) rename {include => deps}/glm/gtx/exterior_product.inl (100%) rename {include => deps}/glm/gtx/fast_exponential.hpp (100%) rename {include => deps}/glm/gtx/fast_exponential.inl (100%) rename {include => deps}/glm/gtx/fast_square_root.hpp (100%) rename {include => deps}/glm/gtx/fast_square_root.inl (100%) rename {include => deps}/glm/gtx/fast_trigonometry.hpp (100%) rename {include => deps}/glm/gtx/fast_trigonometry.inl (100%) rename {include => deps}/glm/gtx/float_notmalize.inl (100%) rename {include => deps}/glm/gtx/functions.hpp (100%) rename {include => deps}/glm/gtx/functions.inl (100%) rename {include => deps}/glm/gtx/gradient_paint.hpp (100%) rename {include => deps}/glm/gtx/gradient_paint.inl (100%) rename {include => deps}/glm/gtx/handed_coordinate_space.hpp (100%) rename {include => deps}/glm/gtx/handed_coordinate_space.inl (100%) rename {include => deps}/glm/gtx/hash.hpp (100%) rename {include => deps}/glm/gtx/hash.inl (100%) rename {include => deps}/glm/gtx/integer.hpp (100%) rename {include => deps}/glm/gtx/integer.inl (100%) rename {include => deps}/glm/gtx/intersect.hpp (100%) rename {include => deps}/glm/gtx/intersect.inl (100%) rename {include => deps}/glm/gtx/io.hpp (100%) rename {include => deps}/glm/gtx/io.inl (100%) rename {include => deps}/glm/gtx/log_base.hpp (100%) rename {include => deps}/glm/gtx/log_base.inl (100%) rename {include => deps}/glm/gtx/matrix_cross_product.hpp (100%) rename {include => deps}/glm/gtx/matrix_cross_product.inl (100%) rename {include => deps}/glm/gtx/matrix_decompose.hpp (100%) rename {include => deps}/glm/gtx/matrix_decompose.inl (100%) rename {include => deps}/glm/gtx/matrix_factorisation.hpp (100%) rename {include => deps}/glm/gtx/matrix_factorisation.inl (100%) rename {include => deps}/glm/gtx/matrix_interpolation.hpp (100%) rename {include => deps}/glm/gtx/matrix_interpolation.inl (100%) rename {include => deps}/glm/gtx/matrix_major_storage.hpp (100%) rename {include => deps}/glm/gtx/matrix_major_storage.inl (100%) rename {include => deps}/glm/gtx/matrix_operation.hpp (100%) rename {include => deps}/glm/gtx/matrix_operation.inl (100%) rename {include => deps}/glm/gtx/matrix_query.hpp (100%) rename {include => deps}/glm/gtx/matrix_query.inl (100%) rename {include => deps}/glm/gtx/matrix_transform_2d.hpp (100%) rename {include => deps}/glm/gtx/matrix_transform_2d.inl (100%) rename {include => deps}/glm/gtx/mixed_product.hpp (100%) rename {include => deps}/glm/gtx/mixed_product.inl (100%) rename {include => deps}/glm/gtx/norm.hpp (100%) rename {include => deps}/glm/gtx/norm.inl (100%) rename {include => deps}/glm/gtx/normal.hpp (100%) rename {include => deps}/glm/gtx/normal.inl (100%) rename {include => deps}/glm/gtx/normalize_dot.hpp (100%) rename {include => deps}/glm/gtx/normalize_dot.inl (100%) rename {include => deps}/glm/gtx/number_precision.hpp (100%) rename {include => deps}/glm/gtx/number_precision.inl (100%) rename {include => deps}/glm/gtx/optimum_pow.hpp (100%) rename {include => deps}/glm/gtx/optimum_pow.inl (100%) rename {include => deps}/glm/gtx/orthonormalize.hpp (100%) rename {include => deps}/glm/gtx/orthonormalize.inl (100%) rename {include => deps}/glm/gtx/perpendicular.hpp (100%) rename {include => deps}/glm/gtx/perpendicular.inl (100%) rename {include => deps}/glm/gtx/polar_coordinates.hpp (100%) rename {include => deps}/glm/gtx/polar_coordinates.inl (100%) rename {include => deps}/glm/gtx/projection.hpp (100%) rename {include => deps}/glm/gtx/projection.inl (100%) rename {include => deps}/glm/gtx/quaternion.hpp (100%) rename {include => deps}/glm/gtx/quaternion.inl (100%) rename {include => deps}/glm/gtx/range.hpp (100%) rename {include => deps}/glm/gtx/raw_data.hpp (100%) rename {include => deps}/glm/gtx/raw_data.inl (100%) rename {include => deps}/glm/gtx/rotate_normalized_axis.hpp (100%) rename {include => deps}/glm/gtx/rotate_normalized_axis.inl (100%) rename {include => deps}/glm/gtx/rotate_vector.hpp (100%) rename {include => deps}/glm/gtx/rotate_vector.inl (100%) rename {include => deps}/glm/gtx/scalar_multiplication.hpp (100%) rename {include => deps}/glm/gtx/scalar_relational.hpp (100%) rename {include => deps}/glm/gtx/scalar_relational.inl (100%) rename {include => deps}/glm/gtx/spline.hpp (100%) rename {include => deps}/glm/gtx/spline.inl (100%) rename {include => deps}/glm/gtx/std_based_type.hpp (100%) rename {include => deps}/glm/gtx/std_based_type.inl (100%) rename {include => deps}/glm/gtx/string_cast.hpp (100%) rename {include => deps}/glm/gtx/string_cast.inl (100%) rename {include => deps}/glm/gtx/texture.hpp (100%) rename {include => deps}/glm/gtx/texture.inl (100%) rename {include => deps}/glm/gtx/transform.hpp (100%) rename {include => deps}/glm/gtx/transform.inl (100%) rename {include => deps}/glm/gtx/transform2.hpp (100%) rename {include => deps}/glm/gtx/transform2.inl (100%) rename {include => deps}/glm/gtx/type_aligned.hpp (100%) rename {include => deps}/glm/gtx/type_aligned.inl (100%) rename {include => deps}/glm/gtx/type_trait.hpp (100%) rename {include => deps}/glm/gtx/type_trait.inl (100%) rename {include => deps}/glm/gtx/vec_swizzle.hpp (100%) rename {include => deps}/glm/gtx/vector_angle.hpp (100%) rename {include => deps}/glm/gtx/vector_angle.inl (100%) rename {include => deps}/glm/gtx/vector_query.hpp (100%) rename {include => deps}/glm/gtx/vector_query.inl (100%) rename {include => deps}/glm/gtx/wrap.hpp (100%) rename {include => deps}/glm/gtx/wrap.inl (100%) rename {include => deps}/glm/integer.hpp (100%) rename {include => deps}/glm/mat2x2.hpp (100%) rename {include => deps}/glm/mat2x3.hpp (100%) rename {include => deps}/glm/mat2x4.hpp (100%) rename {include => deps}/glm/mat3x2.hpp (100%) rename {include => deps}/glm/mat3x3.hpp (100%) rename {include => deps}/glm/mat3x4.hpp (100%) rename {include => deps}/glm/mat4x2.hpp (100%) rename {include => deps}/glm/mat4x3.hpp (100%) rename {include => deps}/glm/mat4x4.hpp (100%) rename {include => deps}/glm/matrix.hpp (100%) rename {include => deps}/glm/packing.hpp (100%) rename {include => deps}/glm/simd/common.h (100%) rename {include => deps}/glm/simd/exponential.h (100%) rename {include => deps}/glm/simd/geometric.h (100%) rename {include => deps}/glm/simd/integer.h (100%) rename {include => deps}/glm/simd/matrix.h (100%) rename {include => deps}/glm/simd/neon.h (100%) rename {include => deps}/glm/simd/packing.h (100%) rename {include => deps}/glm/simd/platform.h (100%) rename {include => deps}/glm/simd/trigonometric.h (100%) rename {include => deps}/glm/simd/vector_relational.h (100%) rename {include => deps}/glm/trigonometric.hpp (100%) rename {include => deps}/glm/vec2.hpp (100%) rename {include => deps}/glm/vec3.hpp (100%) rename {include => deps}/glm/vec4.hpp (100%) rename {include => deps}/glm/vector_relational.hpp (100%) rename {include => deps}/imgui/LICENSE.txt (100%) rename {include => deps}/imgui/imconfig.h (100%) rename {include => deps}/imgui/imgui.cpp (100%) rename {include => deps}/imgui/imgui.h (100%) rename {include => deps}/imgui/imgui_demo.cpp (100%) rename {include => deps}/imgui/imgui_draw.cpp (100%) rename {include => deps}/imgui/imgui_impl_glfw.cpp (100%) rename {include => deps}/imgui/imgui_impl_glfw.h (100%) rename {include => deps}/imgui/imgui_impl_opengl3.cpp (100%) rename {include => deps}/imgui/imgui_impl_opengl3.h (100%) rename {include => deps}/imgui/imgui_impl_vulkan.cpp (100%) rename {include => deps}/imgui/imgui_impl_vulkan.h (100%) rename {include => deps}/imgui/imgui_internal.h (100%) rename {include => deps}/imgui/imgui_widgets.cpp (100%) rename {include => deps}/imgui/imstb_rectpack.h (100%) rename {include => deps}/imgui/imstb_textedit.h (100%) rename {include => deps}/imgui/imstb_truetype.h (100%) rename {include => deps}/libguarded/LICENSE (100%) rename {include => deps}/libguarded/mutex_guarded.h (100%) rename {include => deps}/libguarded/shared_mutex_guarded.h (100%) rename {include => deps}/meshoptimizer/CONTRIBUTING.md (100%) rename {include => deps}/meshoptimizer/LICENSE.md (100%) rename {include => deps}/meshoptimizer/README.md (100%) rename {include => deps}/meshoptimizer/allocator.cpp (100%) rename {include => deps}/meshoptimizer/clusterizer.cpp (100%) rename {include => deps}/meshoptimizer/extern/basisu_format.h (100%) rename {include => deps}/meshoptimizer/extern/cgltf.h (100%) rename {include => deps}/meshoptimizer/extern/fast_obj.h (100%) rename {include => deps}/meshoptimizer/extern/khr_df.h (100%) rename {include => deps}/meshoptimizer/indexcodec.cpp (100%) rename {include => deps}/meshoptimizer/indexgenerator.cpp (100%) rename {include => deps}/meshoptimizer/meshoptimizer.h (100%) rename {include => deps}/meshoptimizer/overdrawanalyzer.cpp (100%) rename {include => deps}/meshoptimizer/overdrawoptimizer.cpp (100%) rename {include => deps}/meshoptimizer/simplifier.cpp (100%) rename {include => deps}/meshoptimizer/spatialorder.cpp (100%) rename {include => deps}/meshoptimizer/stripifier.cpp (100%) rename {include => deps}/meshoptimizer/vcacheanalyzer.cpp (100%) rename {include => deps}/meshoptimizer/vcacheoptimizer.cpp (100%) rename {include => deps}/meshoptimizer/vertexcodec.cpp (100%) rename {include => deps}/meshoptimizer/vertexfilter.cpp (100%) rename {include => deps}/meshoptimizer/vfetchanalyzer.cpp (100%) rename {include => deps}/meshoptimizer/vfetchoptimizer.cpp (100%) rename {include => deps}/mini-yaml/LICENSE (100%) rename {include => deps}/mini-yaml/README.md (100%) rename {include => deps}/mini-yaml/Yaml.cpp (100%) rename {include => deps}/mini-yaml/Yaml.hpp (100%) create mode 100644 deps/picoquic/CMakeLists.txt create mode 100644 deps/picoquic/LICENSE create mode 100644 deps/picoquic/README.md create mode 100644 deps/picoquic/ReadMe.txt create mode 100644 deps/picoquic/bbr.c create mode 100644 deps/picoquic/bytestream.c create mode 100644 deps/picoquic/bytestream.h create mode 100644 deps/picoquic/cc_common.c create mode 100644 deps/picoquic/cc_common.h create mode 100644 deps/picoquic/cidset.h create mode 100644 deps/picoquic/cubic.c create mode 100644 deps/picoquic/fastcc.c create mode 100644 deps/picoquic/frames.c create mode 100644 deps/picoquic/intformat.c create mode 100644 deps/picoquic/logger.c create mode 100644 deps/picoquic/logwriter.c create mode 100644 deps/picoquic/newreno.c create mode 100644 deps/picoquic/packet.c create mode 100644 deps/picoquic/picohash.c create mode 100644 deps/picoquic/picohash.h create mode 100644 deps/picoquic/picoquic.h create mode 100644 deps/picoquic/picoquic.vcxproj create mode 100644 deps/picoquic/picoquic.vcxproj.filters create mode 100644 deps/picoquic/picoquic_binlog.h create mode 100644 deps/picoquic/picoquic_internal.h create mode 100644 deps/picoquic/picoquic_logger.h create mode 100644 deps/picoquic/picoquic_packet_loop.h create mode 100644 deps/picoquic/picoquic_unified_log.h create mode 100644 deps/picoquic/picoquic_utils.h create mode 100644 deps/picoquic/picosocks.c create mode 100644 deps/picoquic/picosocks.h create mode 100644 deps/picoquic/picosplay.c create mode 100644 deps/picoquic/picosplay.h create mode 100644 deps/picoquic/picotls/CMakeLists.txt create mode 100644 deps/picoquic/picotls/README.md create mode 100644 deps/picoquic/picotls/WindowsPort.md create mode 100644 deps/picoquic/picotls/deps/cifra/.travis.yml create mode 100644 deps/picoquic/picotls/deps/cifra/COPYING create mode 100644 deps/picoquic/picotls/deps/cifra/README.md create mode 100644 deps/picoquic/picotls/deps/cifra/curve25519-shootout.md create mode 100644 deps/picoquic/picotls/deps/cifra/doc/Makefile create mode 100644 deps/picoquic/picotls/deps/cifra/doc/build.py create mode 100644 deps/picoquic/picotls/deps/cifra/doc/conf.py create mode 100644 deps/picoquic/picotls/deps/cifra/doc/index.rst create mode 100644 deps/picoquic/picotls/deps/cifra/extra_vecs/.gitignore create mode 100644 deps/picoquic/picotls/deps/cifra/extra_vecs/Makefile create mode 100644 deps/picoquic/picotls/deps/cifra/extra_vecs/README.md create mode 100644 deps/picoquic/picotls/deps/cifra/extra_vecs/openssl-hash.c create mode 100644 deps/picoquic/picotls/deps/cifra/extra_vecs/python-hash.py create mode 100644 deps/picoquic/picotls/deps/cifra/shitlisp/.gitignore create mode 100644 deps/picoquic/picotls/deps/cifra/shitlisp/Makefile create mode 100644 deps/picoquic/picotls/deps/cifra/shitlisp/sl-cifra.c create mode 100644 deps/picoquic/picotls/deps/cifra/shitlisp/test-aes.sl create mode 100644 deps/picoquic/picotls/deps/cifra/shitlisp/test-pbkdf2.sl create mode 100644 deps/picoquic/picotls/deps/cifra/shitlisp/test-sha224.sl create mode 100644 deps/picoquic/picotls/deps/cifra/shitlisp/test-sha256.sl create mode 100644 deps/picoquic/picotls/deps/cifra/shitlisp/test-sha384.sl create mode 100644 deps/picoquic/picotls/deps/cifra/shitlisp/test-sha512.sl create mode 100644 deps/picoquic/picotls/deps/cifra/src/.gitignore create mode 100644 deps/picoquic/picotls/deps/cifra/src/Makefile create mode 100644 deps/picoquic/picotls/deps/cifra/src/aes.c create mode 100644 deps/picoquic/picotls/deps/cifra/src/aes.h create mode 100644 deps/picoquic/picotls/deps/cifra/src/arm/.gitignore create mode 100644 deps/picoquic/picotls/deps/cifra/src/arm/Makefile create mode 100644 deps/picoquic/picotls/deps/cifra/src/arm/analyse.py create mode 100644 deps/picoquic/picotls/deps/cifra/src/arm/boot.c create mode 100644 deps/picoquic/picotls/deps/cifra/src/arm/curve25519-results.txt create mode 100644 deps/picoquic/picotls/deps/cifra/src/arm/ext/cutest.h create mode 100644 deps/picoquic/picotls/deps/cifra/src/arm/linkscript.efm32.ld create mode 100644 deps/picoquic/picotls/deps/cifra/src/arm/linkscript.lm3s6965evb.ld create mode 100644 deps/picoquic/picotls/deps/cifra/src/arm/linkscript.qemucm3.ld create mode 100644 deps/picoquic/picotls/deps/cifra/src/arm/linkscript.std.ld create mode 100644 deps/picoquic/picotls/deps/cifra/src/arm/linkscript.stm32f0.ld create mode 100644 deps/picoquic/picotls/deps/cifra/src/arm/linkscript.stm32f1.ld create mode 100644 deps/picoquic/picotls/deps/cifra/src/arm/linkscript.stm32f3.ld create mode 100644 deps/picoquic/picotls/deps/cifra/src/arm/main.c create mode 100644 deps/picoquic/picotls/deps/cifra/src/arm/memcpy.s create mode 100644 deps/picoquic/picotls/deps/cifra/src/arm/memset.s create mode 100644 deps/picoquic/picotls/deps/cifra/src/arm/merge.py create mode 100644 deps/picoquic/picotls/deps/cifra/src/arm/openocd.efm32.cfg create mode 100644 deps/picoquic/picotls/deps/cifra/src/arm/openocd.stm32f0.cfg create mode 100644 deps/picoquic/picotls/deps/cifra/src/arm/openocd.stm32f1.cfg create mode 100644 deps/picoquic/picotls/deps/cifra/src/arm/openocd.stm32f3.cfg create mode 100644 deps/picoquic/picotls/deps/cifra/src/arm/report.py create mode 100644 deps/picoquic/picotls/deps/cifra/src/arm/semihost.c create mode 100644 deps/picoquic/picotls/deps/cifra/src/arm/semihost.h create mode 100644 deps/picoquic/picotls/deps/cifra/src/arm/semihost.s create mode 100644 deps/picoquic/picotls/deps/cifra/src/arm/unacl/cortex_m0_mpy121666.s create mode 100644 deps/picoquic/picotls/deps/cifra/src/arm/unacl/cortex_m0_reduce25519.s create mode 100644 deps/picoquic/picotls/deps/cifra/src/arm/unacl/mul.s create mode 100644 deps/picoquic/picotls/deps/cifra/src/arm/unacl/scalarmult.c create mode 100644 deps/picoquic/picotls/deps/cifra/src/arm/unacl/sqr.s create mode 100644 deps/picoquic/picotls/deps/cifra/src/bitops.h create mode 100644 deps/picoquic/picotls/deps/cifra/src/blockwise.c create mode 100644 deps/picoquic/picotls/deps/cifra/src/blockwise.h create mode 100644 deps/picoquic/picotls/deps/cifra/src/cbcmac.c create mode 100644 deps/picoquic/picotls/deps/cifra/src/ccm.c create mode 100644 deps/picoquic/picotls/deps/cifra/src/cf_config.h create mode 100644 deps/picoquic/picotls/deps/cifra/src/chacha20.c create mode 100644 deps/picoquic/picotls/deps/cifra/src/chacha20poly1305.c create mode 100644 deps/picoquic/picotls/deps/cifra/src/chacha20poly1305.h create mode 100644 deps/picoquic/picotls/deps/cifra/src/chash.c create mode 100644 deps/picoquic/picotls/deps/cifra/src/chash.h create mode 100644 deps/picoquic/picotls/deps/cifra/src/cmac.c create mode 100644 deps/picoquic/picotls/deps/cifra/src/curve25519.c create mode 100644 deps/picoquic/picotls/deps/cifra/src/curve25519.donna.c create mode 100644 deps/picoquic/picotls/deps/cifra/src/curve25519.h create mode 100644 deps/picoquic/picotls/deps/cifra/src/curve25519.naclref.c create mode 100644 deps/picoquic/picotls/deps/cifra/src/curve25519.tweetnacl.c create mode 100644 deps/picoquic/picotls/deps/cifra/src/drbg.c create mode 100644 deps/picoquic/picotls/deps/cifra/src/drbg.h create mode 100644 deps/picoquic/picotls/deps/cifra/src/eax.c create mode 100644 deps/picoquic/picotls/deps/cifra/src/ext/cutest.h create mode 100644 deps/picoquic/picotls/deps/cifra/src/ext/handy.h create mode 100644 deps/picoquic/picotls/deps/cifra/src/gcm.c create mode 100644 deps/picoquic/picotls/deps/cifra/src/gf128.c create mode 100644 deps/picoquic/picotls/deps/cifra/src/gf128.h create mode 100644 deps/picoquic/picotls/deps/cifra/src/hmac.c create mode 100644 deps/picoquic/picotls/deps/cifra/src/hmac.h create mode 100644 deps/picoquic/picotls/deps/cifra/src/modes.c create mode 100644 deps/picoquic/picotls/deps/cifra/src/modes.h create mode 100644 deps/picoquic/picotls/deps/cifra/src/norx.c create mode 100644 deps/picoquic/picotls/deps/cifra/src/norx.h create mode 100644 deps/picoquic/picotls/deps/cifra/src/ocb.c create mode 100644 deps/picoquic/picotls/deps/cifra/src/pbkdf2.c create mode 100644 deps/picoquic/picotls/deps/cifra/src/pbkdf2.h create mode 100644 deps/picoquic/picotls/deps/cifra/src/poly1305.c create mode 100644 deps/picoquic/picotls/deps/cifra/src/poly1305.h create mode 100644 deps/picoquic/picotls/deps/cifra/src/poly1305.py create mode 100644 deps/picoquic/picotls/deps/cifra/src/prp.h create mode 100644 deps/picoquic/picotls/deps/cifra/src/salsa20.c create mode 100644 deps/picoquic/picotls/deps/cifra/src/salsa20.h create mode 100644 deps/picoquic/picotls/deps/cifra/src/sha1.c create mode 100644 deps/picoquic/picotls/deps/cifra/src/sha1.h create mode 100644 deps/picoquic/picotls/deps/cifra/src/sha2.h create mode 100644 deps/picoquic/picotls/deps/cifra/src/sha256.c create mode 100644 deps/picoquic/picotls/deps/cifra/src/sha3.c create mode 100644 deps/picoquic/picotls/deps/cifra/src/sha3.h create mode 100644 deps/picoquic/picotls/deps/cifra/src/sha512.c create mode 100644 deps/picoquic/picotls/deps/cifra/src/tassert.h create mode 100644 deps/picoquic/picotls/deps/cifra/src/testaes.c create mode 100644 deps/picoquic/picotls/deps/cifra/src/testchacha20poly1305.c create mode 100644 deps/picoquic/picotls/deps/cifra/src/testcurve25519.c create mode 100644 deps/picoquic/picotls/deps/cifra/src/testdrbg.c create mode 100644 deps/picoquic/picotls/deps/cifra/src/testmodes.c create mode 100644 deps/picoquic/picotls/deps/cifra/src/testnorx.c create mode 100644 deps/picoquic/picotls/deps/cifra/src/testnorx.katdata.inc create mode 100644 deps/picoquic/picotls/deps/cifra/src/testpoly1305.c create mode 100644 deps/picoquic/picotls/deps/cifra/src/testsalsa20.c create mode 100644 deps/picoquic/picotls/deps/cifra/src/testsha.h create mode 100644 deps/picoquic/picotls/deps/cifra/src/testsha1.c create mode 100644 deps/picoquic/picotls/deps/cifra/src/testsha2.c create mode 100644 deps/picoquic/picotls/deps/cifra/src/testsha3.c create mode 100644 deps/picoquic/picotls/deps/cifra/src/testutil.h create mode 100644 deps/picoquic/picotls/deps/micro-ecc/.gitignore create mode 100644 deps/picoquic/picotls/deps/micro-ecc/LICENSE.txt create mode 100644 deps/picoquic/picotls/deps/micro-ecc/README.md create mode 100644 deps/picoquic/picotls/deps/micro-ecc/asm_arm.inc create mode 100644 deps/picoquic/picotls/deps/micro-ecc/asm_arm_mult_square.inc create mode 100644 deps/picoquic/picotls/deps/micro-ecc/asm_arm_mult_square_umaal.inc create mode 100644 deps/picoquic/picotls/deps/micro-ecc/asm_avr.inc create mode 100644 deps/picoquic/picotls/deps/micro-ecc/asm_avr_mult_square.inc create mode 100644 deps/picoquic/picotls/deps/micro-ecc/curve-specific.inc create mode 100644 deps/picoquic/picotls/deps/micro-ecc/emk_project.py create mode 100644 deps/picoquic/picotls/deps/micro-ecc/emk_rules.py create mode 100644 deps/picoquic/picotls/deps/micro-ecc/platform-specific.inc create mode 100755 deps/picoquic/picotls/deps/micro-ecc/scripts/mult_arm.py create mode 100755 deps/picoquic/picotls/deps/micro-ecc/scripts/mult_avr.py create mode 100755 deps/picoquic/picotls/deps/micro-ecc/scripts/mult_avr_extra.py create mode 100755 deps/picoquic/picotls/deps/micro-ecc/scripts/square_arm.py create mode 100755 deps/picoquic/picotls/deps/micro-ecc/scripts/square_avr.py create mode 100644 deps/picoquic/picotls/deps/micro-ecc/test/ecc_test/ecc_test.ino create mode 100644 deps/picoquic/picotls/deps/micro-ecc/test/emk_rules.py create mode 100644 deps/picoquic/picotls/deps/micro-ecc/test/test_compress.c create mode 100644 deps/picoquic/picotls/deps/micro-ecc/test/test_compute.c create mode 100644 deps/picoquic/picotls/deps/micro-ecc/test/test_ecdh.c create mode 100644 deps/picoquic/picotls/deps/micro-ecc/test/test_ecdsa.c create mode 100644 deps/picoquic/picotls/deps/micro-ecc/test/test_ecdsa_deterministic.c.example create mode 100644 deps/picoquic/picotls/deps/micro-ecc/types.h create mode 100644 deps/picoquic/picotls/deps/micro-ecc/uECC.c create mode 100644 deps/picoquic/picotls/deps/micro-ecc/uECC.h create mode 100644 deps/picoquic/picotls/deps/micro-ecc/uECC_vli.h create mode 100644 deps/picoquic/picotls/include/picotls.h create mode 100644 deps/picoquic/picotls/include/picotls/asn1.h create mode 100644 deps/picoquic/picotls/include/picotls/certificate_compression.h create mode 100644 deps/picoquic/picotls/include/picotls/ffx.h create mode 100644 deps/picoquic/picotls/include/picotls/fusion.h create mode 100644 deps/picoquic/picotls/include/picotls/minicrypto.h create mode 100644 deps/picoquic/picotls/include/picotls/openssl.h create mode 100644 deps/picoquic/picotls/include/picotls/pembase64.h create mode 100644 deps/picoquic/picotls/include/picotls/ptlsbcrypt.h create mode 100644 deps/picoquic/picotls/lib/asn1.c create mode 100644 deps/picoquic/picotls/lib/certificate_compression.c create mode 100644 deps/picoquic/picotls/lib/cifra.c create mode 100644 deps/picoquic/picotls/lib/cifra/aes-common.h create mode 100644 deps/picoquic/picotls/lib/cifra/aes128.c create mode 100644 deps/picoquic/picotls/lib/cifra/aes256.c create mode 100644 deps/picoquic/picotls/lib/cifra/chacha20.c create mode 100644 deps/picoquic/picotls/lib/cifra/random.c create mode 100644 deps/picoquic/picotls/lib/cifra/x25519.c create mode 100644 deps/picoquic/picotls/lib/ffx.c create mode 100644 deps/picoquic/picotls/lib/fusion.c create mode 100644 deps/picoquic/picotls/lib/minicrypto-pem.c create mode 100644 deps/picoquic/picotls/lib/openssl.c create mode 100644 deps/picoquic/picotls/lib/pembase64.c create mode 100644 deps/picoquic/picotls/lib/picotls.c create mode 100644 deps/picoquic/picotls/lib/ptlsbcrypt.c create mode 100644 deps/picoquic/picotls/lib/uecc.c create mode 100644 deps/picoquic/picotls/picotlsvs/bcrypt-test/bcrypt-test.c create mode 100644 deps/picoquic/picotls/picotlsvs/bcrypt-test/bcrypt-test.vcxproj create mode 100644 deps/picoquic/picotls/picotlsvs/bcrypt-test/bcrypt-test.vcxproj.filters create mode 100644 deps/picoquic/picotls/picotlsvs/bcrypt-test/bcrypt-test.vcxproj.user create mode 100644 deps/picoquic/picotls/picotlsvs/cifra/ReadMe.txt create mode 100644 deps/picoquic/picotls/picotlsvs/cifra/cifra.vcxproj create mode 100644 deps/picoquic/picotls/picotlsvs/cifra/cifra.vcxproj.filters create mode 100644 deps/picoquic/picotls/picotlsvs/fusiontest/fusiontest.vcxproj create mode 100644 deps/picoquic/picotls/picotlsvs/fusiontest/fusiontest.vcxproj.filters create mode 100644 deps/picoquic/picotls/picotlsvs/fusiontest/fusiontest.vcxproj.user create mode 100644 deps/picoquic/picotls/picotlsvs/microecc/ReadMe.txt create mode 100644 deps/picoquic/picotls/picotlsvs/microecc/microecc.vcxproj create mode 100644 deps/picoquic/picotls/picotlsvs/microecc/microecc.vcxproj.filters create mode 100644 deps/picoquic/picotls/picotlsvs/openssl.cnf create mode 100644 deps/picoquic/picotls/picotlsvs/picotls-bcrypt/picotls-bcrypt.vcxproj create mode 100644 deps/picoquic/picotls/picotlsvs/picotls-bcrypt/picotls-bcrypt.vcxproj.filters create mode 100644 deps/picoquic/picotls/picotlsvs/picotls-bcrypt/picotls-bcrypt.vcxproj.user create mode 100644 deps/picoquic/picotls/picotlsvs/picotls-core/picotls-core.vcxproj create mode 100644 deps/picoquic/picotls/picotlsvs/picotls-core/picotls-core.vcxproj.filters create mode 100644 deps/picoquic/picotls/picotlsvs/picotls-core/picotls-core.vcxproj.user create mode 100644 deps/picoquic/picotls/picotlsvs/picotls-core/targetver.h create mode 100644 deps/picoquic/picotls/picotlsvs/picotls-esni/getopt.c create mode 100644 deps/picoquic/picotls/picotlsvs/picotls-esni/getopt.h create mode 100644 deps/picoquic/picotls/picotlsvs/picotls-esni/picotls-esni.vcxproj create mode 100644 deps/picoquic/picotls/picotlsvs/picotls-esni/picotls-esni.vcxproj.filters create mode 100644 deps/picoquic/picotls/picotlsvs/picotls-esni/picotls-esni.vcxproj.user create mode 100644 deps/picoquic/picotls/picotlsvs/picotls-fusion/picotls-fusion.vcxproj create mode 100644 deps/picoquic/picotls/picotlsvs/picotls-fusion/picotls-fusion.vcxproj.filters create mode 100644 deps/picoquic/picotls/picotlsvs/picotls-fusion/picotls-fusion.vcxproj.user create mode 100644 deps/picoquic/picotls/picotlsvs/picotls-minicrypto-deps/picotls-minicrypto-deps.vcxproj create mode 100644 deps/picoquic/picotls/picotlsvs/picotls-minicrypto-deps/picotls-minicrypto-deps.vcxproj.filters create mode 100644 deps/picoquic/picotls/picotlsvs/picotls-minicrypto-deps/picotls-minicrypto-deps.vcxproj.user create mode 100644 deps/picoquic/picotls/picotlsvs/picotls-minicrypto-deps/targetver.h create mode 100644 deps/picoquic/picotls/picotlsvs/picotls-minicrypto/picotls-minicrypto.vcxproj create mode 100644 deps/picoquic/picotls/picotlsvs/picotls-minicrypto/picotls-minicrypto.vcxproj.filters create mode 100644 deps/picoquic/picotls/picotlsvs/picotls-minicrypto/picotls-minicrypto.vcxproj.user create mode 100644 deps/picoquic/picotls/picotlsvs/picotls-minicrypto/targetver.h create mode 100644 deps/picoquic/picotls/picotlsvs/picotls-openssl/picotls-openssl.vcxproj create mode 100644 deps/picoquic/picotls/picotlsvs/picotls-openssl/picotls-openssl.vcxproj.filters create mode 100644 deps/picoquic/picotls/picotlsvs/picotls-openssl/picotls-openssl.vcxproj.user create mode 100644 deps/picoquic/picotls/picotlsvs/picotls-openssl/targetver.h create mode 100644 deps/picoquic/picotls/picotlsvs/picotls/ReadMe.txt create mode 100644 deps/picoquic/picotls/picotlsvs/picotls/picotls.vcxproj create mode 100644 deps/picoquic/picotls/picotlsvs/picotls/picotls.vcxproj.filters create mode 100644 deps/picoquic/picotls/picotlsvs/picotls/picotls.vcxproj.user create mode 100644 deps/picoquic/picotls/picotlsvs/picotls/targetver.h create mode 100644 deps/picoquic/picotls/picotlsvs/picotls/wincompat.h create mode 100644 deps/picoquic/picotls/picotlsvs/picotls/wintimeofday.c create mode 100644 deps/picoquic/picotls/picotlsvs/picotlsvs.sln create mode 100644 deps/picoquic/picotls/picotlsvs/picotlsvs/ReadMe.txt create mode 100644 deps/picoquic/picotls/picotlsvs/picotlsvs/cert.pem create mode 100644 deps/picoquic/picotls/picotlsvs/picotlsvs/ec_cert.pem create mode 100644 deps/picoquic/picotls/picotlsvs/picotlsvs/ec_key.pem create mode 100644 deps/picoquic/picotls/picotlsvs/picotlsvs/key-test-1.pem create mode 100644 deps/picoquic/picotls/picotlsvs/picotlsvs/key-test-2.pem create mode 100644 deps/picoquic/picotls/picotlsvs/picotlsvs/key-test-3.pem create mode 100644 deps/picoquic/picotls/picotlsvs/picotlsvs/key-test-4.pem create mode 100644 deps/picoquic/picotls/picotlsvs/picotlsvs/key.pem create mode 100644 deps/picoquic/picotls/picotlsvs/picotlsvs/myec1.pem create mode 100644 deps/picoquic/picotls/picotlsvs/picotlsvs/openssl.cnf create mode 100644 deps/picoquic/picotls/picotlsvs/picotlsvs/picotlsvs.c create mode 100644 deps/picoquic/picotls/picotlsvs/picotlsvs/picotlsvs.vcxproj create mode 100644 deps/picoquic/picotls/picotlsvs/picotlsvs/picotlsvs.vcxproj.filters create mode 100644 deps/picoquic/picotls/picotlsvs/picotlsvs/picotlsvs.vcxproj.user create mode 100644 deps/picoquic/picotls/picotlsvs/picotlsvs/targetver.h create mode 100644 deps/picoquic/picotls/picotlsvs/pioctls-core/pioctls-core.vcxproj create mode 100644 deps/picoquic/picotls/picotlsvs/pioctls-core/pioctls-core.vcxproj.filters create mode 100644 deps/picoquic/picotls/picotlsvs/pioctls-core/pioctls-core.vcxproj.user create mode 100644 deps/picoquic/picotls/picotlsvs/pioctls-core/stdafx.cpp create mode 100644 deps/picoquic/picotls/picotlsvs/pioctls-core/stdafx.h create mode 100644 deps/picoquic/picotls/picotlsvs/pioctls-core/targetver.h create mode 100644 deps/picoquic/picotls/picotlsvs/ptlsbench/ptlsbench.vcxproj create mode 100644 deps/picoquic/picotls/picotlsvs/ptlsbench/ptlsbench.vcxproj.filters create mode 100644 deps/picoquic/picotls/picotlsvs/ptlsbench/ptlsbench.vcxproj.user create mode 100644 deps/picoquic/picotls/picotlsvs/testopenssl/testopenssl.vcxproj create mode 100644 deps/picoquic/picotls/picotlsvs/testopenssl/testopenssl.vcxproj.filters create mode 100644 deps/picoquic/picotls/src/esni.c create mode 100644 deps/picoquic/quicctx.c create mode 100644 deps/picoquic/sacks.c create mode 100644 deps/picoquic/sender.c create mode 100644 deps/picoquic/sim_link.c create mode 100644 deps/picoquic/sockloop.c create mode 100644 deps/picoquic/spinbit.c create mode 100644 deps/picoquic/ticket_store.c create mode 100644 deps/picoquic/tls_api.c create mode 100644 deps/picoquic/tls_api.h create mode 100644 deps/picoquic/token_store.c create mode 100644 deps/picoquic/transport.c create mode 100644 deps/picoquic/unified_log.c create mode 100644 deps/picoquic/util.c create mode 100644 deps/picoquic/wincompat.h create mode 100644 deps/picoquic/winsockloop.c rename {include => deps}/robin_hood/robin_hood.h (100%) rename {include => deps}/toml++/toml.h (100%) rename {include => deps}/toml++/toml_array.h (100%) rename {include => deps}/toml++/toml_array.hpp (100%) rename {include => deps}/toml++/toml_common.h (100%) rename {include => deps}/toml++/toml_date_time.h (100%) rename {include => deps}/toml++/toml_default_formatter.h (100%) rename {include => deps}/toml++/toml_default_formatter.hpp (100%) rename {include => deps}/toml++/toml_formatter.h (100%) rename {include => deps}/toml++/toml_instantiations.hpp (100%) rename {include => deps}/toml++/toml_json_formatter.h (100%) rename {include => deps}/toml++/toml_json_formatter.hpp (100%) rename {include => deps}/toml++/toml_node.h (100%) rename {include => deps}/toml++/toml_node.hpp (100%) rename {include => deps}/toml++/toml_node_view.h (100%) rename {include => deps}/toml++/toml_parse_error.h (100%) rename {include => deps}/toml++/toml_parser.h (100%) rename {include => deps}/toml++/toml_parser.hpp (100%) rename {include => deps}/toml++/toml_preprocessor.h (100%) rename {include => deps}/toml++/toml_print_to_stream.h (100%) rename {include => deps}/toml++/toml_table.h (100%) rename {include => deps}/toml++/toml_table.hpp (100%) rename {include => deps}/toml++/toml_utf8.h (100%) rename {include => deps}/toml++/toml_utf8_generated.h (100%) rename {include => deps}/toml++/toml_utf8_streams.h (100%) rename {include => deps}/toml++/toml_value.h (100%) rename {include => deps}/toml++/toml_version.h (100%) rename {include => deps}/tracy/AUTHORS (100%) rename {include => deps}/tracy/LICENSE (100%) rename {include => deps}/tracy/README.md (100%) rename {include => deps}/tracy/Tracy.hpp (100%) rename {include => deps}/tracy/TracyC.h (100%) rename {include => deps}/tracy/TracyClient.cpp (100%) rename {include => deps}/tracy/TracyOpenGL.hpp (100%) rename {include => deps}/tracy/TracyVulkan.hpp (100%) rename {include => deps}/tracy/client/TracyArmCpuTable.hpp (100%) rename {include => deps}/tracy/client/TracyCallstack.cpp (100%) rename {include => deps}/tracy/client/TracyCallstack.h (100%) rename {include => deps}/tracy/client/TracyCallstack.hpp (100%) rename {include => deps}/tracy/client/TracyDxt1.cpp (100%) rename {include => deps}/tracy/client/TracyDxt1.hpp (100%) rename {include => deps}/tracy/client/TracyFastVector.hpp (100%) rename {include => deps}/tracy/client/TracyLock.hpp (100%) rename {include => deps}/tracy/client/TracyProfiler.cpp (100%) rename {include => deps}/tracy/client/TracyProfiler.hpp (100%) rename {include => deps}/tracy/client/TracyScoped.hpp (100%) rename {include => deps}/tracy/client/TracySysTime.cpp (100%) rename {include => deps}/tracy/client/TracySysTime.hpp (100%) rename {include => deps}/tracy/client/TracySysTrace.cpp (100%) rename {include => deps}/tracy/client/TracySysTrace.hpp (100%) rename {include => deps}/tracy/client/TracySysTracePayload.hpp (100%) rename {include => deps}/tracy/client/TracyThread.hpp (100%) rename {include => deps}/tracy/client/tracy_concurrentqueue.h (100%) rename {include => deps}/tracy/client/tracy_rpmalloc.cpp (100%) rename {include => deps}/tracy/client/tracy_rpmalloc.hpp (100%) rename {include => deps}/tracy/common/TracyAlign.hpp (100%) rename {include => deps}/tracy/common/TracyAlloc.hpp (100%) rename {include => deps}/tracy/common/TracyApi.h (100%) rename {include => deps}/tracy/common/TracyColor.hpp (100%) rename {include => deps}/tracy/common/TracyForceInline.hpp (100%) rename {include => deps}/tracy/common/TracyMutex.hpp (100%) rename {include => deps}/tracy/common/TracyProtocol.hpp (100%) rename {include => deps}/tracy/common/TracyQueue.hpp (100%) rename {include => deps}/tracy/common/TracySocket.cpp (100%) rename {include => deps}/tracy/common/TracySocket.hpp (100%) rename {include => deps}/tracy/common/TracySystem.cpp (100%) rename {include => deps}/tracy/common/TracySystem.hpp (100%) rename {include => deps}/tracy/common/tracy_lz4.cpp (100%) rename {include => deps}/tracy/common/tracy_lz4.hpp (100%) rename {include => deps}/tracy/common/tracy_lz4hc.cpp (100%) rename {include => deps}/tracy/common/tracy_lz4hc.hpp (100%) rename {include => deps}/tracy/libbacktrace/LICENSE (100%) rename {include => deps}/tracy/libbacktrace/alloc.cpp (100%) rename {include => deps}/tracy/libbacktrace/backtrace.hpp (100%) rename {include => deps}/tracy/libbacktrace/config.h (100%) rename {include => deps}/tracy/libbacktrace/dwarf.cpp (100%) rename {include => deps}/tracy/libbacktrace/elf.cpp (100%) rename {include => deps}/tracy/libbacktrace/fileline.cpp (100%) rename {include => deps}/tracy/libbacktrace/filenames.hpp (100%) rename {include => deps}/tracy/libbacktrace/internal.hpp (100%) rename {include => deps}/tracy/libbacktrace/macho.cpp (100%) rename {include => deps}/tracy/libbacktrace/mmapio.cpp (100%) rename {include => deps}/tracy/libbacktrace/posix.cpp (100%) rename {include => deps}/tracy/libbacktrace/sort.cpp (100%) rename {include => deps}/tracy/libbacktrace/state.cpp (100%) rename {include => deps}/volk/volk.c (100%) rename {include => deps}/volk/volk.h (100%) rename {include => deps}/zstd/CMakeLists.txt (100%) rename {include => deps}/zstd/common/bitstream.h (100%) rename {include => deps}/zstd/common/compiler.h (100%) rename {include => deps}/zstd/common/cpu.h (100%) rename {include => deps}/zstd/common/debug.c (100%) rename {include => deps}/zstd/common/debug.h (100%) rename {include => deps}/zstd/common/entropy_common.c (100%) rename {include => deps}/zstd/common/error_private.c (100%) rename {include => deps}/zstd/common/error_private.h (100%) rename {include => deps}/zstd/common/fse.h (100%) rename {include => deps}/zstd/common/fse_decompress.c (100%) rename {include => deps}/zstd/common/huf.h (100%) rename {include => deps}/zstd/common/mem.h (100%) rename {include => deps}/zstd/common/pool.c (100%) rename {include => deps}/zstd/common/pool.h (100%) rename {include => deps}/zstd/common/threading.c (100%) rename {include => deps}/zstd/common/threading.h (100%) rename {include => deps}/zstd/common/xxhash.c (100%) rename {include => deps}/zstd/common/xxhash.h (100%) rename {include => deps}/zstd/common/zstd_common.c (100%) rename {include => deps}/zstd/common/zstd_errors.h (100%) rename {include => deps}/zstd/common/zstd_internal.h (100%) rename {include => deps}/zstd/compress/fse_compress.c (100%) rename {include => deps}/zstd/compress/hist.c (100%) rename {include => deps}/zstd/compress/hist.h (100%) rename {include => deps}/zstd/compress/huf_compress.c (100%) rename {include => deps}/zstd/compress/zstd_compress.c (100%) rename {include => deps}/zstd/compress/zstd_compress_internal.h (100%) rename {include => deps}/zstd/compress/zstd_compress_literals.c (100%) rename {include => deps}/zstd/compress/zstd_compress_literals.h (100%) rename {include => deps}/zstd/compress/zstd_compress_sequences.c (100%) rename {include => deps}/zstd/compress/zstd_compress_sequences.h (100%) rename {include => deps}/zstd/compress/zstd_compress_superblock.c (100%) rename {include => deps}/zstd/compress/zstd_compress_superblock.h (100%) rename {include => deps}/zstd/compress/zstd_cwksp.h (100%) rename {include => deps}/zstd/compress/zstd_double_fast.c (100%) rename {include => deps}/zstd/compress/zstd_double_fast.h (100%) rename {include => deps}/zstd/compress/zstd_fast.c (100%) rename {include => deps}/zstd/compress/zstd_fast.h (100%) rename {include => deps}/zstd/compress/zstd_lazy.c (100%) rename {include => deps}/zstd/compress/zstd_lazy.h (100%) rename {include => deps}/zstd/compress/zstd_ldm.c (100%) rename {include => deps}/zstd/compress/zstd_ldm.h (100%) rename {include => deps}/zstd/compress/zstd_opt.c (100%) rename {include => deps}/zstd/compress/zstd_opt.h (100%) rename {include => deps}/zstd/compress/zstdmt_compress.c (100%) rename {include => deps}/zstd/compress/zstdmt_compress.h (100%) rename {include => deps}/zstd/decompress/huf_decompress.c (100%) rename {include => deps}/zstd/decompress/zstd_ddict.c (100%) rename {include => deps}/zstd/decompress/zstd_ddict.h (100%) rename {include => deps}/zstd/decompress/zstd_decompress.c (100%) rename {include => deps}/zstd/decompress/zstd_decompress_block.c (100%) rename {include => deps}/zstd/decompress/zstd_decompress_block.h (100%) rename {include => deps}/zstd/decompress/zstd_decompress_internal.h (100%) rename {include => deps}/zstd/dictBuilder/cover.c (100%) rename {include => deps}/zstd/dictBuilder/cover.h (100%) rename {include => deps}/zstd/dictBuilder/divsufsort.c (100%) rename {include => deps}/zstd/dictBuilder/divsufsort.h (100%) rename {include => deps}/zstd/dictBuilder/fastcover.c (100%) rename {include => deps}/zstd/dictBuilder/zdict.c (100%) rename {include => deps}/zstd/dictBuilder/zdict.h (100%) rename {include => deps}/zstd/zstd.h (100%) delete mode 100644 include/enet/CMakeLists.txt delete mode 100644 include/enet/ChangeLog delete mode 100644 include/enet/LICENSE delete mode 100644 include/enet/Makefile.am delete mode 100644 include/enet/README delete mode 100644 include/enet/callbacks.c delete mode 100644 include/enet/compress.c delete mode 100644 include/enet/configure.ac delete mode 100644 include/enet/host.c delete mode 100644 include/enet/include/enet/callbacks.h delete mode 100644 include/enet/include/enet/enet.h delete mode 100644 include/enet/include/enet/list.h delete mode 100644 include/enet/include/enet/protocol.h delete mode 100644 include/enet/include/enet/time.h delete mode 100644 include/enet/include/enet/types.h delete mode 100644 include/enet/include/enet/unix.h delete mode 100644 include/enet/include/enet/utility.h delete mode 100644 include/enet/include/enet/win32.h delete mode 100644 include/enet/libenet.pc.in delete mode 100644 include/enet/list.c delete mode 100644 include/enet/packet.c delete mode 100644 include/enet/peer.c delete mode 100644 include/enet/premake4.lua delete mode 100644 include/enet/protocol.c delete mode 100644 include/enet/unix.c delete mode 100644 include/enet/win32.c create mode 100644 resource/content/cert.pem create mode 100644 resource/content/key.pem create mode 100644 src/client/net/Client.cpp create mode 100644 src/client/net/Client.hpp create mode 100644 src/core/data/mem.hpp delete mode 100644 src/core/net/Client.hpp create mode 100644 src/core/net/Context.cpp create mode 100644 src/core/net/Context.hpp delete mode 100644 src/core/net/PacketView.hpp delete mode 100644 src/core/net/Server.hpp create mode 100644 src/core/net/io.hpp create mode 100644 src/server/net/Server.cpp create mode 100644 src/server/net/Server.hpp diff --git a/.vscode/launch.json b/.vscode/launch.json index 80ff5e9..d77223f 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -40,6 +40,26 @@ "ignoreFailures": true } ] + }, + { + "name": "Run server (gdb debug)", + "type": "cppdbg", + "request": "launch", + "program": "${workspaceFolder}/build/univerxel-server", + "args": ["server.toml"], + "preLaunchTask": "Build debug server", + "stopAtEntry": false, + "cwd": "${workspaceFolder}/build", + "environment": [], + "externalConsole": false, + "MIMode": "gdb", + "setupCommands": [ + { + "description": "Activer l'impression en mode Pretty pour gdb", + "text": "-enable-pretty-printing", + "ignoreFailures": true + } + ] } ] } \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json index e010142..efcbe23 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -41,6 +41,15 @@ "cwd": "${workspaceRoot}/build" } }, + { + "label": "make server", + "type": "shell", + "command": "make", + "args": ["univerxel-server"], + "options": { + "cwd": "${workspaceRoot}/build" + } + }, { "label": "exec", "type": "shell", @@ -91,6 +100,12 @@ "dependsOrder": "sequence", "dependsOn": ["cmake debug", "make"] }, + { + "label": "Build debug server", + "group": "build", + "dependsOrder": "sequence", + "dependsOn": ["cmake debug", "make server"] + }, { "label": "Rebuild", "group": "build", diff --git a/CMakeLists.txt b/CMakeLists.txt index eff1c8a..7a70c4e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,6 +8,16 @@ option(USE_FMA "Use fma" 1) option(LOG_DEBUG "Show debug logs" 0) option(LOG_TRACE "Show trace logs" 0) +find_program(CCACHE_FOUND ccache) +if(CCACHE_FOUND) + set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE ccache) +endif(CCACHE_FOUND) + +add_subdirectory("deps/glfw") +add_subdirectory("deps/glm") +add_subdirectory("deps/picoquic") +add_subdirectory("deps/zstd") + if(NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE Release) endif() @@ -15,16 +25,6 @@ endif() set(CMAKE_CXX_FLAGS "-Wall -Wextra") set(CMAKE_EXPORT_COMPILE_COMMANDS ON) -find_program(CCACHE_FOUND ccache) -if(CCACHE_FOUND) - set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE ccache) -endif(CCACHE_FOUND) - -add_subdirectory("include/glfw") -add_subdirectory("include/glm") -add_subdirectory("include/enet") -add_subdirectory("include/zstd") - add_compile_definitions(FIXED_WINDOW=${FIXED_WINDOW} LOG_DEBUG=${LOG_DEBUG} LOG_TRACE=${LOG_TRACE} HN_USE_FILESYSTEM=1) if(PROFILING) add_compile_definitions(TRACY_ENABLE=1) @@ -39,16 +39,16 @@ if(USE_FMA) add_definitions(-mfma) endif(USE_FMA) -file(GLOB_RECURSE CORE_SOURCES "src/core/*.cpp" "include/tracy/TracyClient.cpp") -set(CORE_HEADERS "include/toml++" "include/robin_hood" "include/libguarded" "include/tracy") -set(CORE_LIBS pthread dl glm::glm_static enet::enet_static zstd::zstd_static) +file(GLOB_RECURSE CORE_SOURCES "src/core/*.cpp" "deps/tracy/TracyClient.cpp") +set(CORE_HEADERS "deps/toml++" "deps/robin_hood" "deps/libguarded" "deps/tracy") +set(CORE_LIBS pthread dl glm::glm_static picoquic-core zstd::zstd_static) -file(GLOB_RECURSE CLIENT_SOURCES "src/client/*.cpp" "include/imgui/*.cpp" "include/meshoptimizer/*.cpp" "include/gl3w/gl3w.c" "include/volk/volk.c") -set(CLIENT_HEADERS "include/imgui" "include/meshoptimizer" "include/gl3w" "include/volk") +file(GLOB_RECURSE CLIENT_SOURCES "src/client/*.cpp" "deps/imgui/*.cpp" "deps/meshoptimizer/*.cpp" "deps/gl3w/gl3w.c" "deps/volk/volk.c") +set(CLIENT_HEADERS "deps/imgui" "deps/meshoptimizer" "deps/gl3w" "deps/volk") set(CLIENT_LIBS glfw) -file(GLOB_RECURSE SERVER_SOURCES "src/server/*.cpp" "include/FastNoiseSIMD/*.cpp") -set(SERVER_HEADERS "include/FastNoiseSIMD") +file(GLOB_RECURSE SERVER_SOURCES "src/server/*.cpp" "deps/FastNoiseSIMD/*.cpp") +set(SERVER_HEADERS "deps/FastNoiseSIMD") set(SERVER_LINKED) # All in one exec diff --git a/README.md b/README.md index 245dbae..f41dd63 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ Experimental project using OpenGL and Vulkan. * OpenGL * Vulkan * ImGui -* ENet +* PicoQUIC * FasNoiseSIMD * Love and insomnia @@ -44,6 +44,7 @@ Get a release compatible with your system window library and processor capabilit ### Prerequisites * OpenGL or Vulkan driver +* OpenSSL 1.1+ ### Usage @@ -65,6 +66,7 @@ To get a local copy up and running, follow these simple steps. * C++17 * CMake 3.11 * System window headers *(xorg-dev)* +* OpenGL, Vulkan and OpenSSL headers #### Optionally diff --git a/TODO.md b/TODO.md index 1419ef6..d56534f 100644 --- a/TODO.md +++ b/TODO.md @@ -8,13 +8,12 @@ ## Hello other - - [~] Multiplayer + - [x] Multiplayer - [~] Chat - Private messages - [~] Authentication - [x] Compression - - [ ] Encryption - - DTLS + - [x] Encryption - [x] Embedded - [x] Standalone @@ -38,6 +37,7 @@ - [ ] Get models - [ ] Reduce compile unit count - [ ] Review documentation + - [ ] Clean kick ## Hello universe @@ -78,7 +78,7 @@ - [ ] Break area part to entity - [ ] Slash screen - [ ] Start/Pause menu - - [ ] QUIC protocol + - [x] QUIC protocol - [ ] 8 for 1 contouring problem - [ ] Use in memory protocol (to replace server_handle) - [ ] Octree diff --git a/include/FastNoiseSIMD/ARM/cpu-features.c b/deps/FastNoiseSIMD/ARM/cpu-features.c similarity index 100% rename from include/FastNoiseSIMD/ARM/cpu-features.c rename to deps/FastNoiseSIMD/ARM/cpu-features.c diff --git a/include/FastNoiseSIMD/ARM/cpu-features.h b/deps/FastNoiseSIMD/ARM/cpu-features.h similarity index 100% rename from include/FastNoiseSIMD/ARM/cpu-features.h rename to deps/FastNoiseSIMD/ARM/cpu-features.h diff --git a/include/FastNoiseSIMD/FastNoiseSIMD.cpp b/deps/FastNoiseSIMD/FastNoiseSIMD.cpp similarity index 100% rename from include/FastNoiseSIMD/FastNoiseSIMD.cpp rename to deps/FastNoiseSIMD/FastNoiseSIMD.cpp diff --git a/include/FastNoiseSIMD/FastNoiseSIMD.h b/deps/FastNoiseSIMD/FastNoiseSIMD.h similarity index 100% rename from include/FastNoiseSIMD/FastNoiseSIMD.h rename to deps/FastNoiseSIMD/FastNoiseSIMD.h diff --git a/include/FastNoiseSIMD/FastNoiseSIMD_avx2.cpp b/deps/FastNoiseSIMD/FastNoiseSIMD_avx2.cpp similarity index 100% rename from include/FastNoiseSIMD/FastNoiseSIMD_avx2.cpp rename to deps/FastNoiseSIMD/FastNoiseSIMD_avx2.cpp diff --git a/include/FastNoiseSIMD/FastNoiseSIMD_avx512.cpp b/deps/FastNoiseSIMD/FastNoiseSIMD_avx512.cpp similarity index 100% rename from include/FastNoiseSIMD/FastNoiseSIMD_avx512.cpp rename to deps/FastNoiseSIMD/FastNoiseSIMD_avx512.cpp diff --git a/include/FastNoiseSIMD/FastNoiseSIMD_internal.cpp b/deps/FastNoiseSIMD/FastNoiseSIMD_internal.cpp similarity index 100% rename from include/FastNoiseSIMD/FastNoiseSIMD_internal.cpp rename to deps/FastNoiseSIMD/FastNoiseSIMD_internal.cpp diff --git a/include/FastNoiseSIMD/FastNoiseSIMD_internal.h b/deps/FastNoiseSIMD/FastNoiseSIMD_internal.h similarity index 100% rename from include/FastNoiseSIMD/FastNoiseSIMD_internal.h rename to deps/FastNoiseSIMD/FastNoiseSIMD_internal.h diff --git a/include/FastNoiseSIMD/FastNoiseSIMD_neon.cpp b/deps/FastNoiseSIMD/FastNoiseSIMD_neon.cpp similarity index 100% rename from include/FastNoiseSIMD/FastNoiseSIMD_neon.cpp rename to deps/FastNoiseSIMD/FastNoiseSIMD_neon.cpp diff --git a/include/FastNoiseSIMD/FastNoiseSIMD_sse2.cpp b/deps/FastNoiseSIMD/FastNoiseSIMD_sse2.cpp similarity index 100% rename from include/FastNoiseSIMD/FastNoiseSIMD_sse2.cpp rename to deps/FastNoiseSIMD/FastNoiseSIMD_sse2.cpp diff --git a/include/FastNoiseSIMD/FastNoiseSIMD_sse41.cpp b/deps/FastNoiseSIMD/FastNoiseSIMD_sse41.cpp similarity index 100% rename from include/FastNoiseSIMD/FastNoiseSIMD_sse41.cpp rename to deps/FastNoiseSIMD/FastNoiseSIMD_sse41.cpp diff --git a/include/FastNoiseSIMD/LICENSE b/deps/FastNoiseSIMD/LICENSE similarity index 100% rename from include/FastNoiseSIMD/LICENSE rename to deps/FastNoiseSIMD/LICENSE diff --git a/include/gl3w/GL/gl3w.h b/deps/gl3w/GL/gl3w.h similarity index 100% rename from include/gl3w/GL/gl3w.h rename to deps/gl3w/GL/gl3w.h diff --git a/include/gl3w/GL/glcorearb.h b/deps/gl3w/GL/glcorearb.h similarity index 100% rename from include/gl3w/GL/glcorearb.h rename to deps/gl3w/GL/glcorearb.h diff --git a/include/gl3w/KHR/khrplatform.h b/deps/gl3w/KHR/khrplatform.h similarity index 100% rename from include/gl3w/KHR/khrplatform.h rename to deps/gl3w/KHR/khrplatform.h diff --git a/include/gl3w/UNLICENSE b/deps/gl3w/UNLICENSE similarity index 100% rename from include/gl3w/UNLICENSE rename to deps/gl3w/UNLICENSE diff --git a/include/gl3w/gl3w.c b/deps/gl3w/gl3w.c similarity index 100% rename from include/gl3w/gl3w.c rename to deps/gl3w/gl3w.c diff --git a/include/glfw/CMake/GenerateMappings.cmake b/deps/glfw/CMake/GenerateMappings.cmake similarity index 100% rename from include/glfw/CMake/GenerateMappings.cmake rename to deps/glfw/CMake/GenerateMappings.cmake diff --git a/include/glfw/CMake/MacOSXBundleInfo.plist.in b/deps/glfw/CMake/MacOSXBundleInfo.plist.in similarity index 100% rename from include/glfw/CMake/MacOSXBundleInfo.plist.in rename to deps/glfw/CMake/MacOSXBundleInfo.plist.in diff --git a/include/glfw/CMake/i686-w64-mingw32-clang.cmake b/deps/glfw/CMake/i686-w64-mingw32-clang.cmake similarity index 100% rename from include/glfw/CMake/i686-w64-mingw32-clang.cmake rename to deps/glfw/CMake/i686-w64-mingw32-clang.cmake diff --git a/include/glfw/CMake/i686-w64-mingw32.cmake b/deps/glfw/CMake/i686-w64-mingw32.cmake similarity index 100% rename from include/glfw/CMake/i686-w64-mingw32.cmake rename to deps/glfw/CMake/i686-w64-mingw32.cmake diff --git a/include/glfw/CMake/modules/FindEpollShim.cmake b/deps/glfw/CMake/modules/FindEpollShim.cmake similarity index 100% rename from include/glfw/CMake/modules/FindEpollShim.cmake rename to deps/glfw/CMake/modules/FindEpollShim.cmake diff --git a/include/glfw/CMake/modules/FindOSMesa.cmake b/deps/glfw/CMake/modules/FindOSMesa.cmake similarity index 100% rename from include/glfw/CMake/modules/FindOSMesa.cmake rename to deps/glfw/CMake/modules/FindOSMesa.cmake diff --git a/include/glfw/CMake/modules/FindWaylandProtocols.cmake b/deps/glfw/CMake/modules/FindWaylandProtocols.cmake similarity index 100% rename from include/glfw/CMake/modules/FindWaylandProtocols.cmake rename to deps/glfw/CMake/modules/FindWaylandProtocols.cmake diff --git a/include/glfw/CMake/modules/FindXKBCommon.cmake b/deps/glfw/CMake/modules/FindXKBCommon.cmake similarity index 100% rename from include/glfw/CMake/modules/FindXKBCommon.cmake rename to deps/glfw/CMake/modules/FindXKBCommon.cmake diff --git a/include/glfw/CMake/x86_64-w64-mingw32-clang.cmake b/deps/glfw/CMake/x86_64-w64-mingw32-clang.cmake similarity index 100% rename from include/glfw/CMake/x86_64-w64-mingw32-clang.cmake rename to deps/glfw/CMake/x86_64-w64-mingw32-clang.cmake diff --git a/include/glfw/CMake/x86_64-w64-mingw32.cmake b/deps/glfw/CMake/x86_64-w64-mingw32.cmake similarity index 100% rename from include/glfw/CMake/x86_64-w64-mingw32.cmake rename to deps/glfw/CMake/x86_64-w64-mingw32.cmake diff --git a/include/glfw/CMakeLists.txt b/deps/glfw/CMakeLists.txt similarity index 100% rename from include/glfw/CMakeLists.txt rename to deps/glfw/CMakeLists.txt diff --git a/include/glfw/LICENSE.md b/deps/glfw/LICENSE.md similarity index 100% rename from include/glfw/LICENSE.md rename to deps/glfw/LICENSE.md diff --git a/include/glfw/README.md b/deps/glfw/README.md similarity index 100% rename from include/glfw/README.md rename to deps/glfw/README.md diff --git a/include/glfw/cmake_uninstall.cmake.in b/deps/glfw/cmake_uninstall.cmake.in similarity index 100% rename from include/glfw/cmake_uninstall.cmake.in rename to deps/glfw/cmake_uninstall.cmake.in diff --git a/include/glfw/deps/getopt.c b/deps/glfw/deps/getopt.c similarity index 100% rename from include/glfw/deps/getopt.c rename to deps/glfw/deps/getopt.c diff --git a/include/glfw/deps/getopt.h b/deps/glfw/deps/getopt.h similarity index 100% rename from include/glfw/deps/getopt.h rename to deps/glfw/deps/getopt.h diff --git a/include/glfw/deps/glad/gl.h b/deps/glfw/deps/glad/gl.h similarity index 100% rename from include/glfw/deps/glad/gl.h rename to deps/glfw/deps/glad/gl.h diff --git a/include/glfw/deps/glad/khrplatform.h b/deps/glfw/deps/glad/khrplatform.h similarity index 100% rename from include/glfw/deps/glad/khrplatform.h rename to deps/glfw/deps/glad/khrplatform.h diff --git a/include/glfw/deps/glad/vk_platform.h b/deps/glfw/deps/glad/vk_platform.h similarity index 100% rename from include/glfw/deps/glad/vk_platform.h rename to deps/glfw/deps/glad/vk_platform.h diff --git a/include/glfw/deps/glad/vulkan.h b/deps/glfw/deps/glad/vulkan.h similarity index 100% rename from include/glfw/deps/glad/vulkan.h rename to deps/glfw/deps/glad/vulkan.h diff --git a/include/glfw/deps/glad_gl.c b/deps/glfw/deps/glad_gl.c similarity index 100% rename from include/glfw/deps/glad_gl.c rename to deps/glfw/deps/glad_gl.c diff --git a/include/glfw/deps/glad_vulkan.c b/deps/glfw/deps/glad_vulkan.c similarity index 100% rename from include/glfw/deps/glad_vulkan.c rename to deps/glfw/deps/glad_vulkan.c diff --git a/include/glfw/deps/linmath.h b/deps/glfw/deps/linmath.h similarity index 100% rename from include/glfw/deps/linmath.h rename to deps/glfw/deps/linmath.h diff --git a/include/glfw/deps/mingw/_mingw_dxhelper.h b/deps/glfw/deps/mingw/_mingw_dxhelper.h similarity index 100% rename from include/glfw/deps/mingw/_mingw_dxhelper.h rename to deps/glfw/deps/mingw/_mingw_dxhelper.h diff --git a/include/glfw/deps/mingw/dinput.h b/deps/glfw/deps/mingw/dinput.h similarity index 100% rename from include/glfw/deps/mingw/dinput.h rename to deps/glfw/deps/mingw/dinput.h diff --git a/include/glfw/deps/mingw/xinput.h b/deps/glfw/deps/mingw/xinput.h similarity index 100% rename from include/glfw/deps/mingw/xinput.h rename to deps/glfw/deps/mingw/xinput.h diff --git a/include/glfw/deps/nuklear.h b/deps/glfw/deps/nuklear.h similarity index 100% rename from include/glfw/deps/nuklear.h rename to deps/glfw/deps/nuklear.h diff --git a/include/glfw/deps/nuklear_glfw_gl2.h b/deps/glfw/deps/nuklear_glfw_gl2.h similarity index 100% rename from include/glfw/deps/nuklear_glfw_gl2.h rename to deps/glfw/deps/nuklear_glfw_gl2.h diff --git a/include/glfw/deps/stb_image_write.h b/deps/glfw/deps/stb_image_write.h similarity index 100% rename from include/glfw/deps/stb_image_write.h rename to deps/glfw/deps/stb_image_write.h diff --git a/include/glfw/deps/tinycthread.c b/deps/glfw/deps/tinycthread.c similarity index 100% rename from include/glfw/deps/tinycthread.c rename to deps/glfw/deps/tinycthread.c diff --git a/include/glfw/deps/tinycthread.h b/deps/glfw/deps/tinycthread.h similarity index 100% rename from include/glfw/deps/tinycthread.h rename to deps/glfw/deps/tinycthread.h diff --git a/include/glfw/deps/vs2008/stdint.h b/deps/glfw/deps/vs2008/stdint.h similarity index 100% rename from include/glfw/deps/vs2008/stdint.h rename to deps/glfw/deps/vs2008/stdint.h diff --git a/include/glfw/include/GLFW/glfw3.h b/deps/glfw/include/GLFW/glfw3.h similarity index 100% rename from include/glfw/include/GLFW/glfw3.h rename to deps/glfw/include/GLFW/glfw3.h diff --git a/include/glfw/include/GLFW/glfw3native.h b/deps/glfw/include/GLFW/glfw3native.h similarity index 100% rename from include/glfw/include/GLFW/glfw3native.h rename to deps/glfw/include/GLFW/glfw3native.h diff --git a/include/glfw/src/CMakeLists.txt b/deps/glfw/src/CMakeLists.txt similarity index 100% rename from include/glfw/src/CMakeLists.txt rename to deps/glfw/src/CMakeLists.txt diff --git a/include/glfw/src/cocoa_init.m b/deps/glfw/src/cocoa_init.m similarity index 100% rename from include/glfw/src/cocoa_init.m rename to deps/glfw/src/cocoa_init.m diff --git a/include/glfw/src/cocoa_joystick.h b/deps/glfw/src/cocoa_joystick.h similarity index 100% rename from include/glfw/src/cocoa_joystick.h rename to deps/glfw/src/cocoa_joystick.h diff --git a/include/glfw/src/cocoa_joystick.m b/deps/glfw/src/cocoa_joystick.m similarity index 100% rename from include/glfw/src/cocoa_joystick.m rename to deps/glfw/src/cocoa_joystick.m diff --git a/include/glfw/src/cocoa_monitor.m b/deps/glfw/src/cocoa_monitor.m similarity index 100% rename from include/glfw/src/cocoa_monitor.m rename to deps/glfw/src/cocoa_monitor.m diff --git a/include/glfw/src/cocoa_platform.h b/deps/glfw/src/cocoa_platform.h similarity index 100% rename from include/glfw/src/cocoa_platform.h rename to deps/glfw/src/cocoa_platform.h diff --git a/include/glfw/src/cocoa_time.c b/deps/glfw/src/cocoa_time.c similarity index 100% rename from include/glfw/src/cocoa_time.c rename to deps/glfw/src/cocoa_time.c diff --git a/include/glfw/src/cocoa_window.m b/deps/glfw/src/cocoa_window.m similarity index 100% rename from include/glfw/src/cocoa_window.m rename to deps/glfw/src/cocoa_window.m diff --git a/include/glfw/src/context.c b/deps/glfw/src/context.c similarity index 100% rename from include/glfw/src/context.c rename to deps/glfw/src/context.c diff --git a/include/glfw/src/egl_context.c b/deps/glfw/src/egl_context.c similarity index 100% rename from include/glfw/src/egl_context.c rename to deps/glfw/src/egl_context.c diff --git a/include/glfw/src/egl_context.h b/deps/glfw/src/egl_context.h similarity index 100% rename from include/glfw/src/egl_context.h rename to deps/glfw/src/egl_context.h diff --git a/include/glfw/src/glfw3.pc.in b/deps/glfw/src/glfw3.pc.in similarity index 100% rename from include/glfw/src/glfw3.pc.in rename to deps/glfw/src/glfw3.pc.in diff --git a/include/glfw/src/glfw3Config.cmake.in b/deps/glfw/src/glfw3Config.cmake.in similarity index 100% rename from include/glfw/src/glfw3Config.cmake.in rename to deps/glfw/src/glfw3Config.cmake.in diff --git a/include/glfw/src/glfw_config.h.in b/deps/glfw/src/glfw_config.h.in similarity index 100% rename from include/glfw/src/glfw_config.h.in rename to deps/glfw/src/glfw_config.h.in diff --git a/include/glfw/src/glx_context.c b/deps/glfw/src/glx_context.c similarity index 100% rename from include/glfw/src/glx_context.c rename to deps/glfw/src/glx_context.c diff --git a/include/glfw/src/glx_context.h b/deps/glfw/src/glx_context.h similarity index 100% rename from include/glfw/src/glx_context.h rename to deps/glfw/src/glx_context.h diff --git a/include/glfw/src/init.c b/deps/glfw/src/init.c similarity index 100% rename from include/glfw/src/init.c rename to deps/glfw/src/init.c diff --git a/include/glfw/src/input.c b/deps/glfw/src/input.c similarity index 100% rename from include/glfw/src/input.c rename to deps/glfw/src/input.c diff --git a/include/glfw/src/internal.h b/deps/glfw/src/internal.h similarity index 100% rename from include/glfw/src/internal.h rename to deps/glfw/src/internal.h diff --git a/include/glfw/src/linux_joystick.c b/deps/glfw/src/linux_joystick.c similarity index 100% rename from include/glfw/src/linux_joystick.c rename to deps/glfw/src/linux_joystick.c diff --git a/include/glfw/src/linux_joystick.h b/deps/glfw/src/linux_joystick.h similarity index 100% rename from include/glfw/src/linux_joystick.h rename to deps/glfw/src/linux_joystick.h diff --git a/include/glfw/src/mappings.h b/deps/glfw/src/mappings.h similarity index 100% rename from include/glfw/src/mappings.h rename to deps/glfw/src/mappings.h diff --git a/include/glfw/src/mappings.h.in b/deps/glfw/src/mappings.h.in similarity index 100% rename from include/glfw/src/mappings.h.in rename to deps/glfw/src/mappings.h.in diff --git a/include/glfw/src/monitor.c b/deps/glfw/src/monitor.c similarity index 100% rename from include/glfw/src/monitor.c rename to deps/glfw/src/monitor.c diff --git a/include/glfw/src/nsgl_context.h b/deps/glfw/src/nsgl_context.h similarity index 100% rename from include/glfw/src/nsgl_context.h rename to deps/glfw/src/nsgl_context.h diff --git a/include/glfw/src/nsgl_context.m b/deps/glfw/src/nsgl_context.m similarity index 100% rename from include/glfw/src/nsgl_context.m rename to deps/glfw/src/nsgl_context.m diff --git a/include/glfw/src/null_init.c b/deps/glfw/src/null_init.c similarity index 100% rename from include/glfw/src/null_init.c rename to deps/glfw/src/null_init.c diff --git a/include/glfw/src/null_joystick.c b/deps/glfw/src/null_joystick.c similarity index 100% rename from include/glfw/src/null_joystick.c rename to deps/glfw/src/null_joystick.c diff --git a/include/glfw/src/null_joystick.h b/deps/glfw/src/null_joystick.h similarity index 100% rename from include/glfw/src/null_joystick.h rename to deps/glfw/src/null_joystick.h diff --git a/include/glfw/src/null_monitor.c b/deps/glfw/src/null_monitor.c similarity index 100% rename from include/glfw/src/null_monitor.c rename to deps/glfw/src/null_monitor.c diff --git a/include/glfw/src/null_platform.h b/deps/glfw/src/null_platform.h similarity index 100% rename from include/glfw/src/null_platform.h rename to deps/glfw/src/null_platform.h diff --git a/include/glfw/src/null_window.c b/deps/glfw/src/null_window.c similarity index 100% rename from include/glfw/src/null_window.c rename to deps/glfw/src/null_window.c diff --git a/include/glfw/src/osmesa_context.c b/deps/glfw/src/osmesa_context.c similarity index 100% rename from include/glfw/src/osmesa_context.c rename to deps/glfw/src/osmesa_context.c diff --git a/include/glfw/src/osmesa_context.h b/deps/glfw/src/osmesa_context.h similarity index 100% rename from include/glfw/src/osmesa_context.h rename to deps/glfw/src/osmesa_context.h diff --git a/include/glfw/src/posix_thread.c b/deps/glfw/src/posix_thread.c similarity index 100% rename from include/glfw/src/posix_thread.c rename to deps/glfw/src/posix_thread.c diff --git a/include/glfw/src/posix_thread.h b/deps/glfw/src/posix_thread.h similarity index 100% rename from include/glfw/src/posix_thread.h rename to deps/glfw/src/posix_thread.h diff --git a/include/glfw/src/posix_time.c b/deps/glfw/src/posix_time.c similarity index 100% rename from include/glfw/src/posix_time.c rename to deps/glfw/src/posix_time.c diff --git a/include/glfw/src/posix_time.h b/deps/glfw/src/posix_time.h similarity index 100% rename from include/glfw/src/posix_time.h rename to deps/glfw/src/posix_time.h diff --git a/include/glfw/src/vulkan.c b/deps/glfw/src/vulkan.c similarity index 100% rename from include/glfw/src/vulkan.c rename to deps/glfw/src/vulkan.c diff --git a/include/glfw/src/wgl_context.c b/deps/glfw/src/wgl_context.c similarity index 100% rename from include/glfw/src/wgl_context.c rename to deps/glfw/src/wgl_context.c diff --git a/include/glfw/src/wgl_context.h b/deps/glfw/src/wgl_context.h similarity index 100% rename from include/glfw/src/wgl_context.h rename to deps/glfw/src/wgl_context.h diff --git a/include/glfw/src/win32_init.c b/deps/glfw/src/win32_init.c similarity index 100% rename from include/glfw/src/win32_init.c rename to deps/glfw/src/win32_init.c diff --git a/include/glfw/src/win32_joystick.c b/deps/glfw/src/win32_joystick.c similarity index 100% rename from include/glfw/src/win32_joystick.c rename to deps/glfw/src/win32_joystick.c diff --git a/include/glfw/src/win32_joystick.h b/deps/glfw/src/win32_joystick.h similarity index 100% rename from include/glfw/src/win32_joystick.h rename to deps/glfw/src/win32_joystick.h diff --git a/include/glfw/src/win32_monitor.c b/deps/glfw/src/win32_monitor.c similarity index 100% rename from include/glfw/src/win32_monitor.c rename to deps/glfw/src/win32_monitor.c diff --git a/include/glfw/src/win32_platform.h b/deps/glfw/src/win32_platform.h similarity index 100% rename from include/glfw/src/win32_platform.h rename to deps/glfw/src/win32_platform.h diff --git a/include/glfw/src/win32_thread.c b/deps/glfw/src/win32_thread.c similarity index 100% rename from include/glfw/src/win32_thread.c rename to deps/glfw/src/win32_thread.c diff --git a/include/glfw/src/win32_time.c b/deps/glfw/src/win32_time.c similarity index 100% rename from include/glfw/src/win32_time.c rename to deps/glfw/src/win32_time.c diff --git a/include/glfw/src/win32_window.c b/deps/glfw/src/win32_window.c similarity index 100% rename from include/glfw/src/win32_window.c rename to deps/glfw/src/win32_window.c diff --git a/include/glfw/src/window.c b/deps/glfw/src/window.c similarity index 100% rename from include/glfw/src/window.c rename to deps/glfw/src/window.c diff --git a/include/glfw/src/wl_init.c b/deps/glfw/src/wl_init.c similarity index 100% rename from include/glfw/src/wl_init.c rename to deps/glfw/src/wl_init.c diff --git a/include/glfw/src/wl_monitor.c b/deps/glfw/src/wl_monitor.c similarity index 100% rename from include/glfw/src/wl_monitor.c rename to deps/glfw/src/wl_monitor.c diff --git a/include/glfw/src/wl_platform.h b/deps/glfw/src/wl_platform.h similarity index 100% rename from include/glfw/src/wl_platform.h rename to deps/glfw/src/wl_platform.h diff --git a/include/glfw/src/wl_window.c b/deps/glfw/src/wl_window.c similarity index 100% rename from include/glfw/src/wl_window.c rename to deps/glfw/src/wl_window.c diff --git a/include/glfw/src/x11_init.c b/deps/glfw/src/x11_init.c similarity index 100% rename from include/glfw/src/x11_init.c rename to deps/glfw/src/x11_init.c diff --git a/include/glfw/src/x11_monitor.c b/deps/glfw/src/x11_monitor.c similarity index 100% rename from include/glfw/src/x11_monitor.c rename to deps/glfw/src/x11_monitor.c diff --git a/include/glfw/src/x11_platform.h b/deps/glfw/src/x11_platform.h similarity index 100% rename from include/glfw/src/x11_platform.h rename to deps/glfw/src/x11_platform.h diff --git a/include/glfw/src/x11_window.c b/deps/glfw/src/x11_window.c similarity index 100% rename from include/glfw/src/x11_window.c rename to deps/glfw/src/x11_window.c diff --git a/include/glfw/src/xkb_unicode.c b/deps/glfw/src/xkb_unicode.c similarity index 100% rename from include/glfw/src/xkb_unicode.c rename to deps/glfw/src/xkb_unicode.c diff --git a/include/glfw/src/xkb_unicode.h b/deps/glfw/src/xkb_unicode.h similarity index 100% rename from include/glfw/src/xkb_unicode.h rename to deps/glfw/src/xkb_unicode.h diff --git a/include/glm/CMakeLists.txt b/deps/glm/CMakeLists.txt similarity index 100% rename from include/glm/CMakeLists.txt rename to deps/glm/CMakeLists.txt diff --git a/include/glm/common.hpp b/deps/glm/common.hpp similarity index 100% rename from include/glm/common.hpp rename to deps/glm/common.hpp diff --git a/include/glm/copying.txt b/deps/glm/copying.txt similarity index 100% rename from include/glm/copying.txt rename to deps/glm/copying.txt diff --git a/include/glm/detail/_features.hpp b/deps/glm/detail/_features.hpp similarity index 100% rename from include/glm/detail/_features.hpp rename to deps/glm/detail/_features.hpp diff --git a/include/glm/detail/_fixes.hpp b/deps/glm/detail/_fixes.hpp similarity index 100% rename from include/glm/detail/_fixes.hpp rename to deps/glm/detail/_fixes.hpp diff --git a/include/glm/detail/_noise.hpp b/deps/glm/detail/_noise.hpp similarity index 100% rename from include/glm/detail/_noise.hpp rename to deps/glm/detail/_noise.hpp diff --git a/include/glm/detail/_swizzle.hpp b/deps/glm/detail/_swizzle.hpp similarity index 100% rename from include/glm/detail/_swizzle.hpp rename to deps/glm/detail/_swizzle.hpp diff --git a/include/glm/detail/_swizzle_func.hpp b/deps/glm/detail/_swizzle_func.hpp similarity index 100% rename from include/glm/detail/_swizzle_func.hpp rename to deps/glm/detail/_swizzle_func.hpp diff --git a/include/glm/detail/_vectorize.hpp b/deps/glm/detail/_vectorize.hpp similarity index 100% rename from include/glm/detail/_vectorize.hpp rename to deps/glm/detail/_vectorize.hpp diff --git a/include/glm/detail/compute_common.hpp b/deps/glm/detail/compute_common.hpp similarity index 100% rename from include/glm/detail/compute_common.hpp rename to deps/glm/detail/compute_common.hpp diff --git a/include/glm/detail/compute_vector_relational.hpp b/deps/glm/detail/compute_vector_relational.hpp similarity index 100% rename from include/glm/detail/compute_vector_relational.hpp rename to deps/glm/detail/compute_vector_relational.hpp diff --git a/include/glm/detail/func_common.inl b/deps/glm/detail/func_common.inl similarity index 100% rename from include/glm/detail/func_common.inl rename to deps/glm/detail/func_common.inl diff --git a/include/glm/detail/func_common_simd.inl b/deps/glm/detail/func_common_simd.inl similarity index 100% rename from include/glm/detail/func_common_simd.inl rename to deps/glm/detail/func_common_simd.inl diff --git a/include/glm/detail/func_exponential.inl b/deps/glm/detail/func_exponential.inl similarity index 100% rename from include/glm/detail/func_exponential.inl rename to deps/glm/detail/func_exponential.inl diff --git a/include/glm/detail/func_exponential_simd.inl b/deps/glm/detail/func_exponential_simd.inl similarity index 100% rename from include/glm/detail/func_exponential_simd.inl rename to deps/glm/detail/func_exponential_simd.inl diff --git a/include/glm/detail/func_geometric.inl b/deps/glm/detail/func_geometric.inl similarity index 100% rename from include/glm/detail/func_geometric.inl rename to deps/glm/detail/func_geometric.inl diff --git a/include/glm/detail/func_geometric_simd.inl b/deps/glm/detail/func_geometric_simd.inl similarity index 100% rename from include/glm/detail/func_geometric_simd.inl rename to deps/glm/detail/func_geometric_simd.inl diff --git a/include/glm/detail/func_integer.inl b/deps/glm/detail/func_integer.inl similarity index 100% rename from include/glm/detail/func_integer.inl rename to deps/glm/detail/func_integer.inl diff --git a/include/glm/detail/func_integer_simd.inl b/deps/glm/detail/func_integer_simd.inl similarity index 100% rename from include/glm/detail/func_integer_simd.inl rename to deps/glm/detail/func_integer_simd.inl diff --git a/include/glm/detail/func_matrix.inl b/deps/glm/detail/func_matrix.inl similarity index 100% rename from include/glm/detail/func_matrix.inl rename to deps/glm/detail/func_matrix.inl diff --git a/include/glm/detail/func_matrix_simd.inl b/deps/glm/detail/func_matrix_simd.inl similarity index 100% rename from include/glm/detail/func_matrix_simd.inl rename to deps/glm/detail/func_matrix_simd.inl diff --git a/include/glm/detail/func_packing.inl b/deps/glm/detail/func_packing.inl similarity index 100% rename from include/glm/detail/func_packing.inl rename to deps/glm/detail/func_packing.inl diff --git a/include/glm/detail/func_packing_simd.inl b/deps/glm/detail/func_packing_simd.inl similarity index 100% rename from include/glm/detail/func_packing_simd.inl rename to deps/glm/detail/func_packing_simd.inl diff --git a/include/glm/detail/func_trigonometric.inl b/deps/glm/detail/func_trigonometric.inl similarity index 100% rename from include/glm/detail/func_trigonometric.inl rename to deps/glm/detail/func_trigonometric.inl diff --git a/include/glm/detail/func_trigonometric_simd.inl b/deps/glm/detail/func_trigonometric_simd.inl similarity index 100% rename from include/glm/detail/func_trigonometric_simd.inl rename to deps/glm/detail/func_trigonometric_simd.inl diff --git a/include/glm/detail/func_vector_relational.inl b/deps/glm/detail/func_vector_relational.inl similarity index 100% rename from include/glm/detail/func_vector_relational.inl rename to deps/glm/detail/func_vector_relational.inl diff --git a/include/glm/detail/func_vector_relational_simd.inl b/deps/glm/detail/func_vector_relational_simd.inl similarity index 100% rename from include/glm/detail/func_vector_relational_simd.inl rename to deps/glm/detail/func_vector_relational_simd.inl diff --git a/include/glm/detail/glm.cpp b/deps/glm/detail/glm.cpp similarity index 100% rename from include/glm/detail/glm.cpp rename to deps/glm/detail/glm.cpp diff --git a/include/glm/detail/qualifier.hpp b/deps/glm/detail/qualifier.hpp similarity index 100% rename from include/glm/detail/qualifier.hpp rename to deps/glm/detail/qualifier.hpp diff --git a/include/glm/detail/setup.hpp b/deps/glm/detail/setup.hpp similarity index 100% rename from include/glm/detail/setup.hpp rename to deps/glm/detail/setup.hpp diff --git a/include/glm/detail/type_float.hpp b/deps/glm/detail/type_float.hpp similarity index 100% rename from include/glm/detail/type_float.hpp rename to deps/glm/detail/type_float.hpp diff --git a/include/glm/detail/type_half.hpp b/deps/glm/detail/type_half.hpp similarity index 100% rename from include/glm/detail/type_half.hpp rename to deps/glm/detail/type_half.hpp diff --git a/include/glm/detail/type_half.inl b/deps/glm/detail/type_half.inl similarity index 100% rename from include/glm/detail/type_half.inl rename to deps/glm/detail/type_half.inl diff --git a/include/glm/detail/type_mat2x2.hpp b/deps/glm/detail/type_mat2x2.hpp similarity index 100% rename from include/glm/detail/type_mat2x2.hpp rename to deps/glm/detail/type_mat2x2.hpp diff --git a/include/glm/detail/type_mat2x2.inl b/deps/glm/detail/type_mat2x2.inl similarity index 100% rename from include/glm/detail/type_mat2x2.inl rename to deps/glm/detail/type_mat2x2.inl diff --git a/include/glm/detail/type_mat2x3.hpp b/deps/glm/detail/type_mat2x3.hpp similarity index 100% rename from include/glm/detail/type_mat2x3.hpp rename to deps/glm/detail/type_mat2x3.hpp diff --git a/include/glm/detail/type_mat2x3.inl b/deps/glm/detail/type_mat2x3.inl similarity index 100% rename from include/glm/detail/type_mat2x3.inl rename to deps/glm/detail/type_mat2x3.inl diff --git a/include/glm/detail/type_mat2x4.hpp b/deps/glm/detail/type_mat2x4.hpp similarity index 100% rename from include/glm/detail/type_mat2x4.hpp rename to deps/glm/detail/type_mat2x4.hpp diff --git a/include/glm/detail/type_mat2x4.inl b/deps/glm/detail/type_mat2x4.inl similarity index 100% rename from include/glm/detail/type_mat2x4.inl rename to deps/glm/detail/type_mat2x4.inl diff --git a/include/glm/detail/type_mat3x2.hpp b/deps/glm/detail/type_mat3x2.hpp similarity index 100% rename from include/glm/detail/type_mat3x2.hpp rename to deps/glm/detail/type_mat3x2.hpp diff --git a/include/glm/detail/type_mat3x2.inl b/deps/glm/detail/type_mat3x2.inl similarity index 100% rename from include/glm/detail/type_mat3x2.inl rename to deps/glm/detail/type_mat3x2.inl diff --git a/include/glm/detail/type_mat3x3.hpp b/deps/glm/detail/type_mat3x3.hpp similarity index 100% rename from include/glm/detail/type_mat3x3.hpp rename to deps/glm/detail/type_mat3x3.hpp diff --git a/include/glm/detail/type_mat3x3.inl b/deps/glm/detail/type_mat3x3.inl similarity index 100% rename from include/glm/detail/type_mat3x3.inl rename to deps/glm/detail/type_mat3x3.inl diff --git a/include/glm/detail/type_mat3x4.hpp b/deps/glm/detail/type_mat3x4.hpp similarity index 100% rename from include/glm/detail/type_mat3x4.hpp rename to deps/glm/detail/type_mat3x4.hpp diff --git a/include/glm/detail/type_mat3x4.inl b/deps/glm/detail/type_mat3x4.inl similarity index 100% rename from include/glm/detail/type_mat3x4.inl rename to deps/glm/detail/type_mat3x4.inl diff --git a/include/glm/detail/type_mat4x2.hpp b/deps/glm/detail/type_mat4x2.hpp similarity index 100% rename from include/glm/detail/type_mat4x2.hpp rename to deps/glm/detail/type_mat4x2.hpp diff --git a/include/glm/detail/type_mat4x2.inl b/deps/glm/detail/type_mat4x2.inl similarity index 100% rename from include/glm/detail/type_mat4x2.inl rename to deps/glm/detail/type_mat4x2.inl diff --git a/include/glm/detail/type_mat4x3.hpp b/deps/glm/detail/type_mat4x3.hpp similarity index 100% rename from include/glm/detail/type_mat4x3.hpp rename to deps/glm/detail/type_mat4x3.hpp diff --git a/include/glm/detail/type_mat4x3.inl b/deps/glm/detail/type_mat4x3.inl similarity index 100% rename from include/glm/detail/type_mat4x3.inl rename to deps/glm/detail/type_mat4x3.inl diff --git a/include/glm/detail/type_mat4x4.hpp b/deps/glm/detail/type_mat4x4.hpp similarity index 100% rename from include/glm/detail/type_mat4x4.hpp rename to deps/glm/detail/type_mat4x4.hpp diff --git a/include/glm/detail/type_mat4x4.inl b/deps/glm/detail/type_mat4x4.inl similarity index 100% rename from include/glm/detail/type_mat4x4.inl rename to deps/glm/detail/type_mat4x4.inl diff --git a/include/glm/detail/type_mat4x4_simd.inl b/deps/glm/detail/type_mat4x4_simd.inl similarity index 100% rename from include/glm/detail/type_mat4x4_simd.inl rename to deps/glm/detail/type_mat4x4_simd.inl diff --git a/include/glm/detail/type_quat.hpp b/deps/glm/detail/type_quat.hpp similarity index 100% rename from include/glm/detail/type_quat.hpp rename to deps/glm/detail/type_quat.hpp diff --git a/include/glm/detail/type_quat.inl b/deps/glm/detail/type_quat.inl similarity index 100% rename from include/glm/detail/type_quat.inl rename to deps/glm/detail/type_quat.inl diff --git a/include/glm/detail/type_quat_simd.inl b/deps/glm/detail/type_quat_simd.inl similarity index 100% rename from include/glm/detail/type_quat_simd.inl rename to deps/glm/detail/type_quat_simd.inl diff --git a/include/glm/detail/type_vec1.hpp b/deps/glm/detail/type_vec1.hpp similarity index 100% rename from include/glm/detail/type_vec1.hpp rename to deps/glm/detail/type_vec1.hpp diff --git a/include/glm/detail/type_vec1.inl b/deps/glm/detail/type_vec1.inl similarity index 100% rename from include/glm/detail/type_vec1.inl rename to deps/glm/detail/type_vec1.inl diff --git a/include/glm/detail/type_vec2.hpp b/deps/glm/detail/type_vec2.hpp similarity index 100% rename from include/glm/detail/type_vec2.hpp rename to deps/glm/detail/type_vec2.hpp diff --git a/include/glm/detail/type_vec2.inl b/deps/glm/detail/type_vec2.inl similarity index 100% rename from include/glm/detail/type_vec2.inl rename to deps/glm/detail/type_vec2.inl diff --git a/include/glm/detail/type_vec3.hpp b/deps/glm/detail/type_vec3.hpp similarity index 100% rename from include/glm/detail/type_vec3.hpp rename to deps/glm/detail/type_vec3.hpp diff --git a/include/glm/detail/type_vec3.inl b/deps/glm/detail/type_vec3.inl similarity index 100% rename from include/glm/detail/type_vec3.inl rename to deps/glm/detail/type_vec3.inl diff --git a/include/glm/detail/type_vec4.hpp b/deps/glm/detail/type_vec4.hpp similarity index 100% rename from include/glm/detail/type_vec4.hpp rename to deps/glm/detail/type_vec4.hpp diff --git a/include/glm/detail/type_vec4.inl b/deps/glm/detail/type_vec4.inl similarity index 100% rename from include/glm/detail/type_vec4.inl rename to deps/glm/detail/type_vec4.inl diff --git a/include/glm/detail/type_vec4_simd.inl b/deps/glm/detail/type_vec4_simd.inl similarity index 100% rename from include/glm/detail/type_vec4_simd.inl rename to deps/glm/detail/type_vec4_simd.inl diff --git a/include/glm/exponential.hpp b/deps/glm/exponential.hpp similarity index 100% rename from include/glm/exponential.hpp rename to deps/glm/exponential.hpp diff --git a/include/glm/ext.hpp b/deps/glm/ext.hpp similarity index 100% rename from include/glm/ext.hpp rename to deps/glm/ext.hpp diff --git a/include/glm/ext/matrix_clip_space.hpp b/deps/glm/ext/matrix_clip_space.hpp similarity index 100% rename from include/glm/ext/matrix_clip_space.hpp rename to deps/glm/ext/matrix_clip_space.hpp diff --git a/include/glm/ext/matrix_clip_space.inl b/deps/glm/ext/matrix_clip_space.inl similarity index 100% rename from include/glm/ext/matrix_clip_space.inl rename to deps/glm/ext/matrix_clip_space.inl diff --git a/include/glm/ext/matrix_common.hpp b/deps/glm/ext/matrix_common.hpp similarity index 100% rename from include/glm/ext/matrix_common.hpp rename to deps/glm/ext/matrix_common.hpp diff --git a/include/glm/ext/matrix_common.inl b/deps/glm/ext/matrix_common.inl similarity index 100% rename from include/glm/ext/matrix_common.inl rename to deps/glm/ext/matrix_common.inl diff --git a/include/glm/ext/matrix_double2x2.hpp b/deps/glm/ext/matrix_double2x2.hpp similarity index 100% rename from include/glm/ext/matrix_double2x2.hpp rename to deps/glm/ext/matrix_double2x2.hpp diff --git a/include/glm/ext/matrix_double2x2_precision.hpp b/deps/glm/ext/matrix_double2x2_precision.hpp similarity index 100% rename from include/glm/ext/matrix_double2x2_precision.hpp rename to deps/glm/ext/matrix_double2x2_precision.hpp diff --git a/include/glm/ext/matrix_double2x3.hpp b/deps/glm/ext/matrix_double2x3.hpp similarity index 100% rename from include/glm/ext/matrix_double2x3.hpp rename to deps/glm/ext/matrix_double2x3.hpp diff --git a/include/glm/ext/matrix_double2x3_precision.hpp b/deps/glm/ext/matrix_double2x3_precision.hpp similarity index 100% rename from include/glm/ext/matrix_double2x3_precision.hpp rename to deps/glm/ext/matrix_double2x3_precision.hpp diff --git a/include/glm/ext/matrix_double2x4.hpp b/deps/glm/ext/matrix_double2x4.hpp similarity index 100% rename from include/glm/ext/matrix_double2x4.hpp rename to deps/glm/ext/matrix_double2x4.hpp diff --git a/include/glm/ext/matrix_double2x4_precision.hpp b/deps/glm/ext/matrix_double2x4_precision.hpp similarity index 100% rename from include/glm/ext/matrix_double2x4_precision.hpp rename to deps/glm/ext/matrix_double2x4_precision.hpp diff --git a/include/glm/ext/matrix_double3x2.hpp b/deps/glm/ext/matrix_double3x2.hpp similarity index 100% rename from include/glm/ext/matrix_double3x2.hpp rename to deps/glm/ext/matrix_double3x2.hpp diff --git a/include/glm/ext/matrix_double3x2_precision.hpp b/deps/glm/ext/matrix_double3x2_precision.hpp similarity index 100% rename from include/glm/ext/matrix_double3x2_precision.hpp rename to deps/glm/ext/matrix_double3x2_precision.hpp diff --git a/include/glm/ext/matrix_double3x3.hpp b/deps/glm/ext/matrix_double3x3.hpp similarity index 100% rename from include/glm/ext/matrix_double3x3.hpp rename to deps/glm/ext/matrix_double3x3.hpp diff --git a/include/glm/ext/matrix_double3x3_precision.hpp b/deps/glm/ext/matrix_double3x3_precision.hpp similarity index 100% rename from include/glm/ext/matrix_double3x3_precision.hpp rename to deps/glm/ext/matrix_double3x3_precision.hpp diff --git a/include/glm/ext/matrix_double3x4.hpp b/deps/glm/ext/matrix_double3x4.hpp similarity index 100% rename from include/glm/ext/matrix_double3x4.hpp rename to deps/glm/ext/matrix_double3x4.hpp diff --git a/include/glm/ext/matrix_double3x4_precision.hpp b/deps/glm/ext/matrix_double3x4_precision.hpp similarity index 100% rename from include/glm/ext/matrix_double3x4_precision.hpp rename to deps/glm/ext/matrix_double3x4_precision.hpp diff --git a/include/glm/ext/matrix_double4x2.hpp b/deps/glm/ext/matrix_double4x2.hpp similarity index 100% rename from include/glm/ext/matrix_double4x2.hpp rename to deps/glm/ext/matrix_double4x2.hpp diff --git a/include/glm/ext/matrix_double4x2_precision.hpp b/deps/glm/ext/matrix_double4x2_precision.hpp similarity index 100% rename from include/glm/ext/matrix_double4x2_precision.hpp rename to deps/glm/ext/matrix_double4x2_precision.hpp diff --git a/include/glm/ext/matrix_double4x3.hpp b/deps/glm/ext/matrix_double4x3.hpp similarity index 100% rename from include/glm/ext/matrix_double4x3.hpp rename to deps/glm/ext/matrix_double4x3.hpp diff --git a/include/glm/ext/matrix_double4x3_precision.hpp b/deps/glm/ext/matrix_double4x3_precision.hpp similarity index 100% rename from include/glm/ext/matrix_double4x3_precision.hpp rename to deps/glm/ext/matrix_double4x3_precision.hpp diff --git a/include/glm/ext/matrix_double4x4.hpp b/deps/glm/ext/matrix_double4x4.hpp similarity index 100% rename from include/glm/ext/matrix_double4x4.hpp rename to deps/glm/ext/matrix_double4x4.hpp diff --git a/include/glm/ext/matrix_double4x4_precision.hpp b/deps/glm/ext/matrix_double4x4_precision.hpp similarity index 100% rename from include/glm/ext/matrix_double4x4_precision.hpp rename to deps/glm/ext/matrix_double4x4_precision.hpp diff --git a/include/glm/ext/matrix_float2x2.hpp b/deps/glm/ext/matrix_float2x2.hpp similarity index 100% rename from include/glm/ext/matrix_float2x2.hpp rename to deps/glm/ext/matrix_float2x2.hpp diff --git a/include/glm/ext/matrix_float2x2_precision.hpp b/deps/glm/ext/matrix_float2x2_precision.hpp similarity index 100% rename from include/glm/ext/matrix_float2x2_precision.hpp rename to deps/glm/ext/matrix_float2x2_precision.hpp diff --git a/include/glm/ext/matrix_float2x3.hpp b/deps/glm/ext/matrix_float2x3.hpp similarity index 100% rename from include/glm/ext/matrix_float2x3.hpp rename to deps/glm/ext/matrix_float2x3.hpp diff --git a/include/glm/ext/matrix_float2x3_precision.hpp b/deps/glm/ext/matrix_float2x3_precision.hpp similarity index 100% rename from include/glm/ext/matrix_float2x3_precision.hpp rename to deps/glm/ext/matrix_float2x3_precision.hpp diff --git a/include/glm/ext/matrix_float2x4.hpp b/deps/glm/ext/matrix_float2x4.hpp similarity index 100% rename from include/glm/ext/matrix_float2x4.hpp rename to deps/glm/ext/matrix_float2x4.hpp diff --git a/include/glm/ext/matrix_float2x4_precision.hpp b/deps/glm/ext/matrix_float2x4_precision.hpp similarity index 100% rename from include/glm/ext/matrix_float2x4_precision.hpp rename to deps/glm/ext/matrix_float2x4_precision.hpp diff --git a/include/glm/ext/matrix_float3x2.hpp b/deps/glm/ext/matrix_float3x2.hpp similarity index 100% rename from include/glm/ext/matrix_float3x2.hpp rename to deps/glm/ext/matrix_float3x2.hpp diff --git a/include/glm/ext/matrix_float3x2_precision.hpp b/deps/glm/ext/matrix_float3x2_precision.hpp similarity index 100% rename from include/glm/ext/matrix_float3x2_precision.hpp rename to deps/glm/ext/matrix_float3x2_precision.hpp diff --git a/include/glm/ext/matrix_float3x3.hpp b/deps/glm/ext/matrix_float3x3.hpp similarity index 100% rename from include/glm/ext/matrix_float3x3.hpp rename to deps/glm/ext/matrix_float3x3.hpp diff --git a/include/glm/ext/matrix_float3x3_precision.hpp b/deps/glm/ext/matrix_float3x3_precision.hpp similarity index 100% rename from include/glm/ext/matrix_float3x3_precision.hpp rename to deps/glm/ext/matrix_float3x3_precision.hpp diff --git a/include/glm/ext/matrix_float3x4.hpp b/deps/glm/ext/matrix_float3x4.hpp similarity index 100% rename from include/glm/ext/matrix_float3x4.hpp rename to deps/glm/ext/matrix_float3x4.hpp diff --git a/include/glm/ext/matrix_float3x4_precision.hpp b/deps/glm/ext/matrix_float3x4_precision.hpp similarity index 100% rename from include/glm/ext/matrix_float3x4_precision.hpp rename to deps/glm/ext/matrix_float3x4_precision.hpp diff --git a/include/glm/ext/matrix_float4x2.hpp b/deps/glm/ext/matrix_float4x2.hpp similarity index 100% rename from include/glm/ext/matrix_float4x2.hpp rename to deps/glm/ext/matrix_float4x2.hpp diff --git a/include/glm/ext/matrix_float4x2_precision.hpp b/deps/glm/ext/matrix_float4x2_precision.hpp similarity index 100% rename from include/glm/ext/matrix_float4x2_precision.hpp rename to deps/glm/ext/matrix_float4x2_precision.hpp diff --git a/include/glm/ext/matrix_float4x3.hpp b/deps/glm/ext/matrix_float4x3.hpp similarity index 100% rename from include/glm/ext/matrix_float4x3.hpp rename to deps/glm/ext/matrix_float4x3.hpp diff --git a/include/glm/ext/matrix_float4x3_precision.hpp b/deps/glm/ext/matrix_float4x3_precision.hpp similarity index 100% rename from include/glm/ext/matrix_float4x3_precision.hpp rename to deps/glm/ext/matrix_float4x3_precision.hpp diff --git a/include/glm/ext/matrix_float4x4.hpp b/deps/glm/ext/matrix_float4x4.hpp similarity index 100% rename from include/glm/ext/matrix_float4x4.hpp rename to deps/glm/ext/matrix_float4x4.hpp diff --git a/include/glm/ext/matrix_float4x4_precision.hpp b/deps/glm/ext/matrix_float4x4_precision.hpp similarity index 100% rename from include/glm/ext/matrix_float4x4_precision.hpp rename to deps/glm/ext/matrix_float4x4_precision.hpp diff --git a/include/glm/ext/matrix_int2x2.hpp b/deps/glm/ext/matrix_int2x2.hpp similarity index 100% rename from include/glm/ext/matrix_int2x2.hpp rename to deps/glm/ext/matrix_int2x2.hpp diff --git a/include/glm/ext/matrix_int2x2_sized.hpp b/deps/glm/ext/matrix_int2x2_sized.hpp similarity index 100% rename from include/glm/ext/matrix_int2x2_sized.hpp rename to deps/glm/ext/matrix_int2x2_sized.hpp diff --git a/include/glm/ext/matrix_int2x3.hpp b/deps/glm/ext/matrix_int2x3.hpp similarity index 100% rename from include/glm/ext/matrix_int2x3.hpp rename to deps/glm/ext/matrix_int2x3.hpp diff --git a/include/glm/ext/matrix_int2x3_sized.hpp b/deps/glm/ext/matrix_int2x3_sized.hpp similarity index 100% rename from include/glm/ext/matrix_int2x3_sized.hpp rename to deps/glm/ext/matrix_int2x3_sized.hpp diff --git a/include/glm/ext/matrix_int2x4.hpp b/deps/glm/ext/matrix_int2x4.hpp similarity index 100% rename from include/glm/ext/matrix_int2x4.hpp rename to deps/glm/ext/matrix_int2x4.hpp diff --git a/include/glm/ext/matrix_int2x4_sized.hpp b/deps/glm/ext/matrix_int2x4_sized.hpp similarity index 100% rename from include/glm/ext/matrix_int2x4_sized.hpp rename to deps/glm/ext/matrix_int2x4_sized.hpp diff --git a/include/glm/ext/matrix_int3x2.hpp b/deps/glm/ext/matrix_int3x2.hpp similarity index 100% rename from include/glm/ext/matrix_int3x2.hpp rename to deps/glm/ext/matrix_int3x2.hpp diff --git a/include/glm/ext/matrix_int3x2_sized.hpp b/deps/glm/ext/matrix_int3x2_sized.hpp similarity index 100% rename from include/glm/ext/matrix_int3x2_sized.hpp rename to deps/glm/ext/matrix_int3x2_sized.hpp diff --git a/include/glm/ext/matrix_int3x3.hpp b/deps/glm/ext/matrix_int3x3.hpp similarity index 100% rename from include/glm/ext/matrix_int3x3.hpp rename to deps/glm/ext/matrix_int3x3.hpp diff --git a/include/glm/ext/matrix_int3x3_sized.hpp b/deps/glm/ext/matrix_int3x3_sized.hpp similarity index 100% rename from include/glm/ext/matrix_int3x3_sized.hpp rename to deps/glm/ext/matrix_int3x3_sized.hpp diff --git a/include/glm/ext/matrix_int3x4.hpp b/deps/glm/ext/matrix_int3x4.hpp similarity index 100% rename from include/glm/ext/matrix_int3x4.hpp rename to deps/glm/ext/matrix_int3x4.hpp diff --git a/include/glm/ext/matrix_int3x4_sized.hpp b/deps/glm/ext/matrix_int3x4_sized.hpp similarity index 100% rename from include/glm/ext/matrix_int3x4_sized.hpp rename to deps/glm/ext/matrix_int3x4_sized.hpp diff --git a/include/glm/ext/matrix_int4x2.hpp b/deps/glm/ext/matrix_int4x2.hpp similarity index 100% rename from include/glm/ext/matrix_int4x2.hpp rename to deps/glm/ext/matrix_int4x2.hpp diff --git a/include/glm/ext/matrix_int4x2_sized.hpp b/deps/glm/ext/matrix_int4x2_sized.hpp similarity index 100% rename from include/glm/ext/matrix_int4x2_sized.hpp rename to deps/glm/ext/matrix_int4x2_sized.hpp diff --git a/include/glm/ext/matrix_int4x3.hpp b/deps/glm/ext/matrix_int4x3.hpp similarity index 100% rename from include/glm/ext/matrix_int4x3.hpp rename to deps/glm/ext/matrix_int4x3.hpp diff --git a/include/glm/ext/matrix_int4x3_sized.hpp b/deps/glm/ext/matrix_int4x3_sized.hpp similarity index 100% rename from include/glm/ext/matrix_int4x3_sized.hpp rename to deps/glm/ext/matrix_int4x3_sized.hpp diff --git a/include/glm/ext/matrix_int4x4.hpp b/deps/glm/ext/matrix_int4x4.hpp similarity index 100% rename from include/glm/ext/matrix_int4x4.hpp rename to deps/glm/ext/matrix_int4x4.hpp diff --git a/include/glm/ext/matrix_int4x4_sized.hpp b/deps/glm/ext/matrix_int4x4_sized.hpp similarity index 100% rename from include/glm/ext/matrix_int4x4_sized.hpp rename to deps/glm/ext/matrix_int4x4_sized.hpp diff --git a/include/glm/ext/matrix_projection.hpp b/deps/glm/ext/matrix_projection.hpp similarity index 100% rename from include/glm/ext/matrix_projection.hpp rename to deps/glm/ext/matrix_projection.hpp diff --git a/include/glm/ext/matrix_projection.inl b/deps/glm/ext/matrix_projection.inl similarity index 100% rename from include/glm/ext/matrix_projection.inl rename to deps/glm/ext/matrix_projection.inl diff --git a/include/glm/ext/matrix_relational.hpp b/deps/glm/ext/matrix_relational.hpp similarity index 100% rename from include/glm/ext/matrix_relational.hpp rename to deps/glm/ext/matrix_relational.hpp diff --git a/include/glm/ext/matrix_relational.inl b/deps/glm/ext/matrix_relational.inl similarity index 100% rename from include/glm/ext/matrix_relational.inl rename to deps/glm/ext/matrix_relational.inl diff --git a/include/glm/ext/matrix_transform.hpp b/deps/glm/ext/matrix_transform.hpp similarity index 100% rename from include/glm/ext/matrix_transform.hpp rename to deps/glm/ext/matrix_transform.hpp diff --git a/include/glm/ext/matrix_transform.inl b/deps/glm/ext/matrix_transform.inl similarity index 100% rename from include/glm/ext/matrix_transform.inl rename to deps/glm/ext/matrix_transform.inl diff --git a/include/glm/ext/matrix_uint2x2.hpp b/deps/glm/ext/matrix_uint2x2.hpp similarity index 100% rename from include/glm/ext/matrix_uint2x2.hpp rename to deps/glm/ext/matrix_uint2x2.hpp diff --git a/include/glm/ext/matrix_uint2x2_sized.hpp b/deps/glm/ext/matrix_uint2x2_sized.hpp similarity index 100% rename from include/glm/ext/matrix_uint2x2_sized.hpp rename to deps/glm/ext/matrix_uint2x2_sized.hpp diff --git a/include/glm/ext/matrix_uint2x3.hpp b/deps/glm/ext/matrix_uint2x3.hpp similarity index 100% rename from include/glm/ext/matrix_uint2x3.hpp rename to deps/glm/ext/matrix_uint2x3.hpp diff --git a/include/glm/ext/matrix_uint2x3_sized.hpp b/deps/glm/ext/matrix_uint2x3_sized.hpp similarity index 100% rename from include/glm/ext/matrix_uint2x3_sized.hpp rename to deps/glm/ext/matrix_uint2x3_sized.hpp diff --git a/include/glm/ext/matrix_uint2x4.hpp b/deps/glm/ext/matrix_uint2x4.hpp similarity index 100% rename from include/glm/ext/matrix_uint2x4.hpp rename to deps/glm/ext/matrix_uint2x4.hpp diff --git a/include/glm/ext/matrix_uint2x4_sized.hpp b/deps/glm/ext/matrix_uint2x4_sized.hpp similarity index 100% rename from include/glm/ext/matrix_uint2x4_sized.hpp rename to deps/glm/ext/matrix_uint2x4_sized.hpp diff --git a/include/glm/ext/matrix_uint3x2.hpp b/deps/glm/ext/matrix_uint3x2.hpp similarity index 100% rename from include/glm/ext/matrix_uint3x2.hpp rename to deps/glm/ext/matrix_uint3x2.hpp diff --git a/include/glm/ext/matrix_uint3x2_sized.hpp b/deps/glm/ext/matrix_uint3x2_sized.hpp similarity index 100% rename from include/glm/ext/matrix_uint3x2_sized.hpp rename to deps/glm/ext/matrix_uint3x2_sized.hpp diff --git a/include/glm/ext/matrix_uint3x3.hpp b/deps/glm/ext/matrix_uint3x3.hpp similarity index 100% rename from include/glm/ext/matrix_uint3x3.hpp rename to deps/glm/ext/matrix_uint3x3.hpp diff --git a/include/glm/ext/matrix_uint3x3_sized.hpp b/deps/glm/ext/matrix_uint3x3_sized.hpp similarity index 100% rename from include/glm/ext/matrix_uint3x3_sized.hpp rename to deps/glm/ext/matrix_uint3x3_sized.hpp diff --git a/include/glm/ext/matrix_uint3x4.hpp b/deps/glm/ext/matrix_uint3x4.hpp similarity index 100% rename from include/glm/ext/matrix_uint3x4.hpp rename to deps/glm/ext/matrix_uint3x4.hpp diff --git a/include/glm/ext/matrix_uint3x4_sized.hpp b/deps/glm/ext/matrix_uint3x4_sized.hpp similarity index 100% rename from include/glm/ext/matrix_uint3x4_sized.hpp rename to deps/glm/ext/matrix_uint3x4_sized.hpp diff --git a/include/glm/ext/matrix_uint4x2.hpp b/deps/glm/ext/matrix_uint4x2.hpp similarity index 100% rename from include/glm/ext/matrix_uint4x2.hpp rename to deps/glm/ext/matrix_uint4x2.hpp diff --git a/include/glm/ext/matrix_uint4x2_sized.hpp b/deps/glm/ext/matrix_uint4x2_sized.hpp similarity index 100% rename from include/glm/ext/matrix_uint4x2_sized.hpp rename to deps/glm/ext/matrix_uint4x2_sized.hpp diff --git a/include/glm/ext/matrix_uint4x3.hpp b/deps/glm/ext/matrix_uint4x3.hpp similarity index 100% rename from include/glm/ext/matrix_uint4x3.hpp rename to deps/glm/ext/matrix_uint4x3.hpp diff --git a/include/glm/ext/matrix_uint4x3_sized.hpp b/deps/glm/ext/matrix_uint4x3_sized.hpp similarity index 100% rename from include/glm/ext/matrix_uint4x3_sized.hpp rename to deps/glm/ext/matrix_uint4x3_sized.hpp diff --git a/include/glm/ext/matrix_uint4x4.hpp b/deps/glm/ext/matrix_uint4x4.hpp similarity index 100% rename from include/glm/ext/matrix_uint4x4.hpp rename to deps/glm/ext/matrix_uint4x4.hpp diff --git a/include/glm/ext/matrix_uint4x4_sized.hpp b/deps/glm/ext/matrix_uint4x4_sized.hpp similarity index 100% rename from include/glm/ext/matrix_uint4x4_sized.hpp rename to deps/glm/ext/matrix_uint4x4_sized.hpp diff --git a/include/glm/ext/quaternion_common.hpp b/deps/glm/ext/quaternion_common.hpp similarity index 100% rename from include/glm/ext/quaternion_common.hpp rename to deps/glm/ext/quaternion_common.hpp diff --git a/include/glm/ext/quaternion_common.inl b/deps/glm/ext/quaternion_common.inl similarity index 100% rename from include/glm/ext/quaternion_common.inl rename to deps/glm/ext/quaternion_common.inl diff --git a/include/glm/ext/quaternion_common_simd.inl b/deps/glm/ext/quaternion_common_simd.inl similarity index 100% rename from include/glm/ext/quaternion_common_simd.inl rename to deps/glm/ext/quaternion_common_simd.inl diff --git a/include/glm/ext/quaternion_double.hpp b/deps/glm/ext/quaternion_double.hpp similarity index 100% rename from include/glm/ext/quaternion_double.hpp rename to deps/glm/ext/quaternion_double.hpp diff --git a/include/glm/ext/quaternion_double_precision.hpp b/deps/glm/ext/quaternion_double_precision.hpp similarity index 100% rename from include/glm/ext/quaternion_double_precision.hpp rename to deps/glm/ext/quaternion_double_precision.hpp diff --git a/include/glm/ext/quaternion_exponential.hpp b/deps/glm/ext/quaternion_exponential.hpp similarity index 100% rename from include/glm/ext/quaternion_exponential.hpp rename to deps/glm/ext/quaternion_exponential.hpp diff --git a/include/glm/ext/quaternion_exponential.inl b/deps/glm/ext/quaternion_exponential.inl similarity index 100% rename from include/glm/ext/quaternion_exponential.inl rename to deps/glm/ext/quaternion_exponential.inl diff --git a/include/glm/ext/quaternion_float.hpp b/deps/glm/ext/quaternion_float.hpp similarity index 100% rename from include/glm/ext/quaternion_float.hpp rename to deps/glm/ext/quaternion_float.hpp diff --git a/include/glm/ext/quaternion_float_precision.hpp b/deps/glm/ext/quaternion_float_precision.hpp similarity index 100% rename from include/glm/ext/quaternion_float_precision.hpp rename to deps/glm/ext/quaternion_float_precision.hpp diff --git a/include/glm/ext/quaternion_geometric.hpp b/deps/glm/ext/quaternion_geometric.hpp similarity index 100% rename from include/glm/ext/quaternion_geometric.hpp rename to deps/glm/ext/quaternion_geometric.hpp diff --git a/include/glm/ext/quaternion_geometric.inl b/deps/glm/ext/quaternion_geometric.inl similarity index 100% rename from include/glm/ext/quaternion_geometric.inl rename to deps/glm/ext/quaternion_geometric.inl diff --git a/include/glm/ext/quaternion_relational.hpp b/deps/glm/ext/quaternion_relational.hpp similarity index 100% rename from include/glm/ext/quaternion_relational.hpp rename to deps/glm/ext/quaternion_relational.hpp diff --git a/include/glm/ext/quaternion_relational.inl b/deps/glm/ext/quaternion_relational.inl similarity index 100% rename from include/glm/ext/quaternion_relational.inl rename to deps/glm/ext/quaternion_relational.inl diff --git a/include/glm/ext/quaternion_transform.hpp b/deps/glm/ext/quaternion_transform.hpp similarity index 100% rename from include/glm/ext/quaternion_transform.hpp rename to deps/glm/ext/quaternion_transform.hpp diff --git a/include/glm/ext/quaternion_transform.inl b/deps/glm/ext/quaternion_transform.inl similarity index 100% rename from include/glm/ext/quaternion_transform.inl rename to deps/glm/ext/quaternion_transform.inl diff --git a/include/glm/ext/quaternion_trigonometric.hpp b/deps/glm/ext/quaternion_trigonometric.hpp similarity index 100% rename from include/glm/ext/quaternion_trigonometric.hpp rename to deps/glm/ext/quaternion_trigonometric.hpp diff --git a/include/glm/ext/quaternion_trigonometric.inl b/deps/glm/ext/quaternion_trigonometric.inl similarity index 100% rename from include/glm/ext/quaternion_trigonometric.inl rename to deps/glm/ext/quaternion_trigonometric.inl diff --git a/include/glm/ext/scalar_common.hpp b/deps/glm/ext/scalar_common.hpp similarity index 100% rename from include/glm/ext/scalar_common.hpp rename to deps/glm/ext/scalar_common.hpp diff --git a/include/glm/ext/scalar_common.inl b/deps/glm/ext/scalar_common.inl similarity index 100% rename from include/glm/ext/scalar_common.inl rename to deps/glm/ext/scalar_common.inl diff --git a/include/glm/ext/scalar_constants.hpp b/deps/glm/ext/scalar_constants.hpp similarity index 100% rename from include/glm/ext/scalar_constants.hpp rename to deps/glm/ext/scalar_constants.hpp diff --git a/include/glm/ext/scalar_constants.inl b/deps/glm/ext/scalar_constants.inl similarity index 100% rename from include/glm/ext/scalar_constants.inl rename to deps/glm/ext/scalar_constants.inl diff --git a/include/glm/ext/scalar_int_sized.hpp b/deps/glm/ext/scalar_int_sized.hpp similarity index 100% rename from include/glm/ext/scalar_int_sized.hpp rename to deps/glm/ext/scalar_int_sized.hpp diff --git a/include/glm/ext/scalar_integer.hpp b/deps/glm/ext/scalar_integer.hpp similarity index 100% rename from include/glm/ext/scalar_integer.hpp rename to deps/glm/ext/scalar_integer.hpp diff --git a/include/glm/ext/scalar_integer.inl b/deps/glm/ext/scalar_integer.inl similarity index 100% rename from include/glm/ext/scalar_integer.inl rename to deps/glm/ext/scalar_integer.inl diff --git a/include/glm/ext/scalar_packing.hpp b/deps/glm/ext/scalar_packing.hpp similarity index 100% rename from include/glm/ext/scalar_packing.hpp rename to deps/glm/ext/scalar_packing.hpp diff --git a/include/glm/ext/scalar_packing.inl b/deps/glm/ext/scalar_packing.inl similarity index 100% rename from include/glm/ext/scalar_packing.inl rename to deps/glm/ext/scalar_packing.inl diff --git a/include/glm/ext/scalar_relational.hpp b/deps/glm/ext/scalar_relational.hpp similarity index 100% rename from include/glm/ext/scalar_relational.hpp rename to deps/glm/ext/scalar_relational.hpp diff --git a/include/glm/ext/scalar_relational.inl b/deps/glm/ext/scalar_relational.inl similarity index 100% rename from include/glm/ext/scalar_relational.inl rename to deps/glm/ext/scalar_relational.inl diff --git a/include/glm/ext/scalar_uint_sized.hpp b/deps/glm/ext/scalar_uint_sized.hpp similarity index 100% rename from include/glm/ext/scalar_uint_sized.hpp rename to deps/glm/ext/scalar_uint_sized.hpp diff --git a/include/glm/ext/scalar_ulp.hpp b/deps/glm/ext/scalar_ulp.hpp similarity index 100% rename from include/glm/ext/scalar_ulp.hpp rename to deps/glm/ext/scalar_ulp.hpp diff --git a/include/glm/ext/scalar_ulp.inl b/deps/glm/ext/scalar_ulp.inl similarity index 100% rename from include/glm/ext/scalar_ulp.inl rename to deps/glm/ext/scalar_ulp.inl diff --git a/include/glm/ext/vector_bool1.hpp b/deps/glm/ext/vector_bool1.hpp similarity index 100% rename from include/glm/ext/vector_bool1.hpp rename to deps/glm/ext/vector_bool1.hpp diff --git a/include/glm/ext/vector_bool1_precision.hpp b/deps/glm/ext/vector_bool1_precision.hpp similarity index 100% rename from include/glm/ext/vector_bool1_precision.hpp rename to deps/glm/ext/vector_bool1_precision.hpp diff --git a/include/glm/ext/vector_bool2.hpp b/deps/glm/ext/vector_bool2.hpp similarity index 100% rename from include/glm/ext/vector_bool2.hpp rename to deps/glm/ext/vector_bool2.hpp diff --git a/include/glm/ext/vector_bool2_precision.hpp b/deps/glm/ext/vector_bool2_precision.hpp similarity index 100% rename from include/glm/ext/vector_bool2_precision.hpp rename to deps/glm/ext/vector_bool2_precision.hpp diff --git a/include/glm/ext/vector_bool3.hpp b/deps/glm/ext/vector_bool3.hpp similarity index 100% rename from include/glm/ext/vector_bool3.hpp rename to deps/glm/ext/vector_bool3.hpp diff --git a/include/glm/ext/vector_bool3_precision.hpp b/deps/glm/ext/vector_bool3_precision.hpp similarity index 100% rename from include/glm/ext/vector_bool3_precision.hpp rename to deps/glm/ext/vector_bool3_precision.hpp diff --git a/include/glm/ext/vector_bool4.hpp b/deps/glm/ext/vector_bool4.hpp similarity index 100% rename from include/glm/ext/vector_bool4.hpp rename to deps/glm/ext/vector_bool4.hpp diff --git a/include/glm/ext/vector_bool4_precision.hpp b/deps/glm/ext/vector_bool4_precision.hpp similarity index 100% rename from include/glm/ext/vector_bool4_precision.hpp rename to deps/glm/ext/vector_bool4_precision.hpp diff --git a/include/glm/ext/vector_common.hpp b/deps/glm/ext/vector_common.hpp similarity index 100% rename from include/glm/ext/vector_common.hpp rename to deps/glm/ext/vector_common.hpp diff --git a/include/glm/ext/vector_common.inl b/deps/glm/ext/vector_common.inl similarity index 100% rename from include/glm/ext/vector_common.inl rename to deps/glm/ext/vector_common.inl diff --git a/include/glm/ext/vector_double1.hpp b/deps/glm/ext/vector_double1.hpp similarity index 100% rename from include/glm/ext/vector_double1.hpp rename to deps/glm/ext/vector_double1.hpp diff --git a/include/glm/ext/vector_double1_precision.hpp b/deps/glm/ext/vector_double1_precision.hpp similarity index 100% rename from include/glm/ext/vector_double1_precision.hpp rename to deps/glm/ext/vector_double1_precision.hpp diff --git a/include/glm/ext/vector_double2.hpp b/deps/glm/ext/vector_double2.hpp similarity index 100% rename from include/glm/ext/vector_double2.hpp rename to deps/glm/ext/vector_double2.hpp diff --git a/include/glm/ext/vector_double2_precision.hpp b/deps/glm/ext/vector_double2_precision.hpp similarity index 100% rename from include/glm/ext/vector_double2_precision.hpp rename to deps/glm/ext/vector_double2_precision.hpp diff --git a/include/glm/ext/vector_double3.hpp b/deps/glm/ext/vector_double3.hpp similarity index 100% rename from include/glm/ext/vector_double3.hpp rename to deps/glm/ext/vector_double3.hpp diff --git a/include/glm/ext/vector_double3_precision.hpp b/deps/glm/ext/vector_double3_precision.hpp similarity index 100% rename from include/glm/ext/vector_double3_precision.hpp rename to deps/glm/ext/vector_double3_precision.hpp diff --git a/include/glm/ext/vector_double4.hpp b/deps/glm/ext/vector_double4.hpp similarity index 100% rename from include/glm/ext/vector_double4.hpp rename to deps/glm/ext/vector_double4.hpp diff --git a/include/glm/ext/vector_double4_precision.hpp b/deps/glm/ext/vector_double4_precision.hpp similarity index 100% rename from include/glm/ext/vector_double4_precision.hpp rename to deps/glm/ext/vector_double4_precision.hpp diff --git a/include/glm/ext/vector_float1.hpp b/deps/glm/ext/vector_float1.hpp similarity index 100% rename from include/glm/ext/vector_float1.hpp rename to deps/glm/ext/vector_float1.hpp diff --git a/include/glm/ext/vector_float1_precision.hpp b/deps/glm/ext/vector_float1_precision.hpp similarity index 100% rename from include/glm/ext/vector_float1_precision.hpp rename to deps/glm/ext/vector_float1_precision.hpp diff --git a/include/glm/ext/vector_float2.hpp b/deps/glm/ext/vector_float2.hpp similarity index 100% rename from include/glm/ext/vector_float2.hpp rename to deps/glm/ext/vector_float2.hpp diff --git a/include/glm/ext/vector_float2_precision.hpp b/deps/glm/ext/vector_float2_precision.hpp similarity index 100% rename from include/glm/ext/vector_float2_precision.hpp rename to deps/glm/ext/vector_float2_precision.hpp diff --git a/include/glm/ext/vector_float3.hpp b/deps/glm/ext/vector_float3.hpp similarity index 100% rename from include/glm/ext/vector_float3.hpp rename to deps/glm/ext/vector_float3.hpp diff --git a/include/glm/ext/vector_float3_precision.hpp b/deps/glm/ext/vector_float3_precision.hpp similarity index 100% rename from include/glm/ext/vector_float3_precision.hpp rename to deps/glm/ext/vector_float3_precision.hpp diff --git a/include/glm/ext/vector_float4.hpp b/deps/glm/ext/vector_float4.hpp similarity index 100% rename from include/glm/ext/vector_float4.hpp rename to deps/glm/ext/vector_float4.hpp diff --git a/include/glm/ext/vector_float4_precision.hpp b/deps/glm/ext/vector_float4_precision.hpp similarity index 100% rename from include/glm/ext/vector_float4_precision.hpp rename to deps/glm/ext/vector_float4_precision.hpp diff --git a/include/glm/ext/vector_int1.hpp b/deps/glm/ext/vector_int1.hpp similarity index 100% rename from include/glm/ext/vector_int1.hpp rename to deps/glm/ext/vector_int1.hpp diff --git a/include/glm/ext/vector_int1_sized.hpp b/deps/glm/ext/vector_int1_sized.hpp similarity index 100% rename from include/glm/ext/vector_int1_sized.hpp rename to deps/glm/ext/vector_int1_sized.hpp diff --git a/include/glm/ext/vector_int2.hpp b/deps/glm/ext/vector_int2.hpp similarity index 100% rename from include/glm/ext/vector_int2.hpp rename to deps/glm/ext/vector_int2.hpp diff --git a/include/glm/ext/vector_int2_sized.hpp b/deps/glm/ext/vector_int2_sized.hpp similarity index 100% rename from include/glm/ext/vector_int2_sized.hpp rename to deps/glm/ext/vector_int2_sized.hpp diff --git a/include/glm/ext/vector_int3.hpp b/deps/glm/ext/vector_int3.hpp similarity index 100% rename from include/glm/ext/vector_int3.hpp rename to deps/glm/ext/vector_int3.hpp diff --git a/include/glm/ext/vector_int3_sized.hpp b/deps/glm/ext/vector_int3_sized.hpp similarity index 100% rename from include/glm/ext/vector_int3_sized.hpp rename to deps/glm/ext/vector_int3_sized.hpp diff --git a/include/glm/ext/vector_int4.hpp b/deps/glm/ext/vector_int4.hpp similarity index 100% rename from include/glm/ext/vector_int4.hpp rename to deps/glm/ext/vector_int4.hpp diff --git a/include/glm/ext/vector_int4_sized.hpp b/deps/glm/ext/vector_int4_sized.hpp similarity index 100% rename from include/glm/ext/vector_int4_sized.hpp rename to deps/glm/ext/vector_int4_sized.hpp diff --git a/include/glm/ext/vector_integer.hpp b/deps/glm/ext/vector_integer.hpp similarity index 100% rename from include/glm/ext/vector_integer.hpp rename to deps/glm/ext/vector_integer.hpp diff --git a/include/glm/ext/vector_integer.inl b/deps/glm/ext/vector_integer.inl similarity index 100% rename from include/glm/ext/vector_integer.inl rename to deps/glm/ext/vector_integer.inl diff --git a/include/glm/ext/vector_packing.hpp b/deps/glm/ext/vector_packing.hpp similarity index 100% rename from include/glm/ext/vector_packing.hpp rename to deps/glm/ext/vector_packing.hpp diff --git a/include/glm/ext/vector_packing.inl b/deps/glm/ext/vector_packing.inl similarity index 100% rename from include/glm/ext/vector_packing.inl rename to deps/glm/ext/vector_packing.inl diff --git a/include/glm/ext/vector_relational.hpp b/deps/glm/ext/vector_relational.hpp similarity index 100% rename from include/glm/ext/vector_relational.hpp rename to deps/glm/ext/vector_relational.hpp diff --git a/include/glm/ext/vector_relational.inl b/deps/glm/ext/vector_relational.inl similarity index 100% rename from include/glm/ext/vector_relational.inl rename to deps/glm/ext/vector_relational.inl diff --git a/include/glm/ext/vector_uint1.hpp b/deps/glm/ext/vector_uint1.hpp similarity index 100% rename from include/glm/ext/vector_uint1.hpp rename to deps/glm/ext/vector_uint1.hpp diff --git a/include/glm/ext/vector_uint1_sized.hpp b/deps/glm/ext/vector_uint1_sized.hpp similarity index 100% rename from include/glm/ext/vector_uint1_sized.hpp rename to deps/glm/ext/vector_uint1_sized.hpp diff --git a/include/glm/ext/vector_uint2.hpp b/deps/glm/ext/vector_uint2.hpp similarity index 100% rename from include/glm/ext/vector_uint2.hpp rename to deps/glm/ext/vector_uint2.hpp diff --git a/include/glm/ext/vector_uint2_sized.hpp b/deps/glm/ext/vector_uint2_sized.hpp similarity index 100% rename from include/glm/ext/vector_uint2_sized.hpp rename to deps/glm/ext/vector_uint2_sized.hpp diff --git a/include/glm/ext/vector_uint3.hpp b/deps/glm/ext/vector_uint3.hpp similarity index 100% rename from include/glm/ext/vector_uint3.hpp rename to deps/glm/ext/vector_uint3.hpp diff --git a/include/glm/ext/vector_uint3_sized.hpp b/deps/glm/ext/vector_uint3_sized.hpp similarity index 100% rename from include/glm/ext/vector_uint3_sized.hpp rename to deps/glm/ext/vector_uint3_sized.hpp diff --git a/include/glm/ext/vector_uint4.hpp b/deps/glm/ext/vector_uint4.hpp similarity index 100% rename from include/glm/ext/vector_uint4.hpp rename to deps/glm/ext/vector_uint4.hpp diff --git a/include/glm/ext/vector_uint4_sized.hpp b/deps/glm/ext/vector_uint4_sized.hpp similarity index 100% rename from include/glm/ext/vector_uint4_sized.hpp rename to deps/glm/ext/vector_uint4_sized.hpp diff --git a/include/glm/ext/vector_ulp.hpp b/deps/glm/ext/vector_ulp.hpp similarity index 100% rename from include/glm/ext/vector_ulp.hpp rename to deps/glm/ext/vector_ulp.hpp diff --git a/include/glm/ext/vector_ulp.inl b/deps/glm/ext/vector_ulp.inl similarity index 100% rename from include/glm/ext/vector_ulp.inl rename to deps/glm/ext/vector_ulp.inl diff --git a/include/glm/fwd.hpp b/deps/glm/fwd.hpp similarity index 100% rename from include/glm/fwd.hpp rename to deps/glm/fwd.hpp diff --git a/include/glm/geometric.hpp b/deps/glm/geometric.hpp similarity index 100% rename from include/glm/geometric.hpp rename to deps/glm/geometric.hpp diff --git a/include/glm/glm.hpp b/deps/glm/glm.hpp similarity index 100% rename from include/glm/glm.hpp rename to deps/glm/glm.hpp diff --git a/include/glm/gtc/bitfield.hpp b/deps/glm/gtc/bitfield.hpp similarity index 100% rename from include/glm/gtc/bitfield.hpp rename to deps/glm/gtc/bitfield.hpp diff --git a/include/glm/gtc/bitfield.inl b/deps/glm/gtc/bitfield.inl similarity index 100% rename from include/glm/gtc/bitfield.inl rename to deps/glm/gtc/bitfield.inl diff --git a/include/glm/gtc/color_space.hpp b/deps/glm/gtc/color_space.hpp similarity index 100% rename from include/glm/gtc/color_space.hpp rename to deps/glm/gtc/color_space.hpp diff --git a/include/glm/gtc/color_space.inl b/deps/glm/gtc/color_space.inl similarity index 100% rename from include/glm/gtc/color_space.inl rename to deps/glm/gtc/color_space.inl diff --git a/include/glm/gtc/constants.hpp b/deps/glm/gtc/constants.hpp similarity index 100% rename from include/glm/gtc/constants.hpp rename to deps/glm/gtc/constants.hpp diff --git a/include/glm/gtc/constants.inl b/deps/glm/gtc/constants.inl similarity index 100% rename from include/glm/gtc/constants.inl rename to deps/glm/gtc/constants.inl diff --git a/include/glm/gtc/epsilon.hpp b/deps/glm/gtc/epsilon.hpp similarity index 100% rename from include/glm/gtc/epsilon.hpp rename to deps/glm/gtc/epsilon.hpp diff --git a/include/glm/gtc/epsilon.inl b/deps/glm/gtc/epsilon.inl similarity index 100% rename from include/glm/gtc/epsilon.inl rename to deps/glm/gtc/epsilon.inl diff --git a/include/glm/gtc/integer.hpp b/deps/glm/gtc/integer.hpp similarity index 100% rename from include/glm/gtc/integer.hpp rename to deps/glm/gtc/integer.hpp diff --git a/include/glm/gtc/integer.inl b/deps/glm/gtc/integer.inl similarity index 100% rename from include/glm/gtc/integer.inl rename to deps/glm/gtc/integer.inl diff --git a/include/glm/gtc/matrix_access.hpp b/deps/glm/gtc/matrix_access.hpp similarity index 100% rename from include/glm/gtc/matrix_access.hpp rename to deps/glm/gtc/matrix_access.hpp diff --git a/include/glm/gtc/matrix_access.inl b/deps/glm/gtc/matrix_access.inl similarity index 100% rename from include/glm/gtc/matrix_access.inl rename to deps/glm/gtc/matrix_access.inl diff --git a/include/glm/gtc/matrix_integer.hpp b/deps/glm/gtc/matrix_integer.hpp similarity index 100% rename from include/glm/gtc/matrix_integer.hpp rename to deps/glm/gtc/matrix_integer.hpp diff --git a/include/glm/gtc/matrix_inverse.hpp b/deps/glm/gtc/matrix_inverse.hpp similarity index 100% rename from include/glm/gtc/matrix_inverse.hpp rename to deps/glm/gtc/matrix_inverse.hpp diff --git a/include/glm/gtc/matrix_inverse.inl b/deps/glm/gtc/matrix_inverse.inl similarity index 100% rename from include/glm/gtc/matrix_inverse.inl rename to deps/glm/gtc/matrix_inverse.inl diff --git a/include/glm/gtc/matrix_transform.hpp b/deps/glm/gtc/matrix_transform.hpp similarity index 100% rename from include/glm/gtc/matrix_transform.hpp rename to deps/glm/gtc/matrix_transform.hpp diff --git a/include/glm/gtc/matrix_transform.inl b/deps/glm/gtc/matrix_transform.inl similarity index 100% rename from include/glm/gtc/matrix_transform.inl rename to deps/glm/gtc/matrix_transform.inl diff --git a/include/glm/gtc/noise.hpp b/deps/glm/gtc/noise.hpp similarity index 100% rename from include/glm/gtc/noise.hpp rename to deps/glm/gtc/noise.hpp diff --git a/include/glm/gtc/noise.inl b/deps/glm/gtc/noise.inl similarity index 100% rename from include/glm/gtc/noise.inl rename to deps/glm/gtc/noise.inl diff --git a/include/glm/gtc/packing.hpp b/deps/glm/gtc/packing.hpp similarity index 100% rename from include/glm/gtc/packing.hpp rename to deps/glm/gtc/packing.hpp diff --git a/include/glm/gtc/packing.inl b/deps/glm/gtc/packing.inl similarity index 100% rename from include/glm/gtc/packing.inl rename to deps/glm/gtc/packing.inl diff --git a/include/glm/gtc/quaternion.hpp b/deps/glm/gtc/quaternion.hpp similarity index 100% rename from include/glm/gtc/quaternion.hpp rename to deps/glm/gtc/quaternion.hpp diff --git a/include/glm/gtc/quaternion.inl b/deps/glm/gtc/quaternion.inl similarity index 100% rename from include/glm/gtc/quaternion.inl rename to deps/glm/gtc/quaternion.inl diff --git a/include/glm/gtc/quaternion_simd.inl b/deps/glm/gtc/quaternion_simd.inl similarity index 100% rename from include/glm/gtc/quaternion_simd.inl rename to deps/glm/gtc/quaternion_simd.inl diff --git a/include/glm/gtc/random.hpp b/deps/glm/gtc/random.hpp similarity index 100% rename from include/glm/gtc/random.hpp rename to deps/glm/gtc/random.hpp diff --git a/include/glm/gtc/random.inl b/deps/glm/gtc/random.inl similarity index 100% rename from include/glm/gtc/random.inl rename to deps/glm/gtc/random.inl diff --git a/include/glm/gtc/reciprocal.hpp b/deps/glm/gtc/reciprocal.hpp similarity index 100% rename from include/glm/gtc/reciprocal.hpp rename to deps/glm/gtc/reciprocal.hpp diff --git a/include/glm/gtc/reciprocal.inl b/deps/glm/gtc/reciprocal.inl similarity index 100% rename from include/glm/gtc/reciprocal.inl rename to deps/glm/gtc/reciprocal.inl diff --git a/include/glm/gtc/round.hpp b/deps/glm/gtc/round.hpp similarity index 100% rename from include/glm/gtc/round.hpp rename to deps/glm/gtc/round.hpp diff --git a/include/glm/gtc/round.inl b/deps/glm/gtc/round.inl similarity index 100% rename from include/glm/gtc/round.inl rename to deps/glm/gtc/round.inl diff --git a/include/glm/gtc/type_aligned.hpp b/deps/glm/gtc/type_aligned.hpp similarity index 100% rename from include/glm/gtc/type_aligned.hpp rename to deps/glm/gtc/type_aligned.hpp diff --git a/include/glm/gtc/type_precision.hpp b/deps/glm/gtc/type_precision.hpp similarity index 100% rename from include/glm/gtc/type_precision.hpp rename to deps/glm/gtc/type_precision.hpp diff --git a/include/glm/gtc/type_precision.inl b/deps/glm/gtc/type_precision.inl similarity index 100% rename from include/glm/gtc/type_precision.inl rename to deps/glm/gtc/type_precision.inl diff --git a/include/glm/gtc/type_ptr.hpp b/deps/glm/gtc/type_ptr.hpp similarity index 100% rename from include/glm/gtc/type_ptr.hpp rename to deps/glm/gtc/type_ptr.hpp diff --git a/include/glm/gtc/type_ptr.inl b/deps/glm/gtc/type_ptr.inl similarity index 100% rename from include/glm/gtc/type_ptr.inl rename to deps/glm/gtc/type_ptr.inl diff --git a/include/glm/gtc/ulp.hpp b/deps/glm/gtc/ulp.hpp similarity index 100% rename from include/glm/gtc/ulp.hpp rename to deps/glm/gtc/ulp.hpp diff --git a/include/glm/gtc/ulp.inl b/deps/glm/gtc/ulp.inl similarity index 100% rename from include/glm/gtc/ulp.inl rename to deps/glm/gtc/ulp.inl diff --git a/include/glm/gtc/vec1.hpp b/deps/glm/gtc/vec1.hpp similarity index 100% rename from include/glm/gtc/vec1.hpp rename to deps/glm/gtc/vec1.hpp diff --git a/include/glm/gtx/associated_min_max.hpp b/deps/glm/gtx/associated_min_max.hpp similarity index 100% rename from include/glm/gtx/associated_min_max.hpp rename to deps/glm/gtx/associated_min_max.hpp diff --git a/include/glm/gtx/associated_min_max.inl b/deps/glm/gtx/associated_min_max.inl similarity index 100% rename from include/glm/gtx/associated_min_max.inl rename to deps/glm/gtx/associated_min_max.inl diff --git a/include/glm/gtx/bit.hpp b/deps/glm/gtx/bit.hpp similarity index 100% rename from include/glm/gtx/bit.hpp rename to deps/glm/gtx/bit.hpp diff --git a/include/glm/gtx/bit.inl b/deps/glm/gtx/bit.inl similarity index 100% rename from include/glm/gtx/bit.inl rename to deps/glm/gtx/bit.inl diff --git a/include/glm/gtx/closest_point.hpp b/deps/glm/gtx/closest_point.hpp similarity index 100% rename from include/glm/gtx/closest_point.hpp rename to deps/glm/gtx/closest_point.hpp diff --git a/include/glm/gtx/closest_point.inl b/deps/glm/gtx/closest_point.inl similarity index 100% rename from include/glm/gtx/closest_point.inl rename to deps/glm/gtx/closest_point.inl diff --git a/include/glm/gtx/color_encoding.hpp b/deps/glm/gtx/color_encoding.hpp similarity index 100% rename from include/glm/gtx/color_encoding.hpp rename to deps/glm/gtx/color_encoding.hpp diff --git a/include/glm/gtx/color_encoding.inl b/deps/glm/gtx/color_encoding.inl similarity index 100% rename from include/glm/gtx/color_encoding.inl rename to deps/glm/gtx/color_encoding.inl diff --git a/include/glm/gtx/color_space.hpp b/deps/glm/gtx/color_space.hpp similarity index 100% rename from include/glm/gtx/color_space.hpp rename to deps/glm/gtx/color_space.hpp diff --git a/include/glm/gtx/color_space.inl b/deps/glm/gtx/color_space.inl similarity index 100% rename from include/glm/gtx/color_space.inl rename to deps/glm/gtx/color_space.inl diff --git a/include/glm/gtx/color_space_YCoCg.hpp b/deps/glm/gtx/color_space_YCoCg.hpp similarity index 100% rename from include/glm/gtx/color_space_YCoCg.hpp rename to deps/glm/gtx/color_space_YCoCg.hpp diff --git a/include/glm/gtx/color_space_YCoCg.inl b/deps/glm/gtx/color_space_YCoCg.inl similarity index 100% rename from include/glm/gtx/color_space_YCoCg.inl rename to deps/glm/gtx/color_space_YCoCg.inl diff --git a/include/glm/gtx/common.hpp b/deps/glm/gtx/common.hpp similarity index 100% rename from include/glm/gtx/common.hpp rename to deps/glm/gtx/common.hpp diff --git a/include/glm/gtx/common.inl b/deps/glm/gtx/common.inl similarity index 100% rename from include/glm/gtx/common.inl rename to deps/glm/gtx/common.inl diff --git a/include/glm/gtx/compatibility.hpp b/deps/glm/gtx/compatibility.hpp similarity index 100% rename from include/glm/gtx/compatibility.hpp rename to deps/glm/gtx/compatibility.hpp diff --git a/include/glm/gtx/compatibility.inl b/deps/glm/gtx/compatibility.inl similarity index 100% rename from include/glm/gtx/compatibility.inl rename to deps/glm/gtx/compatibility.inl diff --git a/include/glm/gtx/component_wise.hpp b/deps/glm/gtx/component_wise.hpp similarity index 100% rename from include/glm/gtx/component_wise.hpp rename to deps/glm/gtx/component_wise.hpp diff --git a/include/glm/gtx/component_wise.inl b/deps/glm/gtx/component_wise.inl similarity index 100% rename from include/glm/gtx/component_wise.inl rename to deps/glm/gtx/component_wise.inl diff --git a/include/glm/gtx/dual_quaternion.hpp b/deps/glm/gtx/dual_quaternion.hpp similarity index 100% rename from include/glm/gtx/dual_quaternion.hpp rename to deps/glm/gtx/dual_quaternion.hpp diff --git a/include/glm/gtx/dual_quaternion.inl b/deps/glm/gtx/dual_quaternion.inl similarity index 100% rename from include/glm/gtx/dual_quaternion.inl rename to deps/glm/gtx/dual_quaternion.inl diff --git a/include/glm/gtx/easing.hpp b/deps/glm/gtx/easing.hpp similarity index 100% rename from include/glm/gtx/easing.hpp rename to deps/glm/gtx/easing.hpp diff --git a/include/glm/gtx/easing.inl b/deps/glm/gtx/easing.inl similarity index 100% rename from include/glm/gtx/easing.inl rename to deps/glm/gtx/easing.inl diff --git a/include/glm/gtx/euler_angles.hpp b/deps/glm/gtx/euler_angles.hpp similarity index 100% rename from include/glm/gtx/euler_angles.hpp rename to deps/glm/gtx/euler_angles.hpp diff --git a/include/glm/gtx/euler_angles.inl b/deps/glm/gtx/euler_angles.inl similarity index 100% rename from include/glm/gtx/euler_angles.inl rename to deps/glm/gtx/euler_angles.inl diff --git a/include/glm/gtx/extend.hpp b/deps/glm/gtx/extend.hpp similarity index 100% rename from include/glm/gtx/extend.hpp rename to deps/glm/gtx/extend.hpp diff --git a/include/glm/gtx/extend.inl b/deps/glm/gtx/extend.inl similarity index 100% rename from include/glm/gtx/extend.inl rename to deps/glm/gtx/extend.inl diff --git a/include/glm/gtx/extended_min_max.hpp b/deps/glm/gtx/extended_min_max.hpp similarity index 100% rename from include/glm/gtx/extended_min_max.hpp rename to deps/glm/gtx/extended_min_max.hpp diff --git a/include/glm/gtx/extended_min_max.inl b/deps/glm/gtx/extended_min_max.inl similarity index 100% rename from include/glm/gtx/extended_min_max.inl rename to deps/glm/gtx/extended_min_max.inl diff --git a/include/glm/gtx/exterior_product.hpp b/deps/glm/gtx/exterior_product.hpp similarity index 100% rename from include/glm/gtx/exterior_product.hpp rename to deps/glm/gtx/exterior_product.hpp diff --git a/include/glm/gtx/exterior_product.inl b/deps/glm/gtx/exterior_product.inl similarity index 100% rename from include/glm/gtx/exterior_product.inl rename to deps/glm/gtx/exterior_product.inl diff --git a/include/glm/gtx/fast_exponential.hpp b/deps/glm/gtx/fast_exponential.hpp similarity index 100% rename from include/glm/gtx/fast_exponential.hpp rename to deps/glm/gtx/fast_exponential.hpp diff --git a/include/glm/gtx/fast_exponential.inl b/deps/glm/gtx/fast_exponential.inl similarity index 100% rename from include/glm/gtx/fast_exponential.inl rename to deps/glm/gtx/fast_exponential.inl diff --git a/include/glm/gtx/fast_square_root.hpp b/deps/glm/gtx/fast_square_root.hpp similarity index 100% rename from include/glm/gtx/fast_square_root.hpp rename to deps/glm/gtx/fast_square_root.hpp diff --git a/include/glm/gtx/fast_square_root.inl b/deps/glm/gtx/fast_square_root.inl similarity index 100% rename from include/glm/gtx/fast_square_root.inl rename to deps/glm/gtx/fast_square_root.inl diff --git a/include/glm/gtx/fast_trigonometry.hpp b/deps/glm/gtx/fast_trigonometry.hpp similarity index 100% rename from include/glm/gtx/fast_trigonometry.hpp rename to deps/glm/gtx/fast_trigonometry.hpp diff --git a/include/glm/gtx/fast_trigonometry.inl b/deps/glm/gtx/fast_trigonometry.inl similarity index 100% rename from include/glm/gtx/fast_trigonometry.inl rename to deps/glm/gtx/fast_trigonometry.inl diff --git a/include/glm/gtx/float_notmalize.inl b/deps/glm/gtx/float_notmalize.inl similarity index 100% rename from include/glm/gtx/float_notmalize.inl rename to deps/glm/gtx/float_notmalize.inl diff --git a/include/glm/gtx/functions.hpp b/deps/glm/gtx/functions.hpp similarity index 100% rename from include/glm/gtx/functions.hpp rename to deps/glm/gtx/functions.hpp diff --git a/include/glm/gtx/functions.inl b/deps/glm/gtx/functions.inl similarity index 100% rename from include/glm/gtx/functions.inl rename to deps/glm/gtx/functions.inl diff --git a/include/glm/gtx/gradient_paint.hpp b/deps/glm/gtx/gradient_paint.hpp similarity index 100% rename from include/glm/gtx/gradient_paint.hpp rename to deps/glm/gtx/gradient_paint.hpp diff --git a/include/glm/gtx/gradient_paint.inl b/deps/glm/gtx/gradient_paint.inl similarity index 100% rename from include/glm/gtx/gradient_paint.inl rename to deps/glm/gtx/gradient_paint.inl diff --git a/include/glm/gtx/handed_coordinate_space.hpp b/deps/glm/gtx/handed_coordinate_space.hpp similarity index 100% rename from include/glm/gtx/handed_coordinate_space.hpp rename to deps/glm/gtx/handed_coordinate_space.hpp diff --git a/include/glm/gtx/handed_coordinate_space.inl b/deps/glm/gtx/handed_coordinate_space.inl similarity index 100% rename from include/glm/gtx/handed_coordinate_space.inl rename to deps/glm/gtx/handed_coordinate_space.inl diff --git a/include/glm/gtx/hash.hpp b/deps/glm/gtx/hash.hpp similarity index 100% rename from include/glm/gtx/hash.hpp rename to deps/glm/gtx/hash.hpp diff --git a/include/glm/gtx/hash.inl b/deps/glm/gtx/hash.inl similarity index 100% rename from include/glm/gtx/hash.inl rename to deps/glm/gtx/hash.inl diff --git a/include/glm/gtx/integer.hpp b/deps/glm/gtx/integer.hpp similarity index 100% rename from include/glm/gtx/integer.hpp rename to deps/glm/gtx/integer.hpp diff --git a/include/glm/gtx/integer.inl b/deps/glm/gtx/integer.inl similarity index 100% rename from include/glm/gtx/integer.inl rename to deps/glm/gtx/integer.inl diff --git a/include/glm/gtx/intersect.hpp b/deps/glm/gtx/intersect.hpp similarity index 100% rename from include/glm/gtx/intersect.hpp rename to deps/glm/gtx/intersect.hpp diff --git a/include/glm/gtx/intersect.inl b/deps/glm/gtx/intersect.inl similarity index 100% rename from include/glm/gtx/intersect.inl rename to deps/glm/gtx/intersect.inl diff --git a/include/glm/gtx/io.hpp b/deps/glm/gtx/io.hpp similarity index 100% rename from include/glm/gtx/io.hpp rename to deps/glm/gtx/io.hpp diff --git a/include/glm/gtx/io.inl b/deps/glm/gtx/io.inl similarity index 100% rename from include/glm/gtx/io.inl rename to deps/glm/gtx/io.inl diff --git a/include/glm/gtx/log_base.hpp b/deps/glm/gtx/log_base.hpp similarity index 100% rename from include/glm/gtx/log_base.hpp rename to deps/glm/gtx/log_base.hpp diff --git a/include/glm/gtx/log_base.inl b/deps/glm/gtx/log_base.inl similarity index 100% rename from include/glm/gtx/log_base.inl rename to deps/glm/gtx/log_base.inl diff --git a/include/glm/gtx/matrix_cross_product.hpp b/deps/glm/gtx/matrix_cross_product.hpp similarity index 100% rename from include/glm/gtx/matrix_cross_product.hpp rename to deps/glm/gtx/matrix_cross_product.hpp diff --git a/include/glm/gtx/matrix_cross_product.inl b/deps/glm/gtx/matrix_cross_product.inl similarity index 100% rename from include/glm/gtx/matrix_cross_product.inl rename to deps/glm/gtx/matrix_cross_product.inl diff --git a/include/glm/gtx/matrix_decompose.hpp b/deps/glm/gtx/matrix_decompose.hpp similarity index 100% rename from include/glm/gtx/matrix_decompose.hpp rename to deps/glm/gtx/matrix_decompose.hpp diff --git a/include/glm/gtx/matrix_decompose.inl b/deps/glm/gtx/matrix_decompose.inl similarity index 100% rename from include/glm/gtx/matrix_decompose.inl rename to deps/glm/gtx/matrix_decompose.inl diff --git a/include/glm/gtx/matrix_factorisation.hpp b/deps/glm/gtx/matrix_factorisation.hpp similarity index 100% rename from include/glm/gtx/matrix_factorisation.hpp rename to deps/glm/gtx/matrix_factorisation.hpp diff --git a/include/glm/gtx/matrix_factorisation.inl b/deps/glm/gtx/matrix_factorisation.inl similarity index 100% rename from include/glm/gtx/matrix_factorisation.inl rename to deps/glm/gtx/matrix_factorisation.inl diff --git a/include/glm/gtx/matrix_interpolation.hpp b/deps/glm/gtx/matrix_interpolation.hpp similarity index 100% rename from include/glm/gtx/matrix_interpolation.hpp rename to deps/glm/gtx/matrix_interpolation.hpp diff --git a/include/glm/gtx/matrix_interpolation.inl b/deps/glm/gtx/matrix_interpolation.inl similarity index 100% rename from include/glm/gtx/matrix_interpolation.inl rename to deps/glm/gtx/matrix_interpolation.inl diff --git a/include/glm/gtx/matrix_major_storage.hpp b/deps/glm/gtx/matrix_major_storage.hpp similarity index 100% rename from include/glm/gtx/matrix_major_storage.hpp rename to deps/glm/gtx/matrix_major_storage.hpp diff --git a/include/glm/gtx/matrix_major_storage.inl b/deps/glm/gtx/matrix_major_storage.inl similarity index 100% rename from include/glm/gtx/matrix_major_storage.inl rename to deps/glm/gtx/matrix_major_storage.inl diff --git a/include/glm/gtx/matrix_operation.hpp b/deps/glm/gtx/matrix_operation.hpp similarity index 100% rename from include/glm/gtx/matrix_operation.hpp rename to deps/glm/gtx/matrix_operation.hpp diff --git a/include/glm/gtx/matrix_operation.inl b/deps/glm/gtx/matrix_operation.inl similarity index 100% rename from include/glm/gtx/matrix_operation.inl rename to deps/glm/gtx/matrix_operation.inl diff --git a/include/glm/gtx/matrix_query.hpp b/deps/glm/gtx/matrix_query.hpp similarity index 100% rename from include/glm/gtx/matrix_query.hpp rename to deps/glm/gtx/matrix_query.hpp diff --git a/include/glm/gtx/matrix_query.inl b/deps/glm/gtx/matrix_query.inl similarity index 100% rename from include/glm/gtx/matrix_query.inl rename to deps/glm/gtx/matrix_query.inl diff --git a/include/glm/gtx/matrix_transform_2d.hpp b/deps/glm/gtx/matrix_transform_2d.hpp similarity index 100% rename from include/glm/gtx/matrix_transform_2d.hpp rename to deps/glm/gtx/matrix_transform_2d.hpp diff --git a/include/glm/gtx/matrix_transform_2d.inl b/deps/glm/gtx/matrix_transform_2d.inl similarity index 100% rename from include/glm/gtx/matrix_transform_2d.inl rename to deps/glm/gtx/matrix_transform_2d.inl diff --git a/include/glm/gtx/mixed_product.hpp b/deps/glm/gtx/mixed_product.hpp similarity index 100% rename from include/glm/gtx/mixed_product.hpp rename to deps/glm/gtx/mixed_product.hpp diff --git a/include/glm/gtx/mixed_product.inl b/deps/glm/gtx/mixed_product.inl similarity index 100% rename from include/glm/gtx/mixed_product.inl rename to deps/glm/gtx/mixed_product.inl diff --git a/include/glm/gtx/norm.hpp b/deps/glm/gtx/norm.hpp similarity index 100% rename from include/glm/gtx/norm.hpp rename to deps/glm/gtx/norm.hpp diff --git a/include/glm/gtx/norm.inl b/deps/glm/gtx/norm.inl similarity index 100% rename from include/glm/gtx/norm.inl rename to deps/glm/gtx/norm.inl diff --git a/include/glm/gtx/normal.hpp b/deps/glm/gtx/normal.hpp similarity index 100% rename from include/glm/gtx/normal.hpp rename to deps/glm/gtx/normal.hpp diff --git a/include/glm/gtx/normal.inl b/deps/glm/gtx/normal.inl similarity index 100% rename from include/glm/gtx/normal.inl rename to deps/glm/gtx/normal.inl diff --git a/include/glm/gtx/normalize_dot.hpp b/deps/glm/gtx/normalize_dot.hpp similarity index 100% rename from include/glm/gtx/normalize_dot.hpp rename to deps/glm/gtx/normalize_dot.hpp diff --git a/include/glm/gtx/normalize_dot.inl b/deps/glm/gtx/normalize_dot.inl similarity index 100% rename from include/glm/gtx/normalize_dot.inl rename to deps/glm/gtx/normalize_dot.inl diff --git a/include/glm/gtx/number_precision.hpp b/deps/glm/gtx/number_precision.hpp similarity index 100% rename from include/glm/gtx/number_precision.hpp rename to deps/glm/gtx/number_precision.hpp diff --git a/include/glm/gtx/number_precision.inl b/deps/glm/gtx/number_precision.inl similarity index 100% rename from include/glm/gtx/number_precision.inl rename to deps/glm/gtx/number_precision.inl diff --git a/include/glm/gtx/optimum_pow.hpp b/deps/glm/gtx/optimum_pow.hpp similarity index 100% rename from include/glm/gtx/optimum_pow.hpp rename to deps/glm/gtx/optimum_pow.hpp diff --git a/include/glm/gtx/optimum_pow.inl b/deps/glm/gtx/optimum_pow.inl similarity index 100% rename from include/glm/gtx/optimum_pow.inl rename to deps/glm/gtx/optimum_pow.inl diff --git a/include/glm/gtx/orthonormalize.hpp b/deps/glm/gtx/orthonormalize.hpp similarity index 100% rename from include/glm/gtx/orthonormalize.hpp rename to deps/glm/gtx/orthonormalize.hpp diff --git a/include/glm/gtx/orthonormalize.inl b/deps/glm/gtx/orthonormalize.inl similarity index 100% rename from include/glm/gtx/orthonormalize.inl rename to deps/glm/gtx/orthonormalize.inl diff --git a/include/glm/gtx/perpendicular.hpp b/deps/glm/gtx/perpendicular.hpp similarity index 100% rename from include/glm/gtx/perpendicular.hpp rename to deps/glm/gtx/perpendicular.hpp diff --git a/include/glm/gtx/perpendicular.inl b/deps/glm/gtx/perpendicular.inl similarity index 100% rename from include/glm/gtx/perpendicular.inl rename to deps/glm/gtx/perpendicular.inl diff --git a/include/glm/gtx/polar_coordinates.hpp b/deps/glm/gtx/polar_coordinates.hpp similarity index 100% rename from include/glm/gtx/polar_coordinates.hpp rename to deps/glm/gtx/polar_coordinates.hpp diff --git a/include/glm/gtx/polar_coordinates.inl b/deps/glm/gtx/polar_coordinates.inl similarity index 100% rename from include/glm/gtx/polar_coordinates.inl rename to deps/glm/gtx/polar_coordinates.inl diff --git a/include/glm/gtx/projection.hpp b/deps/glm/gtx/projection.hpp similarity index 100% rename from include/glm/gtx/projection.hpp rename to deps/glm/gtx/projection.hpp diff --git a/include/glm/gtx/projection.inl b/deps/glm/gtx/projection.inl similarity index 100% rename from include/glm/gtx/projection.inl rename to deps/glm/gtx/projection.inl diff --git a/include/glm/gtx/quaternion.hpp b/deps/glm/gtx/quaternion.hpp similarity index 100% rename from include/glm/gtx/quaternion.hpp rename to deps/glm/gtx/quaternion.hpp diff --git a/include/glm/gtx/quaternion.inl b/deps/glm/gtx/quaternion.inl similarity index 100% rename from include/glm/gtx/quaternion.inl rename to deps/glm/gtx/quaternion.inl diff --git a/include/glm/gtx/range.hpp b/deps/glm/gtx/range.hpp similarity index 100% rename from include/glm/gtx/range.hpp rename to deps/glm/gtx/range.hpp diff --git a/include/glm/gtx/raw_data.hpp b/deps/glm/gtx/raw_data.hpp similarity index 100% rename from include/glm/gtx/raw_data.hpp rename to deps/glm/gtx/raw_data.hpp diff --git a/include/glm/gtx/raw_data.inl b/deps/glm/gtx/raw_data.inl similarity index 100% rename from include/glm/gtx/raw_data.inl rename to deps/glm/gtx/raw_data.inl diff --git a/include/glm/gtx/rotate_normalized_axis.hpp b/deps/glm/gtx/rotate_normalized_axis.hpp similarity index 100% rename from include/glm/gtx/rotate_normalized_axis.hpp rename to deps/glm/gtx/rotate_normalized_axis.hpp diff --git a/include/glm/gtx/rotate_normalized_axis.inl b/deps/glm/gtx/rotate_normalized_axis.inl similarity index 100% rename from include/glm/gtx/rotate_normalized_axis.inl rename to deps/glm/gtx/rotate_normalized_axis.inl diff --git a/include/glm/gtx/rotate_vector.hpp b/deps/glm/gtx/rotate_vector.hpp similarity index 100% rename from include/glm/gtx/rotate_vector.hpp rename to deps/glm/gtx/rotate_vector.hpp diff --git a/include/glm/gtx/rotate_vector.inl b/deps/glm/gtx/rotate_vector.inl similarity index 100% rename from include/glm/gtx/rotate_vector.inl rename to deps/glm/gtx/rotate_vector.inl diff --git a/include/glm/gtx/scalar_multiplication.hpp b/deps/glm/gtx/scalar_multiplication.hpp similarity index 100% rename from include/glm/gtx/scalar_multiplication.hpp rename to deps/glm/gtx/scalar_multiplication.hpp diff --git a/include/glm/gtx/scalar_relational.hpp b/deps/glm/gtx/scalar_relational.hpp similarity index 100% rename from include/glm/gtx/scalar_relational.hpp rename to deps/glm/gtx/scalar_relational.hpp diff --git a/include/glm/gtx/scalar_relational.inl b/deps/glm/gtx/scalar_relational.inl similarity index 100% rename from include/glm/gtx/scalar_relational.inl rename to deps/glm/gtx/scalar_relational.inl diff --git a/include/glm/gtx/spline.hpp b/deps/glm/gtx/spline.hpp similarity index 100% rename from include/glm/gtx/spline.hpp rename to deps/glm/gtx/spline.hpp diff --git a/include/glm/gtx/spline.inl b/deps/glm/gtx/spline.inl similarity index 100% rename from include/glm/gtx/spline.inl rename to deps/glm/gtx/spline.inl diff --git a/include/glm/gtx/std_based_type.hpp b/deps/glm/gtx/std_based_type.hpp similarity index 100% rename from include/glm/gtx/std_based_type.hpp rename to deps/glm/gtx/std_based_type.hpp diff --git a/include/glm/gtx/std_based_type.inl b/deps/glm/gtx/std_based_type.inl similarity index 100% rename from include/glm/gtx/std_based_type.inl rename to deps/glm/gtx/std_based_type.inl diff --git a/include/glm/gtx/string_cast.hpp b/deps/glm/gtx/string_cast.hpp similarity index 100% rename from include/glm/gtx/string_cast.hpp rename to deps/glm/gtx/string_cast.hpp diff --git a/include/glm/gtx/string_cast.inl b/deps/glm/gtx/string_cast.inl similarity index 100% rename from include/glm/gtx/string_cast.inl rename to deps/glm/gtx/string_cast.inl diff --git a/include/glm/gtx/texture.hpp b/deps/glm/gtx/texture.hpp similarity index 100% rename from include/glm/gtx/texture.hpp rename to deps/glm/gtx/texture.hpp diff --git a/include/glm/gtx/texture.inl b/deps/glm/gtx/texture.inl similarity index 100% rename from include/glm/gtx/texture.inl rename to deps/glm/gtx/texture.inl diff --git a/include/glm/gtx/transform.hpp b/deps/glm/gtx/transform.hpp similarity index 100% rename from include/glm/gtx/transform.hpp rename to deps/glm/gtx/transform.hpp diff --git a/include/glm/gtx/transform.inl b/deps/glm/gtx/transform.inl similarity index 100% rename from include/glm/gtx/transform.inl rename to deps/glm/gtx/transform.inl diff --git a/include/glm/gtx/transform2.hpp b/deps/glm/gtx/transform2.hpp similarity index 100% rename from include/glm/gtx/transform2.hpp rename to deps/glm/gtx/transform2.hpp diff --git a/include/glm/gtx/transform2.inl b/deps/glm/gtx/transform2.inl similarity index 100% rename from include/glm/gtx/transform2.inl rename to deps/glm/gtx/transform2.inl diff --git a/include/glm/gtx/type_aligned.hpp b/deps/glm/gtx/type_aligned.hpp similarity index 100% rename from include/glm/gtx/type_aligned.hpp rename to deps/glm/gtx/type_aligned.hpp diff --git a/include/glm/gtx/type_aligned.inl b/deps/glm/gtx/type_aligned.inl similarity index 100% rename from include/glm/gtx/type_aligned.inl rename to deps/glm/gtx/type_aligned.inl diff --git a/include/glm/gtx/type_trait.hpp b/deps/glm/gtx/type_trait.hpp similarity index 100% rename from include/glm/gtx/type_trait.hpp rename to deps/glm/gtx/type_trait.hpp diff --git a/include/glm/gtx/type_trait.inl b/deps/glm/gtx/type_trait.inl similarity index 100% rename from include/glm/gtx/type_trait.inl rename to deps/glm/gtx/type_trait.inl diff --git a/include/glm/gtx/vec_swizzle.hpp b/deps/glm/gtx/vec_swizzle.hpp similarity index 100% rename from include/glm/gtx/vec_swizzle.hpp rename to deps/glm/gtx/vec_swizzle.hpp diff --git a/include/glm/gtx/vector_angle.hpp b/deps/glm/gtx/vector_angle.hpp similarity index 100% rename from include/glm/gtx/vector_angle.hpp rename to deps/glm/gtx/vector_angle.hpp diff --git a/include/glm/gtx/vector_angle.inl b/deps/glm/gtx/vector_angle.inl similarity index 100% rename from include/glm/gtx/vector_angle.inl rename to deps/glm/gtx/vector_angle.inl diff --git a/include/glm/gtx/vector_query.hpp b/deps/glm/gtx/vector_query.hpp similarity index 100% rename from include/glm/gtx/vector_query.hpp rename to deps/glm/gtx/vector_query.hpp diff --git a/include/glm/gtx/vector_query.inl b/deps/glm/gtx/vector_query.inl similarity index 100% rename from include/glm/gtx/vector_query.inl rename to deps/glm/gtx/vector_query.inl diff --git a/include/glm/gtx/wrap.hpp b/deps/glm/gtx/wrap.hpp similarity index 100% rename from include/glm/gtx/wrap.hpp rename to deps/glm/gtx/wrap.hpp diff --git a/include/glm/gtx/wrap.inl b/deps/glm/gtx/wrap.inl similarity index 100% rename from include/glm/gtx/wrap.inl rename to deps/glm/gtx/wrap.inl diff --git a/include/glm/integer.hpp b/deps/glm/integer.hpp similarity index 100% rename from include/glm/integer.hpp rename to deps/glm/integer.hpp diff --git a/include/glm/mat2x2.hpp b/deps/glm/mat2x2.hpp similarity index 100% rename from include/glm/mat2x2.hpp rename to deps/glm/mat2x2.hpp diff --git a/include/glm/mat2x3.hpp b/deps/glm/mat2x3.hpp similarity index 100% rename from include/glm/mat2x3.hpp rename to deps/glm/mat2x3.hpp diff --git a/include/glm/mat2x4.hpp b/deps/glm/mat2x4.hpp similarity index 100% rename from include/glm/mat2x4.hpp rename to deps/glm/mat2x4.hpp diff --git a/include/glm/mat3x2.hpp b/deps/glm/mat3x2.hpp similarity index 100% rename from include/glm/mat3x2.hpp rename to deps/glm/mat3x2.hpp diff --git a/include/glm/mat3x3.hpp b/deps/glm/mat3x3.hpp similarity index 100% rename from include/glm/mat3x3.hpp rename to deps/glm/mat3x3.hpp diff --git a/include/glm/mat3x4.hpp b/deps/glm/mat3x4.hpp similarity index 100% rename from include/glm/mat3x4.hpp rename to deps/glm/mat3x4.hpp diff --git a/include/glm/mat4x2.hpp b/deps/glm/mat4x2.hpp similarity index 100% rename from include/glm/mat4x2.hpp rename to deps/glm/mat4x2.hpp diff --git a/include/glm/mat4x3.hpp b/deps/glm/mat4x3.hpp similarity index 100% rename from include/glm/mat4x3.hpp rename to deps/glm/mat4x3.hpp diff --git a/include/glm/mat4x4.hpp b/deps/glm/mat4x4.hpp similarity index 100% rename from include/glm/mat4x4.hpp rename to deps/glm/mat4x4.hpp diff --git a/include/glm/matrix.hpp b/deps/glm/matrix.hpp similarity index 100% rename from include/glm/matrix.hpp rename to deps/glm/matrix.hpp diff --git a/include/glm/packing.hpp b/deps/glm/packing.hpp similarity index 100% rename from include/glm/packing.hpp rename to deps/glm/packing.hpp diff --git a/include/glm/simd/common.h b/deps/glm/simd/common.h similarity index 100% rename from include/glm/simd/common.h rename to deps/glm/simd/common.h diff --git a/include/glm/simd/exponential.h b/deps/glm/simd/exponential.h similarity index 100% rename from include/glm/simd/exponential.h rename to deps/glm/simd/exponential.h diff --git a/include/glm/simd/geometric.h b/deps/glm/simd/geometric.h similarity index 100% rename from include/glm/simd/geometric.h rename to deps/glm/simd/geometric.h diff --git a/include/glm/simd/integer.h b/deps/glm/simd/integer.h similarity index 100% rename from include/glm/simd/integer.h rename to deps/glm/simd/integer.h diff --git a/include/glm/simd/matrix.h b/deps/glm/simd/matrix.h similarity index 100% rename from include/glm/simd/matrix.h rename to deps/glm/simd/matrix.h diff --git a/include/glm/simd/neon.h b/deps/glm/simd/neon.h similarity index 100% rename from include/glm/simd/neon.h rename to deps/glm/simd/neon.h diff --git a/include/glm/simd/packing.h b/deps/glm/simd/packing.h similarity index 100% rename from include/glm/simd/packing.h rename to deps/glm/simd/packing.h diff --git a/include/glm/simd/platform.h b/deps/glm/simd/platform.h similarity index 100% rename from include/glm/simd/platform.h rename to deps/glm/simd/platform.h diff --git a/include/glm/simd/trigonometric.h b/deps/glm/simd/trigonometric.h similarity index 100% rename from include/glm/simd/trigonometric.h rename to deps/glm/simd/trigonometric.h diff --git a/include/glm/simd/vector_relational.h b/deps/glm/simd/vector_relational.h similarity index 100% rename from include/glm/simd/vector_relational.h rename to deps/glm/simd/vector_relational.h diff --git a/include/glm/trigonometric.hpp b/deps/glm/trigonometric.hpp similarity index 100% rename from include/glm/trigonometric.hpp rename to deps/glm/trigonometric.hpp diff --git a/include/glm/vec2.hpp b/deps/glm/vec2.hpp similarity index 100% rename from include/glm/vec2.hpp rename to deps/glm/vec2.hpp diff --git a/include/glm/vec3.hpp b/deps/glm/vec3.hpp similarity index 100% rename from include/glm/vec3.hpp rename to deps/glm/vec3.hpp diff --git a/include/glm/vec4.hpp b/deps/glm/vec4.hpp similarity index 100% rename from include/glm/vec4.hpp rename to deps/glm/vec4.hpp diff --git a/include/glm/vector_relational.hpp b/deps/glm/vector_relational.hpp similarity index 100% rename from include/glm/vector_relational.hpp rename to deps/glm/vector_relational.hpp diff --git a/include/imgui/LICENSE.txt b/deps/imgui/LICENSE.txt similarity index 100% rename from include/imgui/LICENSE.txt rename to deps/imgui/LICENSE.txt diff --git a/include/imgui/imconfig.h b/deps/imgui/imconfig.h similarity index 100% rename from include/imgui/imconfig.h rename to deps/imgui/imconfig.h diff --git a/include/imgui/imgui.cpp b/deps/imgui/imgui.cpp similarity index 100% rename from include/imgui/imgui.cpp rename to deps/imgui/imgui.cpp diff --git a/include/imgui/imgui.h b/deps/imgui/imgui.h similarity index 100% rename from include/imgui/imgui.h rename to deps/imgui/imgui.h diff --git a/include/imgui/imgui_demo.cpp b/deps/imgui/imgui_demo.cpp similarity index 100% rename from include/imgui/imgui_demo.cpp rename to deps/imgui/imgui_demo.cpp diff --git a/include/imgui/imgui_draw.cpp b/deps/imgui/imgui_draw.cpp similarity index 100% rename from include/imgui/imgui_draw.cpp rename to deps/imgui/imgui_draw.cpp diff --git a/include/imgui/imgui_impl_glfw.cpp b/deps/imgui/imgui_impl_glfw.cpp similarity index 100% rename from include/imgui/imgui_impl_glfw.cpp rename to deps/imgui/imgui_impl_glfw.cpp diff --git a/include/imgui/imgui_impl_glfw.h b/deps/imgui/imgui_impl_glfw.h similarity index 100% rename from include/imgui/imgui_impl_glfw.h rename to deps/imgui/imgui_impl_glfw.h diff --git a/include/imgui/imgui_impl_opengl3.cpp b/deps/imgui/imgui_impl_opengl3.cpp similarity index 100% rename from include/imgui/imgui_impl_opengl3.cpp rename to deps/imgui/imgui_impl_opengl3.cpp diff --git a/include/imgui/imgui_impl_opengl3.h b/deps/imgui/imgui_impl_opengl3.h similarity index 100% rename from include/imgui/imgui_impl_opengl3.h rename to deps/imgui/imgui_impl_opengl3.h diff --git a/include/imgui/imgui_impl_vulkan.cpp b/deps/imgui/imgui_impl_vulkan.cpp similarity index 100% rename from include/imgui/imgui_impl_vulkan.cpp rename to deps/imgui/imgui_impl_vulkan.cpp diff --git a/include/imgui/imgui_impl_vulkan.h b/deps/imgui/imgui_impl_vulkan.h similarity index 100% rename from include/imgui/imgui_impl_vulkan.h rename to deps/imgui/imgui_impl_vulkan.h diff --git a/include/imgui/imgui_internal.h b/deps/imgui/imgui_internal.h similarity index 100% rename from include/imgui/imgui_internal.h rename to deps/imgui/imgui_internal.h diff --git a/include/imgui/imgui_widgets.cpp b/deps/imgui/imgui_widgets.cpp similarity index 100% rename from include/imgui/imgui_widgets.cpp rename to deps/imgui/imgui_widgets.cpp diff --git a/include/imgui/imstb_rectpack.h b/deps/imgui/imstb_rectpack.h similarity index 100% rename from include/imgui/imstb_rectpack.h rename to deps/imgui/imstb_rectpack.h diff --git a/include/imgui/imstb_textedit.h b/deps/imgui/imstb_textedit.h similarity index 100% rename from include/imgui/imstb_textedit.h rename to deps/imgui/imstb_textedit.h diff --git a/include/imgui/imstb_truetype.h b/deps/imgui/imstb_truetype.h similarity index 100% rename from include/imgui/imstb_truetype.h rename to deps/imgui/imstb_truetype.h diff --git a/include/libguarded/LICENSE b/deps/libguarded/LICENSE similarity index 100% rename from include/libguarded/LICENSE rename to deps/libguarded/LICENSE diff --git a/include/libguarded/mutex_guarded.h b/deps/libguarded/mutex_guarded.h similarity index 100% rename from include/libguarded/mutex_guarded.h rename to deps/libguarded/mutex_guarded.h diff --git a/include/libguarded/shared_mutex_guarded.h b/deps/libguarded/shared_mutex_guarded.h similarity index 100% rename from include/libguarded/shared_mutex_guarded.h rename to deps/libguarded/shared_mutex_guarded.h diff --git a/include/meshoptimizer/CONTRIBUTING.md b/deps/meshoptimizer/CONTRIBUTING.md similarity index 100% rename from include/meshoptimizer/CONTRIBUTING.md rename to deps/meshoptimizer/CONTRIBUTING.md diff --git a/include/meshoptimizer/LICENSE.md b/deps/meshoptimizer/LICENSE.md similarity index 100% rename from include/meshoptimizer/LICENSE.md rename to deps/meshoptimizer/LICENSE.md diff --git a/include/meshoptimizer/README.md b/deps/meshoptimizer/README.md similarity index 100% rename from include/meshoptimizer/README.md rename to deps/meshoptimizer/README.md diff --git a/include/meshoptimizer/allocator.cpp b/deps/meshoptimizer/allocator.cpp similarity index 100% rename from include/meshoptimizer/allocator.cpp rename to deps/meshoptimizer/allocator.cpp diff --git a/include/meshoptimizer/clusterizer.cpp b/deps/meshoptimizer/clusterizer.cpp similarity index 100% rename from include/meshoptimizer/clusterizer.cpp rename to deps/meshoptimizer/clusterizer.cpp diff --git a/include/meshoptimizer/extern/basisu_format.h b/deps/meshoptimizer/extern/basisu_format.h similarity index 100% rename from include/meshoptimizer/extern/basisu_format.h rename to deps/meshoptimizer/extern/basisu_format.h diff --git a/include/meshoptimizer/extern/cgltf.h b/deps/meshoptimizer/extern/cgltf.h similarity index 100% rename from include/meshoptimizer/extern/cgltf.h rename to deps/meshoptimizer/extern/cgltf.h diff --git a/include/meshoptimizer/extern/fast_obj.h b/deps/meshoptimizer/extern/fast_obj.h similarity index 100% rename from include/meshoptimizer/extern/fast_obj.h rename to deps/meshoptimizer/extern/fast_obj.h diff --git a/include/meshoptimizer/extern/khr_df.h b/deps/meshoptimizer/extern/khr_df.h similarity index 100% rename from include/meshoptimizer/extern/khr_df.h rename to deps/meshoptimizer/extern/khr_df.h diff --git a/include/meshoptimizer/indexcodec.cpp b/deps/meshoptimizer/indexcodec.cpp similarity index 100% rename from include/meshoptimizer/indexcodec.cpp rename to deps/meshoptimizer/indexcodec.cpp diff --git a/include/meshoptimizer/indexgenerator.cpp b/deps/meshoptimizer/indexgenerator.cpp similarity index 100% rename from include/meshoptimizer/indexgenerator.cpp rename to deps/meshoptimizer/indexgenerator.cpp diff --git a/include/meshoptimizer/meshoptimizer.h b/deps/meshoptimizer/meshoptimizer.h similarity index 100% rename from include/meshoptimizer/meshoptimizer.h rename to deps/meshoptimizer/meshoptimizer.h diff --git a/include/meshoptimizer/overdrawanalyzer.cpp b/deps/meshoptimizer/overdrawanalyzer.cpp similarity index 100% rename from include/meshoptimizer/overdrawanalyzer.cpp rename to deps/meshoptimizer/overdrawanalyzer.cpp diff --git a/include/meshoptimizer/overdrawoptimizer.cpp b/deps/meshoptimizer/overdrawoptimizer.cpp similarity index 100% rename from include/meshoptimizer/overdrawoptimizer.cpp rename to deps/meshoptimizer/overdrawoptimizer.cpp diff --git a/include/meshoptimizer/simplifier.cpp b/deps/meshoptimizer/simplifier.cpp similarity index 100% rename from include/meshoptimizer/simplifier.cpp rename to deps/meshoptimizer/simplifier.cpp diff --git a/include/meshoptimizer/spatialorder.cpp b/deps/meshoptimizer/spatialorder.cpp similarity index 100% rename from include/meshoptimizer/spatialorder.cpp rename to deps/meshoptimizer/spatialorder.cpp diff --git a/include/meshoptimizer/stripifier.cpp b/deps/meshoptimizer/stripifier.cpp similarity index 100% rename from include/meshoptimizer/stripifier.cpp rename to deps/meshoptimizer/stripifier.cpp diff --git a/include/meshoptimizer/vcacheanalyzer.cpp b/deps/meshoptimizer/vcacheanalyzer.cpp similarity index 100% rename from include/meshoptimizer/vcacheanalyzer.cpp rename to deps/meshoptimizer/vcacheanalyzer.cpp diff --git a/include/meshoptimizer/vcacheoptimizer.cpp b/deps/meshoptimizer/vcacheoptimizer.cpp similarity index 100% rename from include/meshoptimizer/vcacheoptimizer.cpp rename to deps/meshoptimizer/vcacheoptimizer.cpp diff --git a/include/meshoptimizer/vertexcodec.cpp b/deps/meshoptimizer/vertexcodec.cpp similarity index 100% rename from include/meshoptimizer/vertexcodec.cpp rename to deps/meshoptimizer/vertexcodec.cpp diff --git a/include/meshoptimizer/vertexfilter.cpp b/deps/meshoptimizer/vertexfilter.cpp similarity index 100% rename from include/meshoptimizer/vertexfilter.cpp rename to deps/meshoptimizer/vertexfilter.cpp diff --git a/include/meshoptimizer/vfetchanalyzer.cpp b/deps/meshoptimizer/vfetchanalyzer.cpp similarity index 100% rename from include/meshoptimizer/vfetchanalyzer.cpp rename to deps/meshoptimizer/vfetchanalyzer.cpp diff --git a/include/meshoptimizer/vfetchoptimizer.cpp b/deps/meshoptimizer/vfetchoptimizer.cpp similarity index 100% rename from include/meshoptimizer/vfetchoptimizer.cpp rename to deps/meshoptimizer/vfetchoptimizer.cpp diff --git a/include/mini-yaml/LICENSE b/deps/mini-yaml/LICENSE similarity index 100% rename from include/mini-yaml/LICENSE rename to deps/mini-yaml/LICENSE diff --git a/include/mini-yaml/README.md b/deps/mini-yaml/README.md similarity index 100% rename from include/mini-yaml/README.md rename to deps/mini-yaml/README.md diff --git a/include/mini-yaml/Yaml.cpp b/deps/mini-yaml/Yaml.cpp similarity index 100% rename from include/mini-yaml/Yaml.cpp rename to deps/mini-yaml/Yaml.cpp diff --git a/include/mini-yaml/Yaml.hpp b/deps/mini-yaml/Yaml.hpp similarity index 100% rename from include/mini-yaml/Yaml.hpp rename to deps/mini-yaml/Yaml.hpp diff --git a/deps/picoquic/CMakeLists.txt b/deps/picoquic/CMakeLists.txt new file mode 100644 index 0000000..0dc7cf8 --- /dev/null +++ b/deps/picoquic/CMakeLists.txt @@ -0,0 +1,60 @@ +cmake_minimum_required(VERSION 2.8.11) +cmake_policy(SET CMP0003 NEW) +cmake_policy(SET CMP0048 NEW) +project(picoquic VERSION 0.0.1 LANGUAGES C CXX) +find_package (Threads REQUIRED) + +add_subdirectory("picotls") + +set(CMAKE_C_STANDARD 11) + +set(CMAKE_C_FLAGS "-std=c99 -Wall -Werror -O2 -g ${CC_WARNING_FLAGS} ${CMAKE_C_FLAGS}") + +if(DISABLE_DEBUG_PRINTF) + set(CMAKE_C_FLAGS "-DDISABLE_DEBUG_PRINTF ${CMAKE_C_FLAGS}") +endif() + +set(PICOQUIC_LIBRARY_FILES + bbr.c + bytestream.c + cc_common.c + cubic.c + fastcc.c + frames.c + intformat.c + logger.c + logwriter.c + newreno.c + packet.c + picohash.c + picosocks.c + picosplay.c + quicctx.c + sacks.c + sender.c + sim_link.c + sockloop.c + spinbit.c + ticket_store.c + token_store.c + tls_api.c + transport.c + unified_log.c + util.c +) + +set(PICOQUIC_CORE_HEADERS + picoquic.h + picosocks.h + picoquic_utils.h + picoquic_packet_loop.h + picoquic_unified_log.h + picoquic_logger.h + picoquic_binlog.h +) + +find_package(OpenSSL) + +include_directories(picoquic "picotls/include" ${OPENSSL_INCLUDE_DIR}) +add_library(picoquic-core ${PICOQUIC_CORE_HEADERS} ${PICOQUIC_LIBRARY_FILES}) +target_link_libraries(picoquic-core picotls-core picotls-openssl picotls-fusion ${OPENSSL_LIBRARIES}) diff --git a/deps/picoquic/LICENSE b/deps/picoquic/LICENSE new file mode 100644 index 0000000..6dd225c --- /dev/null +++ b/deps/picoquic/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2017 Private Octopus + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/deps/picoquic/README.md b/deps/picoquic/README.md new file mode 100644 index 0000000..2f3acda --- /dev/null +++ b/deps/picoquic/README.md @@ -0,0 +1,150 @@ +# picoquic + +Minimalist implementation of the QUIC protocol, as defined by the IETF. +The IETF spec started with the version of QUIC defined by Google and +implemented in Chrome, but the IETF spec is independent of Chrome, and +does not attempt to be backward compatible. The main developer is +Christian Huitema. + +The first goal of this project is to provide feedback on the development +of a QUIC standard in the IETF QUIC WG. Information on the WG is available at +https://datatracker.ietf.org/wg/quic/charter/. The in-progress version of +the spec is available on GitHub at https://github.com/quicwg. + +The second goal is to experiment with API for non-HTTP development, such as +DNS over QUIC. Then there are plenty of other features we may dream off, +such as support for multipath, or support for peer-to-peer applications. +That's on the horizon, but not there now. + +The code in this repo is a work in progress, but it is getting stable, just +like the specification itself. The working group just issued draft-29, which +is meant as the stabilization draft, and the Working Group Last Call is out! +At this stage the +changes in Picoquic will be mostly driven by performance issues, or API +improvements. And bug fixing, too. + +There are many implementations of Quic, listed +at https://github.com/quicwg/base-drafts/wiki/Implementations. The interop +goals are defined at https://github.com/quicwg/base-drafts/wiki/17th-Implementation-Draft, +which points to the current interoperability matrix. Several implementations provide +docker images to the "Quic Interop Runner" project, with results updated daily +at https://interop.seemann.io/. + +Bastian Köcher has developed bindings of the picoquic library to [RUST](https://www.rust-lang.org/en-US/). +His repository can be found [here](https://github.com/bkchr/picoquic-rs). +You may want to check it. + +# Development + +Picoquic is currently developed as a Visual Studio 2017 project, +and simultaneously tested on Windows and on Linux. It has a dependency +on the [Picotls implementation of TLS 1.3](https://github.com/h2o/picotls). +Picotls has two modes, a feature rich version that depends on OpenSSL, and a +leaner version that only depends on the "minicrypto" library. For now, +Picoquic uses the OpenSSL version, and has a dependency on OpenSSL. + +The project consists of a core library (picoquic), of a test library +(picoquictest), and of a test program (picoquicfirst). All these are +written in C. In the Visual Studio project, the +test library is wrapped up in the Visual Studio unittest framework, which +makes for convenient regression testing during development. In the Linux +builds, the tests are run through a command line program. + +# Milestones + +As explained in the Wiki, Picoquic is actively tested against other implementations +during the QUIC Interop days. See https://github.com/private-octopus/picoquic/wiki/QUIC-milestones-and-interop-testing. + +The current version is aligned with draft 27. All big features are supported, including +the interface between QUIC and TLS, 0-RTT, migration and key rollover. The state of +development is tracked in the list of issues in this repository. + +We have started an implementation +of [DNS over QUIC](https://datatracker.ietf.org/doc/draft-huitema-quic-dnsoquic/) +as [Quicdoq](https://github.com/private-octopus/quicdoq). DNS over Quic is interesting +by itself, but it also provides an example for building an application different than +HTTP on top of Picoquic. + +We are spending time bettering the implementation. Until now +the focus has been on correctness rather than performance. We will keep correctness, +but we will improve performance, especially in light of practical experience with +applications. Suggestions are wellcome. + +# Building Picoquic + +Picoquic is developed in C, and can be built under Windows or Linux. Building the +project requires first managing the dependencies, [Picotls](https://github.com/h2o/picotls) +and OpenSSL. Please note that you will need a recent version of Picotls -- +the Picotls API has eveolved recently to support the latest version of QUIC. The +current code is tested against the Picotls version of Sat Sep 12 20:48:55 2020 +0900, +after commit `2464adadf28c1b924416831d24ca62380936a209`. The code uses OpenSSL +version 1.1.1. + +## Picoquic on Windows + +To build Picoquic on Windows, you need to: + + * Install and build Openssl on your machine + + * Document the location of the Openssl install in the environment variable OPENSSLDIR + (OPENSSL64DIR for the x64 builds) + + * Make sure that a copy of `libcrypto.lib` is available at that location, and that + a copy of `applink.c` is available at the `include` location: $(OPENSSLDIR)\include\ + for win32 builds, $(OPENSSL64DIR)\include\ for the x64 builds. + + * Clone and compile Picotls, using the Picotls for Windows options. The picotls project + should be in the same directory level as the picoquic project, and the folder name + should be kept as picotls. + + * Clone and compile Picoquic, using the Visual Studio 2017 solution picoquic.sln included in + the sources. + + * You can use the unit tests included in the Visual Studio solution to verify the port. + +## Picoquic on Linux + +The build experience on Linux is now much improved, thanks to check-ins from Deb Banerjee +and Igor Lubashev. + +To build Picoquic on Linux, you need to: + + * Install and build Openssl on your machine + + * Clone and compile Picotls, using cmake as explained in the Picotls documentation. + + * Clone and compile Picoquic: +~~~ + cmake . + make +~~~ + * Run the test program `picoquic_ct` to verify the port. + +## Picoquic on MacOSX + +Thanks to Frederik Deweerdt for ensuring that Picoquic runs on MacOSX. The build steps +are the same as for Linux. + +## Picoquic on FreeBSD + +Same build steps as Linux. Picoquic probably also works on other BSD variants, but only FreeBSD +has been tested so far. + +## Developing applications + +Sorry, not all that much documentation yet. This will come as we populate the wiki. Your +best bet is to look at the demonstration program "picoquicdemo" that is included in the +release. The sources are in "picoquicfirst/picoquicdemo.c". The `sample` folder +contains a code sample for a simplistic file transfer protocol, which might +be a good place to start. Look at the README.md file in the sample folder for +more details. + +## Testing previous versions + +The code is constantly updated to track the latest version of the specification. It currently +conforms to draft-29, and will negotiate support for the corresponding version `0xFF00001d`. +It will also accept negotiation of version `0xFF00001c`, draft-28, and `0xFF00001b`, draft-27. + +# Creating QLOG Log Files + +See [How To Produce QLOG files with picoquic](doc/QLOG.md) diff --git a/deps/picoquic/ReadMe.txt b/deps/picoquic/ReadMe.txt new file mode 100644 index 0000000..ca1b681 --- /dev/null +++ b/deps/picoquic/ReadMe.txt @@ -0,0 +1,29 @@ +======================================================================== + STATIC LIBRARY : picoquic Project Overview +======================================================================== + +AppWizard has created this picoquic library project for you. + +No source files were created as part of your project. + + +picoquic.vcxproj + This is the main project file for VC++ projects generated using an Application Wizard. + It contains information about the version of Visual C++ that generated the file, and + information about the platforms, configurations, and project features selected with the + Application Wizard. + +picoquic.vcxproj.filters + This is the filters file for VC++ projects generated using an Application Wizard. + It contains information about the association between the files in your project + and the filters. This association is used in the IDE to show grouping of files with + similar extensions under a specific node (for e.g. ".cpp" files are associated with the + "Source Files" filter). + +///////////////////////////////////////////////////////////////////////////// +Other notes: + +AppWizard uses "TODO:" comments to indicate parts of the source code you +should add to or customize. + +///////////////////////////////////////////////////////////////////////////// diff --git a/deps/picoquic/bbr.c b/deps/picoquic/bbr.c new file mode 100644 index 0000000..578c576 --- /dev/null +++ b/deps/picoquic/bbr.c @@ -0,0 +1,1117 @@ +/* +* Author: Christian Huitema +* Copyright (c) 2019, Private Octopus, Inc. +* All rights reserved. +* +* Permission to use, copy, modify, and distribute this software for any +* purpose with or without fee is hereby granted, provided that the above +* copyright notice and this permission notice appear in all copies. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL Private Octopus, Inc. BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "picoquic_internal.h" +#include +#include +#include "cc_common.h" + +/* +Implementation of the BBR algorithm, tuned for Picoquic. + +The main idea of BBR is to track the "bottleneck bandwidth", and to tune the +transport stack to send exactly at that speed. This ensures good network +utilisation while avoiding the building of queues. To do that the stack +needs to constantly estimate the available data rate. It does that by +measuring the rate at which acknowledgements come back, providing what it +calls the delivery rate. + +That approach includes an implicit feedback loop. The delivery rate can never +exceed the sending rate. That will effectively detects a transmission slow +down due to partial congestion, but if the algorithm just did that the +sending rate will remain constant when the network is lightly loaded +and ratchet down during time of congestion, leading to very low efficiency. +The available bandwidth can only be tested by occasionally sending faster +than the measured delivery rate. + +BBR does that by following a cycle of "send, test and drain". During the +sending period, the stack sends at the measured rate. During the testing +period, it sends faster, 25% faster with recommended parameters. This +risk creating a queue if the bandwidth had not increased, so the test +period is followed by a drain period during which the stack sends 25% +slower than the measured rate. If the test is successful, the new bandwidth +measurement will be available at the end of the draining period, and +the increased bandwidth will be used in the next cycle. + +Tuning the sending rate does not guarantee a short queue, it only +guarantees a stable queue. BBR controls the queue by limiting the +amount of data "in flight" (congestion window, CWIN) to the product +of the bandwidth estimate by the RTT estimate, plus a safety marging to ensure +continuous transmission. Using the average RTT there would lead to a runaway +loop in which oversized windows lead to increased queues and then increased +average RTT. Instead of average RTT, BBR uses a minimum RTT. Since the +mimimum RTT might vary with routing changes, the minimum RTT is measured +on a sliding window of 10 seconds. + +The bandwidth estimation needs to be robust against short term variations +common in wireless networks. BBR retains the maximum +delivery rate observed over a series of probing intervals. Each interval +starts with a specific packet transmission and ends when that packet +or a later transmission is acknowledged. BBR does that by tracking +the delivered counter associated with packets and comparing it to +the delivered counter at start of period. + +During start-up, BBR performs its own equivalent of Reno's slow-start. +It does that by using a pacing gain of 2.89, i.e. sending 2.89 times +faster than the measured maximum. It exits slow start when it found +a bandwidth sufficient to fill the pipe. + +The bandwidth measurements can be wrong if the application is not sending +enough data to fill the pipe. BBR tracks that, and does not reduce bandwidth +or exit slow start if the application is limiting transmission. + +This implementation follows draft-cardwell-iccrg-bbr-congestion-control, +with a couple of changes for handling the multipath nature of quic. +There is a BBR control state per path. +Most of BBR the variables defined in the draft are implemented +in the "BBR state" structure, with a few exceptions: + +* BBR.delivered is represented by path_x.delivered, and is maintained + as part of ACK processing + +* Instead of "bytes_in_transit", we use "bytes_in_transit", which is + already maintained by the stack. + +* Compute bytes_delivered by summing all calls to ACK(bytes) before + the call to RTT update. + +* In the Probe BW mode, the draft suggests cwnd_gain = 2. We observed + that this results in queue sizes of 2, which is too high, so we + reset that to 1.125. + +The "packet" variables are defined in the picoquic_packet_t. + +Early testing showed that BBR startup phase requires several more RTT +than the Hystart process used in modern versions of Reno or Cubic. BBR +only ramps up the data rate after the first bandwidth measurement is +available, 2*RTT after start, while Reno or Cubic start ramping up +after just 1 RTT. BBR only exits startup if three consecutive RTT +pass without significant BW measurement increase, which not only +adds delay but also creates big queues as data is sent at 2.89 times +the bottleneck rate. This is a tradeoff: longer search for bandwidth in +slow start is less likely to stop too early because of transient +issues, but one high bandwidth and long delay links this translates +to long delays and a big batch of packet losses. + +This BBR implementation addresses these issues by switching to +Hystart instead of startup if the RTT is above the Reno target of +100 ms. + +*/ + +/* Detection of leaky-bucket pacers. + * This is based on code added to BBR after the IETF draft was published. + * The code detect whether the connection is being "policed" by a leaky-bucket based + * parser, and introduces state variables: + * - lt_use_bw, boolean: check whether the connection is currently constrained + * to use a limited bandwidth. + * - lt_rtt_cnt: number of RTT during which the bandwidth has been limited. Exit the + * limited bandwidth state when this reaches the maximum value. + * - lt_is_sampling: whether the connection is sampling the number of loss + * intervals. This starts when the first losses are noticed. It is reset if + * the bandwidth is app limited. + * Sampling lasts for a number of rounds, as counted by the round_start + * variable. No action taken except updating counters in that state. + * Sampling can only ends on an interval when losses are detected, to avoid + * undercounting. If no loss, sampling continues after the min required + * interval. If no losses for too long, sampling state is reset. + * At the end of the sampling interval compute the ratio of packets lost + * to packet delivered. If it is below threshold, continue sampling, i.e., + * extend the interval. + * If loss rate exceed the threshold, compute the duration of the interval. + * If it is too short, extend. If is is too long, reset the sampling. + * Else, compute the delivery rate for the interval. Check whether this + * is the first detection, in which case do nothing but remember the estimated + * rate in "lt_bw". Else, check whether the new rate is "close enough" to + * the previous rate, which indicates active policing. If that is true, + * activate policing to the average rate between current and previous sample. + */ + +/* Reaction to losses and ECN + * This code is an implementation of BBRv1, which pretty much ignores packet + * losses or ECN marks. Google has still developed BBRv2, which is generally + * considered much more robust. Once the BBRv2 specification is available, + * we should develop it. However, before BBRv2 is there, we need to fix the + * most egregious issues in BBR v1. For example, in a test, we show that if + * a receiver starts a high speed download and then disappears, the sender + * will only close the connection after repeating over 1000 packets, + * compared to only 32 with New Reno or Cubic. This is because BBR does + * not slow down or reduce the CWIN on loss indication, even when there + * are many loss indications. + * + * We implement the following fixes: + * + * - On basic loss indication, run a filter to determine whether the loss + * rate is getting too high. This will allow the code to continue + * ignoring low loss rates, but somehow react to high loss rates. + * + * - If high loss rate is detected, halve the congestion window. Do + * the same if an EC mark is received. + * + * - If a timeout loss is detected, reduce the window to the minimum + * value. + * + * This needs to be coordinated with the BBR state machine. We implement + * it as such: + * + * - if the state is start-up or start-up-long-rtt, exit startup + * and move to a drain state. + * - if the state is probe-bw, start the new period with a conservative + * packet window (trigger by cycle_on_loss state variable) + * - if the state is probe-RTT, do nothing special... + * + * The packet losses and congestion signals should be used only once per + * RTT. We filter with a "loss period start time" value, and only + * take signals into account if they happen 1-RTT after the current + * loss start time. However, if the previous loss was not due to + * timeout, the timeout will still be handled. + */ + +typedef enum { + picoquic_bbr_alg_startup = 0, + picoquic_bbr_alg_drain, + picoquic_bbr_alg_probe_bw, + picoquic_bbr_alg_probe_rtt, + picoquic_bbr_alg_startup_long_rtt +} picoquic_bbr_alg_state_t; + +#define BBR_BTL_BW_FILTER_LENGTH 10 +#define BBR_RT_PROP_FILTER_LENGTH 10 +#define BBR_HIGH_GAIN 2.8853900817779 /* 2/ln(2) */ +#define BBR_MIN_PIPE_CWND(mss) (4*mss) +#define BBR_GAIN_CYCLE_LEN 8 +#define BBR_PROBE_RTT_INTERVAL 10000000 /* 10 sec, 10000000 microsecs */ +#define BBR_PROBE_RTT_DURATION 200000 /* 200msec, 200000 microsecs */ +#define BBR_PACING_RATE_LOW 150000.0 /* 150000 B/s = 1.2 Mbps */ +#define BBR_PACING_RATE_MEDIUM 3000000.0 /* 3000000 B/s = 24 Mbps */ +#define BBR_GAIN_CYCLE_LEN 8 +#define BBR_GAIN_CYCLE_MAX_START 5 +#define BBR_LT_BW_INTERVAL_MIN_RTT 4 +#define BBR_LT_BW_RATIO_SCALE 1024 +#define BBR_LT_BW_RATIO_SCALED_TARGET 205 /* 205/1024 is very close 20% */ + +#define BBR_LT_BW_INTERVAL_MAX_RTT (4*BBR_LT_BW_INTERVAL_MIN_RTT) +#define BBR_LT_BW_RATIO_INVERSE 8 +#define BBR_LT_BW_BYTES_PER_SEC_DIFF 4000 +#define BBR_LT_BW_MAX_RTTS 48 + + +static const double bbr_pacing_gain_cycle[BBR_GAIN_CYCLE_LEN] = { 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.25, 0.75}; + +typedef struct st_picoquic_bbr_state_t { + picoquic_bbr_alg_state_t state; + uint64_t btl_bw; + uint64_t next_round_delivered; + uint64_t round_start_time; + uint64_t btl_bw_filter[BBR_BTL_BW_FILTER_LENGTH]; + uint64_t full_bw; + uint64_t rt_prop; + uint64_t rt_prop_stamp; + uint64_t cycle_stamp; + uint64_t probe_rtt_done_stamp; + uint64_t prior_cwnd; + uint64_t prior_in_flight; + uint64_t bytes_delivered; /* Number of bytes signalled in ACK notify, but not processed yet */ + uint64_t send_quantum; + picoquic_min_max_rtt_t rtt_filter; + uint64_t target_cwnd; + double pacing_gain; + double cwnd_gain; + double pacing_rate; + unsigned int cycle_index; + unsigned int cycle_start; + int round_count; + int full_bw_count; + int lt_rtt_cnt; + uint64_t lt_bw; + uint64_t lt_last_stamp; /* Time in microsec at start of interval */ + uint64_t previous_round_lost; + uint64_t previous_sampling_delivered; + uint64_t previous_sampling_lost; + uint64_t loss_interval_start; /* Time in microsec when last loss considered */ + + unsigned int filled_pipe : 1; + unsigned int round_start : 1; + unsigned int rt_prop_expired : 1; + unsigned int probe_rtt_round_done : 1; + unsigned int idle_restart : 1; + unsigned int packet_conservation : 1; + unsigned int btl_bw_increased : 1; + unsigned int lt_use_bw : 1; + unsigned int lt_is_sampling : 1; + unsigned int last_loss_was_timeout : 1; + unsigned int cycle_on_loss : 1; + +} picoquic_bbr_state_t; + +void BBRltbwSampling(picoquic_bbr_state_t* bbr_state, picoquic_path_t* path_x, uint64_t current_time); +static void BBRResetProbeBwMode(picoquic_bbr_state_t* bbr_state, uint64_t current_time); + +static uint64_t BBRGetBtlBW(picoquic_bbr_state_t* bbr_state) +{ + return (bbr_state->lt_use_bw) ? bbr_state->lt_bw : bbr_state->btl_bw; +} + +void BBREnterStartupLongRTT(picoquic_bbr_state_t* bbr_state, picoquic_path_t* path_x) +{ + uint64_t cwnd = PICOQUIC_CWIN_INITIAL; + bbr_state->state = picoquic_bbr_alg_startup_long_rtt; + + if (path_x->rtt_min > PICOQUIC_TARGET_RENO_RTT) { + if (path_x->rtt_min > PICOQUIC_TARGET_SATELLITE_RTT) { + cwnd = (uint64_t)((double)cwnd * (double)PICOQUIC_TARGET_SATELLITE_RTT / (double)PICOQUIC_TARGET_RENO_RTT); + } + else { + cwnd = (uint64_t)((double)cwnd * (double)path_x->rtt_min / (double)PICOQUIC_TARGET_RENO_RTT); + } + } + if (cwnd > path_x->cwin) { + path_x->cwin = cwnd; + } +} + +void BBREnterStartup(picoquic_bbr_state_t* bbr_state) +{ + bbr_state->state = picoquic_bbr_alg_startup; + bbr_state->pacing_gain = BBR_HIGH_GAIN; + bbr_state->cwnd_gain = BBR_HIGH_GAIN; +} + +void BBRSetSendQuantum(picoquic_bbr_state_t* bbr_state, picoquic_path_t* path_x) +{ + if (bbr_state->pacing_rate < BBR_PACING_RATE_LOW) { + bbr_state->send_quantum = 1ull * path_x->send_mtu; + } + else if (bbr_state->pacing_rate < BBR_PACING_RATE_MEDIUM) { + bbr_state->send_quantum = 2ull * path_x->send_mtu; + } + else { + bbr_state->send_quantum = (uint64_t)(bbr_state->pacing_rate * 0.001); + if (bbr_state->send_quantum > 0x10000) { + bbr_state->send_quantum = 0x10000; + } + } +} + +uint64_t BBRInflight(picoquic_bbr_state_t* bbr_state, double gain) +{ + uint64_t cwnd = PICOQUIC_CWIN_INITIAL; + if (bbr_state->rt_prop != UINT64_MAX){ + /* Bandwidth is estimated in bytes per second, rtt in microseconds*/ + double estimated_bdp = (((double)BBRGetBtlBW(bbr_state) * (double)bbr_state->rt_prop) / 1000000.0); + uint64_t quanta = 3 * bbr_state->send_quantum; + cwnd = (uint64_t)(gain * estimated_bdp) + quanta; + } + return cwnd; +} + + +void BBRUpdateTargetCwnd(picoquic_bbr_state_t* bbr_state) +{ + bbr_state->target_cwnd = BBRInflight(bbr_state, bbr_state->cwnd_gain); +} + +static void picoquic_bbr_reset(picoquic_bbr_state_t* bbr_state, picoquic_path_t* path_x, uint64_t current_time) +{ + memset(bbr_state, 0, sizeof(picoquic_bbr_state_t)); + path_x->cwin = PICOQUIC_CWIN_INITIAL; + bbr_state->rt_prop = UINT64_MAX; + + bbr_state->rt_prop_stamp = current_time; + bbr_state->cycle_stamp = current_time; + bbr_state->cycle_index = 0; + bbr_state->cycle_start = 0; + + BBREnterStartup(bbr_state); + BBRSetSendQuantum(bbr_state, path_x); + BBRUpdateTargetCwnd(bbr_state); +} + +static void picoquic_bbr_init(picoquic_path_t* path_x, uint64_t current_time) +{ + /* Initialize the state of the congestion control algorithm */ + picoquic_bbr_state_t* bbr_state = (picoquic_bbr_state_t*)malloc(sizeof(picoquic_bbr_state_t)); + path_x->congestion_alg_state = (void*)bbr_state; + if (bbr_state != NULL) { + picoquic_bbr_reset(bbr_state, path_x, current_time); + } +} + +/* Release the state of the congestion control algorithm */ +static void picoquic_bbr_delete(picoquic_path_t* path_x) +{ + if (path_x->congestion_alg_state != NULL) { + free(path_x->congestion_alg_state); + path_x->congestion_alg_state = NULL; + } +} + + +/* Implementation of leaky-bucket pacer detection + */ + +void BBRltbwResetInterval(picoquic_bbr_state_t* bbr_state, picoquic_path_t* path_x, uint64_t current_time) +{ + bbr_state->lt_last_stamp = current_time; + bbr_state->previous_sampling_delivered = path_x->delivered; + bbr_state->previous_sampling_lost = path_x->total_bytes_lost; + bbr_state->previous_round_lost = path_x->total_bytes_lost; + bbr_state->lt_rtt_cnt = 0; +} + +void BBRltbwResetSampling(picoquic_bbr_state_t* bbr_state, picoquic_path_t* path_x, uint64_t current_time) +{ + bbr_state->lt_bw = 0; + bbr_state->lt_use_bw = 0; + bbr_state->lt_is_sampling = 0; + BBRltbwResetInterval(bbr_state, path_x, current_time); +} + +void BBRltbwIntervalDone(picoquic_bbr_state_t* bbr_state, picoquic_path_t* path_x, uint64_t bw, uint64_t current_time) +{ + if (bbr_state->lt_bw) { + /* This is not the first limited interval. Look whether it is close enough */ + uint64_t diff = (bw > bbr_state->lt_bw) ? bw - bbr_state->lt_bw : bbr_state->lt_bw - bw; + if (diff * BBR_LT_BW_RATIO_INVERSE < bbr_state->lt_bw || + diff < BBR_LT_BW_BYTES_PER_SEC_DIFF) { + bbr_state->lt_bw = (bbr_state->lt_bw + bw) / 2; + bbr_state->lt_use_bw = 1; + bbr_state->pacing_gain = 1.0; + bbr_state->lt_rtt_cnt = 0; + return; + } + } + /* If first interval or non-matching rate, just remember */ + bbr_state->lt_bw = bw; + BBRltbwResetInterval(bbr_state, path_x, current_time); +} + +void BBRltbwSampling(picoquic_bbr_state_t* bbr_state, picoquic_path_t* path_x, uint64_t current_time) +{ + uint64_t losses = (path_x->total_bytes_lost > bbr_state->previous_round_lost) ? + path_x->total_bytes_lost - bbr_state->previous_round_lost : 0; + uint64_t delivered; + uint64_t interval_microsec; + uint64_t bw; + + if (bbr_state->lt_use_bw) { + if (bbr_state->state == picoquic_bbr_alg_probe_bw && bbr_state->round_start) { + bbr_state->lt_rtt_cnt++; + if (bbr_state->lt_rtt_cnt > BBR_LT_BW_MAX_RTTS) { + BBRltbwResetSampling(bbr_state, path_x, current_time); + BBRResetProbeBwMode(bbr_state, current_time); + return; + } + } + } + + if (!bbr_state->lt_is_sampling) { + /* Return if no loss; */ + if (losses == 0) { + return; + } + /* Reset sampling otherwise. */ + BBRltbwResetSampling(bbr_state, path_x, current_time); + bbr_state->lt_is_sampling = 1; + } + + /* Reset sampling if app is limited */ + if (path_x->last_bw_estimate_path_limited) { + BBRltbwResetSampling(bbr_state, path_x, current_time); + return; + } + /* Check whether we are reaching the end of the interval */ + if (!bbr_state->round_start) { + return; + } else { + bbr_state->lt_rtt_cnt++; /* count round trips in this interval */ + bbr_state->previous_round_lost = path_x->total_bytes_lost; + + if (bbr_state->lt_rtt_cnt < BBR_LT_BW_INTERVAL_MIN_RTT) { + return; /* sampling interval needs to be longer */ + } + if (bbr_state->lt_rtt_cnt > BBR_LT_BW_INTERVAL_MAX_RTT) { + BBRltbwResetSampling(bbr_state, path_x, current_time); /* interval is too long */ + return; + } + } + /* Continue sampling if no losses on this round */ + if (losses == 0) { + return; + } + /* Calculate bytes lost and delivered in sampling interval. + * Notice that the previous values of losses and delivered were for the round, not the interval. + */ + if (path_x->delivered <= bbr_state->previous_sampling_delivered) { + /* No delivery at all, cannot calculate any ratio, wait some more. */ + return; + } + losses = (path_x->total_bytes_lost > bbr_state->previous_sampling_lost) ? + path_x->total_bytes_lost - bbr_state->previous_sampling_lost : 0; + delivered = path_x->delivered - bbr_state->previous_sampling_delivered; + /* Check the loss ratio */ + if (losses * BBR_LT_BW_RATIO_SCALE < BBR_LT_BW_RATIO_SCALED_TARGET * delivered) { + /* Not enough losses, continue sampling */ + return; + } + /* Find average delivery rate in this sampling interval. */ + interval_microsec = current_time - bbr_state->lt_last_stamp; + if (interval_microsec < 1000) { + /* Interval too small for significant measurements, wait a bit */ + return; + } + /* Compute bw in bytes per second */ + bw = (delivered * 1000000) / interval_microsec; + /* Apply the changes */ + BBRltbwIntervalDone(bbr_state, path_x, bw, current_time); +} + +/* Track the round count using the "delivered" counter. The value carried per + * packet is the delivered count when this packet was sent. If it is greater + * than next_round_delivered, it means that the packet was sent at or after + * the beginning of the round, and thus that at least one RTT has elapsed + * for this round. */ + +void BBRUpdateBtlBw(picoquic_bbr_state_t* bbr_state, picoquic_path_t* path_x, uint64_t current_time) +{ + uint64_t bandwidth_estimate = path_x->bandwidth_estimate; + + if (bbr_state->state == picoquic_bbr_alg_startup && + bandwidth_estimate < (path_x->max_bandwidth_estimate / 2)) { + bandwidth_estimate = path_x->max_bandwidth_estimate/2; + } + + if (bbr_state->rt_prop > 0) { + /* Stop the bandwidth estimate from falling too low. */ + uint64_t min_bandwidth = (((uint64_t)PICOQUIC_CWIN_MINIMUM) * 1000000) / bbr_state->rt_prop; + if (bandwidth_estimate < min_bandwidth) { + bandwidth_estimate = min_bandwidth; + } + } + + if (path_x->delivered_last_packet >= bbr_state->next_round_delivered) + { + bbr_state->next_round_delivered = path_x->delivered; + bbr_state->round_count++; + bbr_state->round_start = 1; + } + else { + bbr_state->round_start = 0; + } + + BBRltbwSampling(bbr_state, path_x, current_time); + + if (bbr_state->round_start) { + /* Forget the oldest BW round, shift by 1, compute the max BTL_BW for + * the remaining rounds, set current round max to current value */ + + bbr_state->btl_bw = 0; + + for (int i = BBR_BTL_BW_FILTER_LENGTH - 2; i >= 0; i--) { + uint64_t b = bbr_state->btl_bw_filter[i]; + bbr_state->btl_bw_filter[i + 1] = b; + if (b > bbr_state->btl_bw) { + bbr_state->btl_bw = b; + } + } + + bbr_state->btl_bw_filter[0] = 0; + } + + if (bandwidth_estimate > bbr_state->btl_bw_filter[0]) { + bbr_state->btl_bw_filter[0] =bandwidth_estimate; + if (bandwidth_estimate > bbr_state->btl_bw) { + bbr_state->btl_bw = bandwidth_estimate; + bbr_state->btl_bw_increased = 1; + } + } +} + +/* This will use one way samples if available */ +/* Should augment that with common RTT filter to suppress jitter */ +void BBRUpdateRTprop(picoquic_bbr_state_t* bbr_state, uint64_t rtt_sample, uint64_t current_time) +{ + bbr_state->rt_prop_expired = + current_time > bbr_state->rt_prop_stamp + BBR_PROBE_RTT_INTERVAL && + current_time > bbr_state->rt_prop_stamp + 20 * bbr_state->rt_prop; + if (rtt_sample <= bbr_state->rt_prop || bbr_state->rt_prop_expired) { + bbr_state->rt_prop = rtt_sample; + bbr_state->rt_prop_stamp = current_time; + } + else { + uint64_t delta = rtt_sample - bbr_state->rt_prop; + if (20 * delta < bbr_state->rt_prop) { + bbr_state->rt_prop_stamp = current_time; + } + } +} + +int BBRIsNextCyclePhase(picoquic_bbr_state_t* bbr_state, uint64_t prior_in_flight, uint64_t packets_lost, uint64_t current_time) +{ + int is_full_length = bbr_state->cycle_on_loss || (current_time - bbr_state->cycle_stamp) > bbr_state->rt_prop; + + if (bbr_state->pacing_gain != 1.0) { + if (bbr_state->pacing_gain > 1.0) { + is_full_length &= + (packets_lost > 0 || + prior_in_flight >= BBRInflight(bbr_state, bbr_state->pacing_gain)); + } + else { /* (BBR.pacing_gain < 1) */ + is_full_length &= prior_in_flight <= BBRInflight(bbr_state, 1.0); + } + } + return is_full_length; +} + +void BBRSetMinimalGain(picoquic_bbr_state_t* bbr_state) +{ + if (bbr_state->pacing_gain > 1.0 && bbr_state->rt_prop > 0) { + uint64_t target_cwin = bbr_state->btl_bw * bbr_state->rt_prop / 1000000; + + if (target_cwin < 4 * PICOQUIC_MAX_PACKET_SIZE) { + double d_target = (double)target_cwin; + double d_gain = ((double)(4 * PICOQUIC_MAX_PACKET_SIZE)) / d_target; + + if (d_gain > bbr_state->pacing_gain) { + bbr_state->pacing_gain = d_gain; + } + } + } +} + +void BBRAdvanceCyclePhase(picoquic_bbr_state_t* bbr_state, uint64_t current_time) +{ + bbr_state->cycle_on_loss = 0; + bbr_state->cycle_stamp = current_time; + bbr_state->cycle_index++; + if (bbr_state->cycle_index >= BBR_GAIN_CYCLE_LEN) { + unsigned int start = bbr_state->cycle_start; + if (bbr_state->btl_bw_increased) { + bbr_state->btl_bw_increased = 0; + start++; + if (start > BBR_GAIN_CYCLE_MAX_START) { + start = BBR_GAIN_CYCLE_MAX_START; + } + } + else if (start > 0) { + start--; + } + bbr_state->cycle_index = start; + bbr_state->cycle_start = start; + } + + bbr_state->pacing_gain = bbr_pacing_gain_cycle[bbr_state->cycle_index]; + BBRSetMinimalGain(bbr_state); +} + +void BBRCheckCyclePhase(picoquic_bbr_state_t* bbr_state, uint64_t packets_lost, uint64_t current_time) +{ + if (bbr_state->state == picoquic_bbr_alg_probe_bw && + BBRIsNextCyclePhase(bbr_state, bbr_state->prior_in_flight, packets_lost, current_time)) { + BBRAdvanceCyclePhase(bbr_state, current_time); + } +} + +static void BBRResetProbeBwMode(picoquic_bbr_state_t* bbr_state, uint64_t current_time) +{ + bbr_state->state = picoquic_bbr_alg_probe_bw; + bbr_state->cycle_index = 2; + BBRAdvanceCyclePhase(bbr_state, current_time); +} + +void BBRCheckFullPipe(picoquic_bbr_state_t* bbr_state, int rs_is_app_limited) +{ + if (!bbr_state->filled_pipe && bbr_state->round_start && !rs_is_app_limited) { + if (bbr_state->btl_bw >= bbr_state->full_bw * 1.25) { // BBR.BtlBw still growing? + bbr_state->full_bw = bbr_state->btl_bw; // record new baseline level + bbr_state->full_bw_count = 0; + } + else { + bbr_state->full_bw_count++; // another round w/o much growth + if (bbr_state->full_bw_count >= 3) { + bbr_state->filled_pipe = 1; + } + } + } +} + +void BBREnterProbeBW(picoquic_bbr_state_t* bbr_state, picoquic_path_t* path_x, uint64_t current_time) +{ + unsigned int start = 0; + bbr_state->state = picoquic_bbr_alg_probe_bw; + bbr_state->pacing_gain = 1.0; + bbr_state->cwnd_gain = 2.0; + + if (bbr_state->rt_prop > PICOQUIC_TARGET_RENO_RTT) { + uint64_t ref_rt = (bbr_state->rt_prop > PICOQUIC_TARGET_SATELLITE_RTT) ? PICOQUIC_TARGET_SATELLITE_RTT : bbr_state->rt_prop; + start = (unsigned int)(ref_rt / PICOQUIC_TARGET_RENO_RTT); + if (start > BBR_GAIN_CYCLE_MAX_START) { + start = BBR_GAIN_CYCLE_MAX_START; + } + } + else { + start = 2; + } + + bbr_state->cycle_index = start; + bbr_state->cycle_start = start; + bbr_state->btl_bw_increased = 1; + + BBRAdvanceCyclePhase(bbr_state, current_time); + /* Start sampling */ + BBRltbwSampling(bbr_state, path_x, current_time); +} + +void BBREnterDrain(picoquic_bbr_state_t* bbr_state, picoquic_path_t* path_x, uint64_t current_time) +{ + bbr_state->state = picoquic_bbr_alg_drain; + bbr_state->pacing_gain = 1.0 / BBR_HIGH_GAIN; /* pace slowly */ + bbr_state->cwnd_gain = BBR_HIGH_GAIN; /* maintain cwnd */ + /* Start sampling */ + BBRltbwSampling(bbr_state, path_x, current_time); +} + +void BBRCheckDrain(picoquic_bbr_state_t* bbr_state, picoquic_path_t* path_x, uint64_t bytes_in_transit, uint64_t current_time) +{ + if (bbr_state->state == picoquic_bbr_alg_startup && bbr_state->filled_pipe) { + BBREnterDrain(bbr_state, path_x, current_time); + } + + if (bbr_state->state == picoquic_bbr_alg_drain && bytes_in_transit <= BBRInflight(bbr_state, 1.0)) { + BBREnterProbeBW(bbr_state, path_x, current_time); /* we estimate queue is drained */ + } +} + +void BBRExitStartupLongRtt(picoquic_bbr_state_t* bbr_state, picoquic_path_t* path_x, uint64_t current_time) +{ + /* Reset the round filter so it will start at current time */ + bbr_state->next_round_delivered = path_x->delivered; + bbr_state->round_count++; + bbr_state->round_start = 1; + /* Set the filled pipe indicator */ + bbr_state->full_bw = bbr_state->btl_bw; + bbr_state->full_bw_count = 3; + bbr_state->filled_pipe = 1; + /* Check the RTT measurement for pathological cases */ + if ((bbr_state->rtt_filter.is_init || bbr_state->rtt_filter.sample_current > 0) && + bbr_state->rt_prop > 30000000 && + bbr_state->rtt_filter.sample_max < bbr_state->rt_prop) { + bbr_state->rt_prop = bbr_state->rtt_filter.sample_max; + bbr_state->rt_prop_stamp = current_time; + } + /* Enter drain */ + BBREnterDrain(bbr_state, path_x, current_time); + /* If there were just few bytes in transit, enter probe */ + if (path_x->bytes_in_transit <= BBRInflight(bbr_state, 1.0)) { + BBREnterProbeBW(bbr_state, path_x, current_time); + } +} + +void BBREnterProbeRTT(picoquic_bbr_state_t* bbr_state) +{ + bbr_state->state = picoquic_bbr_alg_probe_rtt; + bbr_state->pacing_gain = 1.0; + bbr_state->cwnd_gain = 1.0; +} + +void BBRExitProbeRTT(picoquic_bbr_state_t* bbr_state, picoquic_path_t* path_x, uint64_t current_time) +{ + if (bbr_state->filled_pipe) { + BBREnterProbeBW(bbr_state, path_x, current_time); + } + else { + BBREnterStartup(bbr_state); + } +} + +int InLossRecovery(picoquic_bbr_state_t* bbr_state) +{ + return bbr_state->packet_conservation; +} + +uint64_t BBRSaveCwnd(picoquic_bbr_state_t* bbr_state, picoquic_path_t* path_x) { + uint64_t w = path_x->cwin; + + if ((InLossRecovery(bbr_state) || bbr_state->state == picoquic_bbr_alg_probe_bw) && + (path_x->cwin < bbr_state->prior_cwnd)){ + w = bbr_state->prior_cwnd; + } + + return w; +} + +void BBRRestoreCwnd(picoquic_bbr_state_t* bbr_state, picoquic_path_t * path_x) +{ + if (path_x->cwin < bbr_state->prior_cwnd) { + path_x->cwin = bbr_state->prior_cwnd; + } +} + + +void BBRHandleProbeRTT(picoquic_bbr_state_t* bbr_state, picoquic_path_t * path_x, uint64_t bytes_in_transit, uint64_t current_time) +{ +#if 0 + /* Ignore low rate samples during ProbeRTT: */ + C.app_limited = + (BW.delivered + bytes_in_transit) ? 0 : 1; +#endif + + if (bbr_state->probe_rtt_done_stamp == 0 && + bytes_in_transit <= BBR_MIN_PIPE_CWND(path_x->send_mtu)) { + bbr_state->probe_rtt_done_stamp = + current_time + BBR_PROBE_RTT_DURATION; + bbr_state->probe_rtt_round_done = 0; + bbr_state->next_round_delivered = path_x->delivered; + } + else if (bbr_state->probe_rtt_done_stamp != 0) { + if (bbr_state->round_start) { + bbr_state->probe_rtt_round_done = 1; + } + + if (bbr_state->probe_rtt_round_done && + current_time > bbr_state->probe_rtt_done_stamp) { + bbr_state->rt_prop_stamp = current_time; + BBRRestoreCwnd(bbr_state, path_x); + BBRExitProbeRTT(bbr_state, path_x, current_time); + } + } +} + +void BBRCheckProbeRTT(picoquic_bbr_state_t* bbr_state, picoquic_path_t* path_x, uint64_t bytes_in_transit, uint64_t current_time) +{ + if (bbr_state->state != picoquic_bbr_alg_probe_rtt && + bbr_state->rt_prop_expired && + !bbr_state->idle_restart) { + BBREnterProbeRTT(bbr_state); + bbr_state->prior_cwnd = BBRSaveCwnd(bbr_state, path_x); + bbr_state->probe_rtt_done_stamp = 0; + } + + if (bbr_state->state == picoquic_bbr_alg_probe_rtt) { + BBRHandleProbeRTT(bbr_state, path_x, bytes_in_transit, current_time); + bbr_state->idle_restart = 0; + } +} + +void BBRUpdateModelAndState(picoquic_bbr_state_t* bbr_state, picoquic_path_t* path_x, + uint64_t rtt_sample, uint64_t bytes_in_transit, uint64_t packets_lost, uint64_t current_time) +{ + BBRUpdateBtlBw(bbr_state, path_x, current_time); + BBRCheckCyclePhase(bbr_state, packets_lost, current_time); + BBRCheckFullPipe(bbr_state, path_x->last_bw_estimate_path_limited); + BBRCheckDrain(bbr_state, path_x, bytes_in_transit, current_time); + BBRUpdateRTprop(bbr_state, rtt_sample, current_time); + BBRCheckProbeRTT(bbr_state, path_x, bytes_in_transit, current_time); +} + +void BBRSetPacingRateWithGain(picoquic_bbr_state_t* bbr_state, double pacing_gain) +{ + double rate = pacing_gain * (double)BBRGetBtlBW(bbr_state); + + if (bbr_state->filled_pipe || rate > bbr_state->pacing_rate){ + bbr_state->pacing_rate = rate; + } +} + +void BBRSetPacingRate(picoquic_bbr_state_t* bbr_state) +{ + BBRSetPacingRateWithGain(bbr_state, bbr_state->pacing_gain); +} + +/* TODO: clarity on bytes vs packets */ +void BBRModulateCwndForRecovery(picoquic_bbr_state_t* bbr_state, picoquic_path_t* path_x, + uint64_t bytes_in_transit, uint64_t bytes_lost, uint64_t bytes_delivered) +{ + if (bytes_lost > 0) { + if (path_x->cwin > bytes_lost) { + path_x->cwin -= bytes_lost; + } + else { + path_x->cwin = path_x->send_mtu; + } + } + if (bbr_state->packet_conservation) { + if (path_x->cwin < bytes_in_transit + bytes_delivered) { + path_x->cwin = bytes_in_transit + bytes_delivered; + } + } +} + +void BBRModulateCwndForProbeRTT(picoquic_bbr_state_t* bbr_state, picoquic_path_t* path_x) +{ + if (bbr_state->state == picoquic_bbr_alg_probe_rtt) + { + if (path_x->cwin > BBR_MIN_PIPE_CWND(path_x->send_mtu)) { + path_x->cwin = BBR_MIN_PIPE_CWND(path_x->send_mtu); + } + } +} + +void BBRSetCwnd(picoquic_bbr_state_t* bbr_state, picoquic_path_t* path_x, uint64_t bytes_in_transit, uint64_t packets_lost, uint64_t bytes_delivered) +{ + BBRUpdateTargetCwnd(bbr_state); + BBRModulateCwndForRecovery(bbr_state, path_x, bytes_in_transit, packets_lost, bytes_delivered); + if (!bbr_state->packet_conservation) { + if (bbr_state->filled_pipe) { + path_x->cwin += bytes_delivered; + if (path_x->cwin > bbr_state->target_cwnd) { + path_x->cwin = bbr_state->target_cwnd; + } + } + else if (path_x->cwin < bbr_state->target_cwnd || path_x->delivered < PICOQUIC_CWIN_INITIAL) + { + path_x->cwin += bytes_delivered; + } + if (path_x->cwin < BBR_MIN_PIPE_CWND(path_x->send_mtu)) + { + path_x->cwin = BBR_MIN_PIPE_CWND(path_x->send_mtu); + } + } + + BBRModulateCwndForProbeRTT(bbr_state, path_x); +} + + +void BBRUpdateControlParameters(picoquic_bbr_state_t* bbr_state, picoquic_path_t* path_x, uint64_t bytes_in_transit, uint64_t packets_lost, uint64_t bytes_delivered) +{ + BBRSetPacingRate(bbr_state); + BBRSetSendQuantum(bbr_state, path_x); + BBRSetCwnd(bbr_state, path_x, bytes_in_transit, packets_lost, bytes_delivered); +} + +void BBRHandleRestartFromIdle(picoquic_bbr_state_t* bbr_state, uint64_t bytes_in_transit, int is_app_limited) +{ + if (bytes_in_transit == 0 && is_app_limited) + { + bbr_state->idle_restart = 1; + if (bbr_state->state == picoquic_bbr_alg_probe_bw) { + BBRSetPacingRateWithGain(bbr_state, 1.0); + } + } +} + +/* This is the per ACK processing, activated upon receiving an ACK. + * At that point, we expect the following: + * - delivered has been updated to reflect all the data acked on the path. + * - the delivery rate sample has been computed. + */ + +void BBRUpdateOnACK(picoquic_bbr_state_t* bbr_state, picoquic_path_t* path_x, + uint64_t rtt_sample, uint64_t bytes_in_transit, uint64_t packets_lost, uint64_t bytes_delivered, + uint64_t current_time) +{ + BBRUpdateModelAndState(bbr_state, path_x, rtt_sample, bytes_in_transit, + packets_lost, current_time); + BBRUpdateControlParameters(bbr_state, path_x, bytes_in_transit, packets_lost, bytes_delivered); +} + +void BBROnTransmit(picoquic_bbr_state_t* bbr_state, uint64_t bytes_in_transit, int is_app_limited) +{ + BBRHandleRestartFromIdle(bbr_state, bytes_in_transit, is_app_limited); +} + +/* Dealing with recovery. What happens when all + * the packets are lost, when all packets have been retransmitted.. */ + +void BBROnAllPacketsLost(picoquic_bbr_state_t* bbr_state, picoquic_path_t* path_x) +{ + bbr_state->prior_cwnd = BBRSaveCwnd(bbr_state, path_x); + path_x->cwin = path_x->send_mtu; +} + +void BBROnEnterFastRecovery(picoquic_bbr_state_t* bbr_state, picoquic_path_t* path_x, uint64_t bytes_in_transit, uint64_t bytes_delivered ) +{ + if (bytes_delivered < path_x->send_mtu) { + bytes_delivered = path_x->send_mtu; + } + bbr_state->prior_cwnd = BBRSaveCwnd(bbr_state, path_x); + path_x->cwin = bytes_in_transit + bytes_delivered; + bbr_state->packet_conservation = 1; +} + +void BBRAfterOneRoundtripInFastRecovery(picoquic_bbr_state_t* bbr_state) +{ + bbr_state->packet_conservation = 0; +} + +void BBRExitFastRecovery(picoquic_bbr_state_t* bbr_state, picoquic_path_t* path_x) +{ + bbr_state->packet_conservation = 0; + BBRRestoreCwnd(bbr_state, path_x); +} + +/* Reaction to ECN or sustained losses + */ +void picoquic_bbr_notify_congestion( + picoquic_bbr_state_t* bbr_state, + picoquic_path_t* path_x, + uint64_t current_time, + int is_timeout) +{ + /* Apply filter of last loss */ + if ((bbr_state->cycle_on_loss || current_time < bbr_state->loss_interval_start + path_x->smoothed_rtt) && + (!is_timeout || bbr_state->last_loss_was_timeout)) { + /* filter repeated loss events */ + return; + } + path_x->cwin = path_x->cwin / 2; + if (is_timeout || path_x->cwin < PICOQUIC_CWIN_MINIMUM) { + path_x->cwin = PICOQUIC_CWIN_MINIMUM; + } + + bbr_state->loss_interval_start = current_time; + bbr_state->last_loss_was_timeout = is_timeout; + + /* Update and check the packet loss rate */ + if (bbr_state->state == picoquic_bbr_alg_startup_long_rtt) { + BBRExitStartupLongRtt(bbr_state, path_x, current_time); + } + else if (bbr_state->state == picoquic_bbr_alg_startup) { + bbr_state->filled_pipe = 1; + BBREnterDrain(bbr_state, path_x, current_time); + } + else { + bbr_state->cycle_on_loss = 1; + } +} + + +/* + * In order to implement BBR, we map generic congestion notification + * signals to the corresponding BBR actions. + */ +static void picoquic_bbr_notify( + picoquic_cnx_t* cnx, + picoquic_path_t* path_x, + picoquic_congestion_notification_t notification, + uint64_t rtt_measurement, + uint64_t one_way_delay, + uint64_t nb_bytes_acknowledged, + uint64_t lost_packet_number, + uint64_t current_time) +{ +#ifdef _WINDOWS + UNREFERENCED_PARAMETER(lost_packet_number); +#endif + picoquic_bbr_state_t* bbr_state = (picoquic_bbr_state_t*)path_x->congestion_alg_state; + + if (bbr_state != NULL) { + switch (notification) { + case picoquic_congestion_notification_acknowledgement: + /* sum the amount of data acked per packet */ + bbr_state->bytes_delivered += nb_bytes_acknowledged; + break; + case picoquic_congestion_notification_ecn_ec: + /* Non standard code to react on ECN_EC */ + picoquic_bbr_notify_congestion(bbr_state, path_x, current_time, 0); + break; + case picoquic_congestion_notification_repeat: + case picoquic_congestion_notification_timeout: + /* Non standard code to react to high rate of packet loss, or timeout loss */ + if (picoquic_hystart_loss_test(&bbr_state->rtt_filter, notification, lost_packet_number)) { + picoquic_bbr_notify_congestion(bbr_state, path_x, current_time, + (notification == picoquic_congestion_notification_timeout) ? 1 : 0); + } + break; + case picoquic_congestion_notification_spurious_repeat: + break; + case picoquic_congestion_notification_rtt_measurement: + if (bbr_state->state == picoquic_bbr_alg_startup && path_x->rtt_min > PICOQUIC_TARGET_RENO_RTT) { + BBREnterStartupLongRTT(bbr_state, path_x); + } + if (bbr_state->state == picoquic_bbr_alg_startup_long_rtt) { + if (picoquic_hystart_test(&bbr_state->rtt_filter, (cnx->is_time_stamp_enabled) ? one_way_delay : rtt_measurement, + cnx->path[0]->pacing_packet_time_microsec, current_time, cnx->is_time_stamp_enabled)) { + BBRExitStartupLongRtt(bbr_state, path_x, current_time); + } + } + break; + case picoquic_congestion_notification_bw_measurement: + /* RTT measurements will happen after the bandwidth is estimated */ + if (bbr_state->state == picoquic_bbr_alg_startup_long_rtt) { + uint64_t max_win; + uint64_t min_win; + + BBRUpdateBtlBw(bbr_state, path_x, current_time); + if (rtt_measurement <= bbr_state->rt_prop) { + bbr_state->rt_prop = rtt_measurement; + bbr_state->rt_prop_stamp = current_time; + } + if (path_x->last_time_acked_data_frame_sent > path_x->last_sender_limited_time) { + picoquic_hystart_increase(path_x, &bbr_state->rtt_filter, bbr_state->bytes_delivered); + } + bbr_state->bytes_delivered = 0; + + max_win = path_x->max_bandwidth_estimate * bbr_state->rt_prop / 1000000; + min_win = max_win /= 2; + + if (path_x->cwin < min_win) { + path_x->cwin =min_win; + } + + picoquic_update_pacing_data(cnx, path_x, 1); + } else { + BBRUpdateOnACK(bbr_state, path_x, + rtt_measurement, path_x->bytes_in_transit, 0 /* packets_lost */, bbr_state->bytes_delivered, + current_time); + /* Remember the number in flight before the next ACK -- TODO: update after send instead. */ + bbr_state->prior_in_flight = path_x->bytes_in_transit; + /* Reset the number of bytes delivered */ + bbr_state->bytes_delivered = 0; + + if (bbr_state->pacing_rate > 0) { + /* Set the pacing rate in picoquic sender */ + picoquic_update_pacing_rate(cnx, path_x, bbr_state->pacing_rate, bbr_state->send_quantum); + } + } + break; + case picoquic_congestion_notification_cwin_blocked: + break; + case picoquic_congestion_notification_reset: + picoquic_bbr_reset(bbr_state, path_x, current_time); + break; + default: + /* ignore */ + break; + } + } +} + +/* Observe the state of congestion control */ + +void picoquic_bbr_observe(picoquic_path_t* path_x, uint64_t* cc_state, uint64_t* cc_param) +{ + picoquic_bbr_state_t* bbr_state = (picoquic_bbr_state_t*)path_x->congestion_alg_state; + *cc_state = (uint64_t)bbr_state->state; + *cc_param = bbr_state->btl_bw; +} + +#define picoquic_bbr_ID "bbr" /* BBR */ + +picoquic_congestion_algorithm_t picoquic_bbr_algorithm_struct = { + picoquic_bbr_ID, 5, + picoquic_bbr_init, + picoquic_bbr_notify, + picoquic_bbr_delete, + picoquic_bbr_observe +}; + +picoquic_congestion_algorithm_t* picoquic_bbr_algorithm = &picoquic_bbr_algorithm_struct; \ No newline at end of file diff --git a/deps/picoquic/bytestream.c b/deps/picoquic/bytestream.c new file mode 100644 index 0000000..78884b4 --- /dev/null +++ b/deps/picoquic/bytestream.c @@ -0,0 +1,437 @@ +/* +* Author: Christian Huitema +* Copyright (c) 2019, Private Octopus, Inc. +* All rights reserved. +* +* Permission to use, copy, modify, and distribute this software for any +* purpose with or without fee is hereby granted, provided that the above +* copyright notice and this permission notice appear in all copies. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL Private Octopus, Inc. BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include + +#include "bytestream.h" +#include "picoquic_internal.h" + +static int bytestream_error(bytestream * s); + +bytestream * bytestream_ref_init(bytestream * s, const void * bytes, size_t nb_bytes) +{ + s->data = (uint8_t*)bytes; + s->size = nb_bytes; + s->ptr = 0; + + return s; +} + +bytestream * bytestream_buf_init(bytestream_buf * s, size_t nb_bytes) +{ + if (nb_bytes > BYTESTREAM_MAX_BUFFER_SIZE) { + return NULL; + } + + s->s.data = s->buf; + s->s.size = nb_bytes; + s->s.ptr = 0; + + return &s->s; +} + +bytestream * bytestream_alloc(bytestream * s, size_t nb_bytes) +{ + s->data = (uint8_t*)malloc(nb_bytes); + if (s->data == NULL) { + free(s); + return NULL; + } + s->size = nb_bytes; + s->ptr = 0; + + return s; +} + +void bytestream_delete(bytestream * s) +{ + if (s->data != NULL) { + free(s->data); + s->data = NULL; + } +} + +const uint8_t * bytestream_data(bytestream * s) +{ + return s->data; +} + +const uint8_t * bytestream_ptr(bytestream * s) +{ + return s->data + s->ptr; +} + +size_t bytestream_size(bytestream * s) +{ + return s->size; +} + +size_t bytestream_length(bytestream * s) +{ + return s->ptr; +} + +size_t bytestream_remain(bytestream * s) +{ + return s->size - s->ptr; +} + +void bytestream_reset(bytestream * s) +{ + s->ptr = 0; +} + +void bytestream_clear(bytestream * s) +{ + s->ptr = 0; + memset(s->data, 0, s->size); +} + +int bytestream_finished(bytestream * s) +{ + return s->ptr >= s->size; +} + +int bytestream_skip(bytestream * s, size_t nb_bytes) +{ + size_t max_bytes = s->size - s->ptr; + if (max_bytes < nb_bytes) { + return bytestream_error(s); + } else { + s->ptr += nb_bytes; + return 0; + } +} + +int byteread_skip_vint(bytestream * s) +{ + size_t max_bytes = s->size - s->ptr; + if (max_bytes < 1) { + return bytestream_error(s); + } + + size_t len = picoquic_decode_varint_length(s->data[s->ptr]); + return bytestream_skip(s, len); +} + +int bytewrite_vint(bytestream * s, uint64_t value) +{ + size_t len = picoquic_varint_encode(s->data + s->ptr, s->size - s->ptr, value); + if (len == 0) { + return bytestream_error(s); + } else { + s->ptr += len; + return 0; + } +} + +int byteread_vint(bytestream * s, uint64_t * value) +{ + size_t max_bytes = s->size - s->ptr; + if (max_bytes < 1) { + return bytestream_error(s); + } + + size_t len = picoquic_varint_decode(s->data + s->ptr, s->size - s->ptr, value); + if (len == 0) { + return bytestream_error(s); + } else { + s->ptr += len; + return 0; + } +} + +size_t bytestream_vint_len(uint64_t value) +{ + return picoquic_encode_varint_length(value); +} + +int byteread_vlen(bytestream * s, size_t * value) +{ + uint64_t val_read = 0; + int ret = byteread_vint(s, &val_read); + + *value = (size_t)val_read; + return *value != val_read ? -1 : ret; +} + +int bytewrite_int8(bytestream * s, uint8_t value) +{ + size_t max_bytes = s->size - s->ptr; + if (max_bytes < 1) { + return bytestream_error(s); + } else { + s->data[s->ptr++] = value; + return 0; + } +} + +int byteread_int8(bytestream * s, uint8_t * value) +{ + size_t max_bytes = s->size - s->ptr; + if (max_bytes < 1) { + return bytestream_error(s); + } else { + *value = s->data[s->ptr++]; + return 0; + } +} + +int byteshow_int8(bytestream * s, uint8_t * value) +{ + size_t max_bytes = s->size - s->ptr; + if (max_bytes < 1) { + return -1; + } else { + *value = s->data[s->ptr]; + return 0; + } +} + +int bytewrite_int16(bytestream * s, uint16_t value) +{ + size_t max_bytes = s->size - s->ptr; + if (max_bytes < 2) { + return bytestream_error(s); + } else { + picoformat_16(s->data + s->ptr, value); + s->ptr += 2; + return 0; + } +} + +int byteread_int16(bytestream * s, uint16_t * value) +{ + size_t max_bytes = s->size - s->ptr; + if (max_bytes < 2) { + return bytestream_error(s); + } + else { + const uint8_t * ptr = s->data + s->ptr; + *value = (ptr[0] << 8) | ptr[1]; + s->ptr += 2; + return 0; + } +} + +int bytewrite_int32(bytestream * s, uint32_t value) +{ + size_t max_bytes = s->size - s->ptr; + if (max_bytes < 4) { + return bytestream_error(s); + } else { + picoformat_32(s->data + s->ptr, value); + s->ptr += 4; + return 0; + } +} + +int byteread_int32(bytestream * s, uint32_t * value) +{ + size_t max_bytes = s->size - s->ptr; + if (max_bytes < 4) { + return bytestream_error(s); + } + else { + const uint8_t * ptr = s->data + s->ptr; + *value = (ptr[0] << 24) | (ptr[1] << 16) | (ptr[2] << 8) | ptr[3]; + s->ptr += 4; + return 0; + } +} + +int bytewrite_int64(bytestream * s, uint64_t value) +{ + size_t max_bytes = s->size - s->ptr; + if (max_bytes < 8) { + return bytestream_error(s); + } else { + picoformat_64(s->data + s->ptr, value); + s->ptr += 8; + return 0; + } +} + +int byteread_int64(bytestream * s, uint64_t * value) +{ + size_t max_bytes = s->size - s->ptr; + if (max_bytes < 8) { + return bytestream_error(s); + } + else { + uint64_t v = 0; + for (size_t i = 0; i < 8; i++) { + v <<= 8; + v += s->data[s->ptr++]; + } + *value = v; + return 0; + } +} + +int bytewrite_buffer(bytestream * s, const void * buffer, size_t length) +{ + size_t max_bytes = s->size - s->ptr; + if (max_bytes < length) { + return bytestream_error(s); + } + + memcpy(s->data + s->ptr, buffer, length); + s->ptr += length; + return 0; +} + +int byteread_buffer(bytestream * s, void * buffer, size_t length) +{ + size_t max_bytes = s->size - s->ptr; + if (max_bytes < length) { + return bytestream_error(s); + } + + memcpy(buffer, s->data + s->ptr, length); + s->ptr += length; + return 0; +} + +/* supplementary byte stream I/O */ + +int bytewrite_cid(bytestream * s, const picoquic_connection_id_t * cid) +{ + int ret = bytewrite_int8(s, cid->id_len); + ret |= bytewrite_buffer(s, cid->id, cid->id_len); + return ret; +} + +int byteread_cid(bytestream * s, picoquic_connection_id_t * cid) +{ + int ret = byteread_int8(s, &cid->id_len); + + if (cid->id_len > PICOQUIC_CONNECTION_ID_MAX_SIZE) { + ret = -1; + } else { + memset(cid->id, 0, sizeof(cid->id)); + ret |= byteread_buffer(s, cid->id, cid->id_len); + } + + return ret; +} + +int byteskip_cid(bytestream * s) +{ + uint8_t id_len = 0; + int ret = byteread_int8(s, &id_len); + ret |= bytestream_skip(s, id_len); + return ret; +} + +int bytewrite_cstr(bytestream * s, const char * cstr) +{ + size_t l_cstr = strlen(cstr); + int ret = bytewrite_vint(s, l_cstr); + ret |= bytewrite_buffer(s, cstr, l_cstr); + return ret; +} + +int byteread_cstr(bytestream * s, char * cstr, size_t max_len) +{ + uint64_t l_read = 0; + int ret = byteread_vint(s, &l_read); + + size_t l_cstr = (size_t)l_read; + + if (ret != 0 || l_cstr != l_read || l_cstr + 1 > max_len) { + ret = -1; + } else { + ret |= byteread_buffer(s, cstr, l_cstr); + cstr[l_cstr] = 0; + } + + return ret; +} + +int byteskip_cstr(bytestream * s) +{ + uint64_t l_read = 0; + int ret = byteread_vint(s, &l_read); + + size_t l_cstr = (size_t)l_read; + + if (ret != 0 || l_cstr != l_read) { + ret = -1; + } else { + ret = bytestream_skip(s, l_cstr); + } + return ret; +} + +int bytewrite_addr(bytestream* s, const struct sockaddr* addr) +{ + int ret = bytewrite_vint(s, addr->sa_family); + if (addr->sa_family == AF_INET) { + struct sockaddr_in* s4 = (struct sockaddr_in*)addr; + ret |= bytewrite_buffer(s, &s4->sin_addr, 4); + ret |= bytewrite_int16(s, s4->sin_port); + } else { + struct sockaddr_in6* s6 = (struct sockaddr_in6*)addr; + ret |= bytewrite_buffer(s, &s6->sin6_addr, 16); + ret |= bytewrite_int16(s, s6->sin6_port); + } + return ret; +} + +int byteread_addr(bytestream* s, struct sockaddr_storage * addr) +{ + uint64_t family = 0; + int ret = byteread_vint(s, &family); + + if (ret == 0 && family == AF_INET) { + struct sockaddr_in* s4 = (struct sockaddr_in*)addr; + s4->sin_family = AF_INET; + ret |= byteread_buffer(s, &s4->sin_addr, 4); + ret |= byteread_int16(s, &s4->sin_port); + } else { + struct sockaddr_in6* s6 = (struct sockaddr_in6*)addr; + s6->sin6_family = AF_INET6; + ret |= byteread_buffer(s, &s6->sin6_addr, 16); + ret |= byteread_int16(s, &s6->sin6_port); + } + return ret; +} + +int byteskip_addr(bytestream* s) +{ + uint64_t family = 0; + int ret = byteread_vint(s, &family); + + if (ret == 0 && family == AF_INET) { + ret |= bytestream_skip(s, 4 + 2); + } else { + ret |= bytestream_skip(s, 16 + 2); + } + return ret; +} + +static int bytestream_error(bytestream* s) +{ + s->ptr = s->size; + return -1; +} diff --git a/deps/picoquic/bytestream.h b/deps/picoquic/bytestream.h new file mode 100644 index 0000000..12926b1 --- /dev/null +++ b/deps/picoquic/bytestream.h @@ -0,0 +1,91 @@ +/* +* Author: Christian Huitema +* Copyright (c) 2019, Private Octopus, Inc. +* All rights reserved. +* +* Permission to use, copy, modify, and distribute this software for any +* purpose with or without fee is hereby granted, provided that the above +* copyright notice and this permission notice appear in all copies. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL Private Octopus, Inc. BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef PICOQUIC_BYTESTREAM_H +#define PICOQUIC_BYTESTREAM_H + +#include "picoquic_internal.h" + +#define BYTESTREAM_MAX_BUFFER_SIZE 2560 + +typedef struct { + uint8_t * data; + size_t size; + size_t ptr; +} bytestream; + +typedef struct { + bytestream s; + uint8_t buf[BYTESTREAM_MAX_BUFFER_SIZE]; +} bytestream_buf; + +bytestream * bytestream_ref_init(bytestream * s, const void * bytes, size_t nb_bytes); +bytestream * bytestream_buf_init(bytestream_buf * s, size_t nb_bytes); +bytestream * bytestream_alloc(bytestream * s, size_t nb_bytes); +void bytestream_delete(bytestream * s); +const uint8_t * bytestream_data(bytestream * s); +const uint8_t * bytestream_ptr(bytestream * s); +size_t bytestream_size(bytestream * s); +size_t bytestream_length(bytestream * s); +size_t bytestream_remain(bytestream * s); + +void bytestream_reset(bytestream * s); +void bytestream_clear(bytestream * s); +int bytestream_finished(bytestream * s); + +int bytestream_skip(bytestream * s, size_t nb_bytes); + +int bytewrite_int8(bytestream * s, uint8_t value); +int byteread_int8(bytestream * s, uint8_t * value); +int byteshow_int8(bytestream * s, uint8_t * value); + +int bytewrite_int16(bytestream * s, uint16_t value); +int byteread_int16(bytestream * s, uint16_t * value); + +int bytewrite_int32(bytestream * s, uint32_t value); +int byteread_int32(bytestream * s, uint32_t * value); + +int bytewrite_int64(bytestream * s, uint64_t value); +int byteread_int64(bytestream * s, uint64_t * value); + +int bytewrite_vint(bytestream * s, uint64_t value); +int byteread_vint(bytestream * s, uint64_t * value); +int byteread_skip_vint(bytestream * s); +size_t bytestream_vint_len(uint64_t value); + +int byteread_vlen(bytestream * s, size_t * value); + +int bytewrite_buffer(bytestream * s, const void * buffer, size_t length); +int byteread_buffer(bytestream * s, void * buffer, size_t length); + +int bytewrite_cid(bytestream * s, const picoquic_connection_id_t * cid); +int byteread_cid(bytestream * s, picoquic_connection_id_t * cid); +int byteskip_cid(bytestream * s); + +int bytewrite_cstr(bytestream* s, const char* cstr); +int byteread_cstr(bytestream* s, char* cstr, size_t max_len); +int byteskip_cstr(bytestream* s); + +int bytewrite_addr(bytestream * s, const struct sockaddr * addr); +int byteread_addr(bytestream * s, struct sockaddr_storage * addr); +int byteskip_addr(bytestream * s); + +#endif /* PICOQUIC_BYTESTREAM_H */ diff --git a/deps/picoquic/cc_common.c b/deps/picoquic/cc_common.c new file mode 100644 index 0000000..80e768c --- /dev/null +++ b/deps/picoquic/cc_common.c @@ -0,0 +1,155 @@ +/* +* Author: Christian Huitema +* Copyright (c) 2019, Private Octopus, Inc. +* All rights reserved. +* +* Permission to use, copy, modify, and distribute this software for any +* purpose with or without fee is hereby granted, provided that the above +* copyright notice and this permission notice appear in all copies. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL Private Octopus, Inc. BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "picoquic_internal.h" +#include +#include +#include "cc_common.h" + + +uint64_t picoquic_cc_get_sequence_number(picoquic_cnx_t* cnx) +{ + return cnx->pkt_ctx[picoquic_packet_context_application].send_sequence; +} + +uint64_t picoquic_cc_get_ack_number(picoquic_cnx_t* cnx) +{ + return cnx->pkt_ctx[picoquic_packet_context_application].highest_acknowledged; +} + +void picoquic_filter_rtt_min_max(picoquic_min_max_rtt_t * rtt_track, uint64_t rtt) +{ + int x = rtt_track->sample_current; + int x_max; + + + rtt_track->samples[x] = rtt; + + rtt_track->sample_current = x + 1; + if (rtt_track->sample_current >= PICOQUIC_MIN_MAX_RTT_SCOPE) { + rtt_track->is_init = 1; + rtt_track->sample_current = 0; + } + + x_max = (rtt_track->is_init) ? PICOQUIC_MIN_MAX_RTT_SCOPE : x + 1; + + rtt_track->sample_min = rtt_track->samples[0]; + rtt_track->sample_max = rtt_track->samples[0]; + + for (int i = 1; i < x_max; i++) { + if (rtt_track->samples[i] < rtt_track->sample_min) { + rtt_track->sample_min = rtt_track->samples[i]; + } else if (rtt_track->samples[i] > rtt_track->sample_max) { + rtt_track->sample_max = rtt_track->samples[i]; + } + } +} + +int picoquic_hystart_loss_test(picoquic_min_max_rtt_t* rtt_track, picoquic_congestion_notification_t event, uint64_t lost_packet_number) +{ + int ret = 0; + uint64_t next_number = rtt_track->last_lost_packet_number; + + if (lost_packet_number > next_number) { + if (next_number + PICOQUIC_SMOOTHED_LOSS_SCOPE < lost_packet_number) { + next_number = lost_packet_number - PICOQUIC_SMOOTHED_LOSS_SCOPE; + } + + while (next_number < lost_packet_number) { + rtt_track->smoothed_drop_rate *= (1.0 - PICOQUIC_SMOOTHED_LOSS_FACTOR); + next_number++; + } + + rtt_track->smoothed_drop_rate += (1.0 - rtt_track->smoothed_drop_rate) * PICOQUIC_SMOOTHED_LOSS_FACTOR; + rtt_track->last_lost_packet_number = lost_packet_number; + + switch (event) { + case picoquic_congestion_notification_repeat: + ret = rtt_track->smoothed_drop_rate > PICOQUIC_SMOOTHED_LOSS_THRESHOLD; + break; + case picoquic_congestion_notification_timeout: + ret = 1; + default: + break; + } + } + + return ret; +} + +int picoquic_hystart_test(picoquic_min_max_rtt_t* rtt_track, uint64_t rtt_measurement, uint64_t packet_time, uint64_t current_time, int is_one_way_delay_enabled) +{ + int ret = 0; + + if(current_time > rtt_track->last_rtt_sample_time + 1000) { + picoquic_filter_rtt_min_max(rtt_track, rtt_measurement); + rtt_track->last_rtt_sample_time = current_time; + + if (rtt_track->is_init) { + uint64_t delta_max; + + if (rtt_track->rtt_filtered_min == 0 || + rtt_track->rtt_filtered_min > rtt_track->sample_max) { + rtt_track->rtt_filtered_min = rtt_track->sample_max; + } + delta_max = rtt_track->rtt_filtered_min / 4; + + if (is_one_way_delay_enabled && delta_max > PICOQUIC_TARGET_RENO_RTT / 2) { + delta_max = PICOQUIC_TARGET_RENO_RTT / 2; + } + + if (rtt_track->sample_min > rtt_track->rtt_filtered_min) { + if (rtt_track->sample_min > rtt_track->rtt_filtered_min + delta_max) { + rtt_track->nb_rtt_excess++; + if (rtt_track->nb_rtt_excess >= PICOQUIC_MIN_MAX_RTT_SCOPE) { + /* RTT increased too much, get out of slow start! */ + ret = 1; + } + } + } + else { + rtt_track->nb_rtt_excess = 0; + } + } + } + + return ret; +} + +void picoquic_hystart_increase(picoquic_path_t * path_x, picoquic_min_max_rtt_t* rtt_filter, uint64_t nb_delivered) +{ + path_x->cwin += nb_delivered; +} + +uint64_t picoquic_cc_increased_window(picoquic_cnx_t* cnx, uint64_t previous_window) +{ + uint64_t new_window; + if (cnx->path[0]->rtt_min <= PICOQUIC_TARGET_RENO_RTT) { + new_window = previous_window * 2; + } + else { + double w = (double)previous_window; + w /= (double)PICOQUIC_TARGET_RENO_RTT; + w *= (cnx->path[0]->rtt_min > PICOQUIC_TARGET_SATELLITE_RTT)? PICOQUIC_TARGET_SATELLITE_RTT: cnx->path[0]->rtt_min; + new_window = (uint64_t)w; + } + return new_window; +} \ No newline at end of file diff --git a/deps/picoquic/cc_common.h b/deps/picoquic/cc_common.h new file mode 100644 index 0000000..8ad5382 --- /dev/null +++ b/deps/picoquic/cc_common.h @@ -0,0 +1,86 @@ +/* +* Author: Christian Huitema +* Copyright (c) 2019, Private Octopus, Inc. +* All rights reserved. +* +* Permission to use, copy, modify, and distribute this software for any +* purpose with or without fee is hereby granted, provided that the above +* copyright notice and this permission notice appear in all copies. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL Private Octopus, Inc. BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef CC_COMMON_H +#define CC_COMMON_H + +#define PICOQUIC_MIN_MAX_RTT_SCOPE 7 +#define PICOQUIC_SMOOTHED_LOSS_SCOPE 32 +#define PICOQUIC_SMOOTHED_LOSS_FACTOR (1.0/16.0) +#define PICOQUIC_SMOOTHED_LOSS_THRESHOLD (0.2) + +typedef struct st_picoquic_min_max_rtt_t { + uint64_t last_rtt_sample_time; + uint64_t rtt_filtered_min; + int nb_rtt_excess; + int sample_current; + int is_init; + double smoothed_drop_rate; + uint64_t last_lost_packet_number; + uint64_t sample_min; + uint64_t sample_max; + uint64_t samples[PICOQUIC_MIN_MAX_RTT_SCOPE]; +} picoquic_min_max_rtt_t; + +uint64_t picoquic_cc_get_sequence_number(picoquic_cnx_t* cnx); + +uint64_t picoquic_cc_get_ack_number(picoquic_cnx_t* cnx); + +void picoquic_filter_rtt_min_max(picoquic_min_max_rtt_t* rtt_track, uint64_t rtt); + +int picoquic_hystart_loss_test(picoquic_min_max_rtt_t* rtt_track, picoquic_congestion_notification_t event, uint64_t lost_packet_number); + +int picoquic_hystart_test(picoquic_min_max_rtt_t* rtt_track, uint64_t rtt_measurement, uint64_t packet_time, uint64_t current_time, int is_one_way_delay_enabled); + +void picoquic_hystart_increase(picoquic_path_t* path_x, picoquic_min_max_rtt_t* rtt_filter, uint64_t nb_delivered); + +/* Many congestion control algorithms run a parallel version of new reno in order + * to provide a lower bound estimate of either the congestion window or the + * the minimal bandwidth. This implementation of new reno does not directly + * refer to the connection and path variables (e.g. cwin) but instead sets + * its entire state in memory. + */ + +typedef enum { + picoquic_newreno_alg_slow_start = 0, + picoquic_newreno_alg_congestion_avoidance +} picoquic_newreno_alg_state_t; + +typedef struct st_picoquic_newreno_sim_state_t { + picoquic_newreno_alg_state_t alg_state; + uint64_t cwin; + uint64_t residual_ack; + uint64_t ssthresh; + uint64_t recovery_start; + uint64_t recovery_sequence; +} picoquic_newreno_sim_state_t; + +void picoquic_newreno_sim_reset(picoquic_newreno_sim_state_t* nrss); + +void picoquic_newreno_sim_notify( + picoquic_newreno_sim_state_t* nr_state, + picoquic_cnx_t* cnx, + picoquic_path_t* path_x, + picoquic_congestion_notification_t notification, + uint64_t nb_bytes_acknowledged, + uint64_t current_time); + +#endif \ No newline at end of file diff --git a/deps/picoquic/cidset.h b/deps/picoquic/cidset.h new file mode 100644 index 0000000..853683d --- /dev/null +++ b/deps/picoquic/cidset.h @@ -0,0 +1,58 @@ +/* +* Author: Christian Huitema +* Copyright (c) 2019, Private Octopus, Inc. +* All rights reserved. +* +* Permission to use, copy, modify, and distribute this software for any +* purpose with or without fee is hereby granted, provided that the above +* copyright notice and this permission notice appear in all copies. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL Private Octopus, Inc. BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#include +#include "picoquic_internal.h" +#include "picohash.h" + +picohash_table * cidset_create(); +picohash_table * cidset_delete(picohash_table * cids); + +/*! \brief Insert connection id \a cid into a set of connection ids \a cids if + * \a cids doesn't contain \a cid already. + * \return 0 if successful, -1 otherwise. + */ +int cidset_insert(picohash_table * cids, const picoquic_connection_id_t * cid); + +/*! \brief Return 1 if a given set of connection ids \a cids contains a specific + * connection id \cid, return 0 otherwise. + */ +int cidset_has_cid(picohash_table * cids, const picoquic_connection_id_t * cid); + +/*! \brief Call the given callback once for every connection id in the given + * set of connection ids. + * + * \param cids Set of connection ids to iterate. + * \param cb Callback to call with each connection id. + * \param cbptr A caller provided context pointer that is passed through + * to the callback. + * + * \return 0 if successfully iterated through all items, otherwise return + * the return value of the failing callback. + */ +int cidset_iterate(const picohash_table * cids, int(*cb)(const picoquic_connection_id_t *, void *), void * cbptr); + +/*! \brief Convert all connection ids as hexadecimal values into strings + * and print them into the provided file. + * + * \param f The file handle of the opened output stream. + * \param cids Set of connection ids to print. + */ +void cidset_print(FILE * f, picohash_table * cids); diff --git a/deps/picoquic/cubic.c b/deps/picoquic/cubic.c new file mode 100644 index 0000000..b1343a7 --- /dev/null +++ b/deps/picoquic/cubic.c @@ -0,0 +1,685 @@ +/* +* Author: Christian Huitema +* Copyright (c) 2019, Private Octopus, Inc. +* All rights reserved. +* +* Permission to use, copy, modify, and distribute this software for any +* purpose with or without fee is hereby granted, provided that the above +* copyright notice and this permission notice appear in all copies. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL Private Octopus, Inc. BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "picoquic_internal.h" +#include +#include +#include "cc_common.h" + +typedef enum { + picoquic_cubic_alg_slow_start = 0, + picoquic_cubic_alg_recovery, + picoquic_cubic_alg_congestion_avoidance +} picoquic_cubic_alg_state_t; + +typedef struct st_picoquic_cubic_state_t { + picoquic_cubic_alg_state_t alg_state; + uint64_t recovery_sequence; + uint64_t start_of_epoch; + uint64_t previous_start_of_epoch; + double K; + double W_max; + double W_last_max; + double C; + double beta; + double W_reno; + uint64_t ssthresh; + picoquic_min_max_rtt_t rtt_filter; +} picoquic_cubic_state_t; + +static void picoquic_cubic_reset(picoquic_cubic_state_t* cubic_state, picoquic_path_t* path_x, uint64_t current_time) { + memset(&cubic_state->rtt_filter, 0, sizeof(picoquic_min_max_rtt_t)); + memset(cubic_state, 0, sizeof(picoquic_cubic_state_t)); + cubic_state->alg_state = picoquic_cubic_alg_slow_start; + cubic_state->ssthresh = UINT64_MAX; + cubic_state->W_last_max = (double)cubic_state->ssthresh / (double)path_x->send_mtu; + cubic_state->W_max = cubic_state->W_last_max; + cubic_state->C = 0.4; + cubic_state->beta = 7.0 / 8.0; + cubic_state->start_of_epoch = current_time; + cubic_state->previous_start_of_epoch = 0; + cubic_state->W_reno = PICOQUIC_CWIN_INITIAL; + cubic_state->recovery_sequence = 0; + path_x->cwin = PICOQUIC_CWIN_INITIAL; +} + +static void picoquic_cubic_init(picoquic_path_t* path_x, uint64_t current_time) +{ + /* Initialize the state of the congestion control algorithm */ + picoquic_cubic_state_t* cubic_state = (picoquic_cubic_state_t*)malloc(sizeof(picoquic_cubic_state_t)); + path_x->congestion_alg_state = (void*)cubic_state; + if (cubic_state != NULL) { + picoquic_cubic_reset(cubic_state, path_x, current_time); + } +} + +static double picoquic_cubic_root(double x) +{ + /* First find an approximation */ + double v = 1; + double y = 1.0; + double y2; + double y3; + + while (v > x * 8) { + v /= 8; + y /= 2; + } + + while (v < x) { + v *= 8; + y *= 2; + } + + for (int i = 0; i < 3; i++) { + y2 = y * y; + y3 = y2 * y; + y += (x - y3) / (3.0*y2); + } + + return y; +} + +/* Compute W_cubic(t) = C * (t - K) ^ 3 + W_max */ +static double picoquic_cubic_W_cubic( + picoquic_cubic_state_t* cubic_state, + uint64_t current_time) +{ + double delta_t_sec = ((double)(current_time - cubic_state->start_of_epoch) / 1000000.0) - cubic_state->K; + double W_cubic = (cubic_state->C * (delta_t_sec * delta_t_sec * delta_t_sec)) + cubic_state->W_max; + + return W_cubic; +} + +/* On entering congestion avoidance, need to compute the new coefficients + * of the cubit curve */ +static void picoquic_cubic_enter_avoidance( + picoquic_cubic_state_t* cubic_state, + uint64_t current_time) +{ + cubic_state->K = picoquic_cubic_root(cubic_state->W_max*(1.0 - cubic_state->beta) / cubic_state->C); + cubic_state->alg_state = picoquic_cubic_alg_congestion_avoidance; + cubic_state->start_of_epoch = current_time; + cubic_state->previous_start_of_epoch = cubic_state->start_of_epoch; +} + +/* The recovery state last 1 RTT, during which parameters will be frozen + */ +static void picoquic_cubic_enter_recovery(picoquic_cnx_t * cnx, + picoquic_path_t* path_x, + picoquic_congestion_notification_t notification, + picoquic_cubic_state_t* cubic_state, + uint64_t current_time) +{ + cubic_state->recovery_sequence = picoquic_cc_get_sequence_number(cnx); + /* Update similar to new reno, but different beta */ + cubic_state->W_max = (double)path_x->cwin / (double)path_x->send_mtu; + /* Apply fast convergence */ + if (cubic_state->W_max < cubic_state->W_last_max) { + cubic_state->W_last_max = cubic_state->W_max; + cubic_state->W_max = cubic_state->W_max *cubic_state->beta; + } + else { + cubic_state->W_last_max = cubic_state->W_max; + } + /* Compute the new ssthresh */ + cubic_state->ssthresh = (uint64_t)(cubic_state->W_max * cubic_state->beta*(double)path_x->send_mtu); + if (cubic_state->ssthresh < PICOQUIC_CWIN_MINIMUM) { + /* If things are that bad, fall back to slow start */ + cubic_state->ssthresh = PICOQUIC_CWIN_MINIMUM; + cubic_state->alg_state = picoquic_cubic_alg_slow_start; + cubic_state->ssthresh = UINT64_MAX; + cubic_state->previous_start_of_epoch = cubic_state->start_of_epoch; + cubic_state->start_of_epoch = current_time; + cubic_state->W_reno = PICOQUIC_CWIN_MINIMUM; + path_x->cwin = PICOQUIC_CWIN_MINIMUM; + } + else { + + if (notification == picoquic_congestion_notification_timeout) { + path_x->cwin = PICOQUIC_CWIN_MINIMUM; + cubic_state->previous_start_of_epoch = cubic_state->start_of_epoch; + cubic_state->start_of_epoch = current_time; + cubic_state->alg_state = picoquic_cubic_alg_slow_start; + } + else { + /* Enter congestion avoidance immediately */ + picoquic_cubic_enter_avoidance(cubic_state, current_time); + /* Compute the inital window for both Reno and Cubic */ + double W_cubic = picoquic_cubic_W_cubic(cubic_state, current_time); + uint64_t win_cubic = (uint64_t)(W_cubic * (double)path_x->send_mtu); + cubic_state->W_reno = ((double)path_x->cwin) / 2.0; + + /* Pick the largest */ + if (win_cubic > cubic_state->W_reno) { + /* if cubic is larger than threshold, switch to cubic mode */ + path_x->cwin = win_cubic; + } + else { + path_x->cwin = (uint64_t)cubic_state->W_reno; + } + } + } +} + +/* On spurious repeat notification, restore the previous congestion control. + * Assume that K is still valid -- we only update it after exiting recovery. + * Set cwin to the value of W_max before the recovery event + * Set W_max to W_max_last, i.e. the value before the recovery event + * Set the epoch back to where it was, by computing the inverse of the + * W_cubic formula */ +static void picoquic_cubic_correct_spurious(picoquic_path_t* path_x, + picoquic_cubic_state_t* cubic_state, + uint64_t current_time) +{ + if (cubic_state->ssthresh != UINT64_MAX) { + cubic_state->W_max = cubic_state->W_last_max; + picoquic_cubic_enter_avoidance(cubic_state, cubic_state->previous_start_of_epoch); + double W_cubic = picoquic_cubic_W_cubic(cubic_state, current_time); + cubic_state->W_reno = W_cubic * (double)path_x->send_mtu; + cubic_state->ssthresh = (uint64_t)(cubic_state->W_max * cubic_state->beta * (double)path_x->send_mtu); + path_x->cwin = (uint64_t)cubic_state->W_reno; + } +} + +/* + * Properly implementing Cubic requires managing a number of + * signals, such as packet losses or acknowledgements. We attempt + * to condensate all that in a single API, which could be shared + * by many different congestion control algorithms. + */ +static void picoquic_cubic_notify( + picoquic_cnx_t* cnx, picoquic_path_t* path_x, + picoquic_congestion_notification_t notification, + uint64_t rtt_measurement, + uint64_t one_way_delay, + uint64_t nb_bytes_acknowledged, + uint64_t lost_packet_number, + uint64_t current_time) +{ +#ifdef _WINDOWS + UNREFERENCED_PARAMETER(rtt_measurement); + UNREFERENCED_PARAMETER(lost_packet_number); +#endif + picoquic_cubic_state_t* cubic_state = (picoquic_cubic_state_t*)path_x->congestion_alg_state; + + if (cubic_state != NULL) { + switch (cubic_state->alg_state) { + case picoquic_cubic_alg_slow_start: + switch (notification) { + case picoquic_congestion_notification_acknowledgement: + if (path_x->last_time_acked_data_frame_sent > path_x->last_sender_limited_time) { + picoquic_hystart_increase(path_x, &cubic_state->rtt_filter, nb_bytes_acknowledged); + /* if cnx->cwin exceeds SSTHRESH, exit and go to CA */ + if (path_x->cwin >= cubic_state->ssthresh) { + cubic_state->W_reno = ((double)path_x->cwin) / 2.0; + picoquic_cubic_enter_avoidance(cubic_state, current_time); + } + } + break; + case picoquic_congestion_notification_ecn_ec: + case picoquic_congestion_notification_repeat: + case picoquic_congestion_notification_timeout: + /* enter recovery */ + if (current_time - cubic_state->start_of_epoch > path_x->smoothed_rtt || + cubic_state->recovery_sequence <= picoquic_cc_get_ack_number(cnx)) { + picoquic_cubic_enter_recovery(cnx, path_x, notification, cubic_state, current_time); + } + break; + case picoquic_congestion_notification_spurious_repeat: + /* Reset CWIN based on ssthresh, not based on current value. */ + picoquic_cubic_correct_spurious(path_x, cubic_state, current_time); + break; + case picoquic_congestion_notification_rtt_measurement: + /* Using RTT increases as signal to get out of initial slow start */ + if (cubic_state->ssthresh == UINT64_MAX && + picoquic_hystart_test(&cubic_state->rtt_filter, (cnx->is_time_stamp_enabled) ? one_way_delay : rtt_measurement, + cnx->path[0]->pacing_packet_time_microsec, current_time, cnx->is_time_stamp_enabled)) { + /* RTT increased too much, get out of slow start! */ + if (cubic_state->rtt_filter.rtt_filtered_min > PICOQUIC_TARGET_RENO_RTT){ + double correction; + if (cubic_state->rtt_filter.rtt_filtered_min > PICOQUIC_TARGET_SATELLITE_RTT) { + correction = (double)PICOQUIC_TARGET_SATELLITE_RTT / (double)cubic_state->rtt_filter.rtt_filtered_min; + } + else { + correction = (double)PICOQUIC_TARGET_RENO_RTT / (double)cubic_state->rtt_filter.rtt_filtered_min; + } + uint64_t base_window = (uint64_t)(correction * (double)path_x->cwin); + uint64_t delta_window = path_x->cwin - base_window; + path_x->cwin -= (delta_window / 2); + } + cubic_state->ssthresh = path_x->cwin; + cubic_state->W_max = (double)path_x->cwin / (double)path_x->send_mtu; + cubic_state->W_last_max = cubic_state->W_max; + cubic_state->W_reno = ((double)path_x->cwin); + picoquic_cubic_enter_avoidance(cubic_state, current_time); + /* apply a correction to enter the test phase immediately */ + uint64_t K_micro = (uint64_t)(cubic_state->K * 1000000.0); + if (K_micro > current_time) { + cubic_state->K = ((double)current_time) / 1000000.0; + cubic_state->start_of_epoch = 0; + } + else { + cubic_state->start_of_epoch = current_time - K_micro; + } + } + + break; + case picoquic_congestion_notification_cwin_blocked: + break; + case picoquic_congestion_notification_bw_measurement: { + /* RTT measurements will happen after the bandwidth is estimated */ + uint64_t max_win = path_x->max_bandwidth_estimate * path_x->smoothed_rtt / 1000000; + uint64_t min_win = max_win /= 2; + if (path_x->cwin < min_win) { + path_x->cwin = min_win; + } + break; + } + case picoquic_congestion_notification_reset: + picoquic_cubic_reset(cubic_state, path_x, current_time); + break; + default: + break; + } + break; + case picoquic_cubic_alg_recovery: + /* If the congestion notification is coming less than 1RTT after start, + * ignore it, unless it is a spurious retransmit detection */ + switch (notification) { + case picoquic_congestion_notification_acknowledgement: + /* exit recovery, move to CA or SS, depending on CWIN */ + cubic_state->alg_state = picoquic_cubic_alg_slow_start; + path_x->cwin += nb_bytes_acknowledged; + /* if cnx->cwin exceeds SSTHRESH, exit and go to CA */ + if (path_x->cwin >= cubic_state->ssthresh) { + cubic_state->alg_state = picoquic_cubic_alg_congestion_avoidance; + } + break; + case picoquic_congestion_notification_spurious_repeat: + picoquic_cubic_correct_spurious(path_x, cubic_state, current_time); + break; + case picoquic_congestion_notification_ecn_ec: + case picoquic_congestion_notification_repeat: + case picoquic_congestion_notification_timeout: + if (current_time - cubic_state->start_of_epoch > path_x->smoothed_rtt || + cubic_state->recovery_sequence <= picoquic_cc_get_ack_number(cnx)) { + /* re-enter recovery if this is a new loss */ + picoquic_cubic_enter_recovery(cnx, path_x, notification, cubic_state, current_time); + } + break; + case picoquic_congestion_notification_rtt_measurement: + case picoquic_congestion_notification_cwin_blocked: + break; + case picoquic_congestion_notification_reset: + picoquic_cubic_reset(cubic_state, path_x, current_time); + break; + default: + /* ignore */ + break; + } + break; + case picoquic_cubic_alg_congestion_avoidance: + switch (notification) { + case picoquic_congestion_notification_acknowledgement: + if (path_x->last_time_acked_data_frame_sent > path_x->last_sender_limited_time) { + double W_cubic; + uint64_t win_cubic; + /* Protection against limited senders. */ + if (cubic_state->start_of_epoch < path_x->last_sender_limited_time) { + cubic_state->start_of_epoch = path_x->last_sender_limited_time; + } + /* Compute the cubic formula */ + W_cubic = picoquic_cubic_W_cubic(cubic_state, current_time); + win_cubic = (uint64_t)(W_cubic * (double)path_x->send_mtu); + /* Also compute the Reno formula */ + cubic_state->W_reno += ((double)nb_bytes_acknowledged) * ((double)path_x->send_mtu) / cubic_state->W_reno; + + /* Pick the largest */ + if (win_cubic > cubic_state->W_reno) { + /* if cubic is larger than threshold, switch to cubic mode */ + path_x->cwin = win_cubic; + } + else { + path_x->cwin = (uint64_t)cubic_state->W_reno; + } + } + break; + case picoquic_congestion_notification_ecn_ec: + case picoquic_congestion_notification_repeat: + case picoquic_congestion_notification_timeout: + if (current_time - cubic_state->start_of_epoch > path_x->smoothed_rtt || + cubic_state->recovery_sequence <= picoquic_cc_get_ack_number(cnx)) { + /* re-enter recovery */ + picoquic_cubic_enter_recovery(cnx, path_x, notification, cubic_state, current_time); + } + break; + case picoquic_congestion_notification_spurious_repeat: + picoquic_cubic_correct_spurious(path_x, cubic_state, current_time); + break; + case picoquic_congestion_notification_cwin_blocked: + break; + case picoquic_congestion_notification_rtt_measurement: + break; + case picoquic_congestion_notification_reset: + picoquic_cubic_reset(cubic_state, path_x, current_time); + break; + default: + /* ignore */ + break; + } + break; + default: + break; + } + + /* Compute pacing data */ + picoquic_update_pacing_data(cnx, path_x, cubic_state->alg_state == picoquic_cubic_alg_slow_start && + cubic_state->ssthresh == UINT64_MAX); + } +} +/* Exit slow start on either long delay of high loss + */ +static void dcubic_exit_slow_start( + picoquic_cnx_t* cnx, picoquic_path_t* path_x, + picoquic_congestion_notification_t notification, + picoquic_cubic_state_t* cubic_state, + uint64_t current_time) +{ + if (cubic_state->ssthresh == UINT64_MAX) { + cubic_state->ssthresh = path_x->cwin; + cubic_state->W_max = (double)path_x->cwin / (double)path_x->send_mtu; + cubic_state->W_last_max = cubic_state->W_max; + cubic_state->W_reno = ((double)path_x->cwin); + picoquic_cubic_enter_avoidance(cubic_state, current_time); + /* apply a correction to enter the test phase immediately */ + uint64_t K_micro = (uint64_t)(cubic_state->K * 1000000.0); + if (K_micro > current_time) { + cubic_state->K = ((double)current_time) / 1000000.0; + cubic_state->start_of_epoch = 0; + } + else { + cubic_state->start_of_epoch = current_time - K_micro; + } + } + else { + if (current_time - cubic_state->start_of_epoch > path_x->smoothed_rtt || + cubic_state->recovery_sequence <= picoquic_cc_get_ack_number(cnx)) { + /* re-enter recovery if this is a new event */ + picoquic_cubic_enter_recovery(cnx, path_x, notification, cubic_state, current_time); + } + } +} + +/* + * Define delay-based Cubic, dcubic, and alternative congestion control protocol similar to Cubic but + * using delay measurements instead of reacting to packet losses. This is a quic hack, intended for + * trials of a lossy satellite networks. + */ +static void picoquic_dcubic_notify( + picoquic_cnx_t* cnx, picoquic_path_t* path_x, + picoquic_congestion_notification_t notification, + uint64_t rtt_measurement, + uint64_t one_way_delay, + uint64_t nb_bytes_acknowledged, + uint64_t lost_packet_number, + uint64_t current_time) +{ +#ifdef _WINDOWS + UNREFERENCED_PARAMETER(rtt_measurement); + UNREFERENCED_PARAMETER(lost_packet_number); +#endif + picoquic_cubic_state_t* cubic_state = (picoquic_cubic_state_t*)path_x->congestion_alg_state; + + if (cubic_state != NULL) { + switch (cubic_state->alg_state) { + case picoquic_cubic_alg_slow_start: + switch (notification) { + case picoquic_congestion_notification_acknowledgement: + /* Same as Cubic */ + if (path_x->last_time_acked_data_frame_sent > path_x->last_sender_limited_time) { + picoquic_hystart_increase(path_x, &cubic_state->rtt_filter, nb_bytes_acknowledged); + /* if cnx->cwin exceeds SSTHRESH, exit and go to CA */ + if (path_x->cwin >= cubic_state->ssthresh) { + cubic_state->W_reno = ((double)path_x->cwin) / 2.0; + picoquic_cubic_enter_avoidance(cubic_state, current_time); + } + } + break; + case picoquic_congestion_notification_ecn_ec: + /* In contrast to Cubic, do nothing here */ + break; + case picoquic_congestion_notification_repeat: + case picoquic_congestion_notification_timeout: + /* In contrast to Cubic, only exit on high losses */ + if (picoquic_hystart_loss_test(&cubic_state->rtt_filter, notification, lost_packet_number)) { + dcubic_exit_slow_start(cnx, path_x, notification, cubic_state, current_time); + } + break; + case picoquic_congestion_notification_spurious_repeat: + /* Unlike Cubic, losses have no effect so do nothing here */ + break; + case picoquic_congestion_notification_rtt_measurement: + /* if in slow start, increase the window for long delay RTT */ + if (path_x->rtt_min > PICOQUIC_TARGET_RENO_RTT && cubic_state->ssthresh == UINT64_MAX) { + uint64_t min_cwnd; + + if (path_x->rtt_min > PICOQUIC_TARGET_SATELLITE_RTT) { + min_cwnd = (uint64_t)((double)PICOQUIC_CWIN_INITIAL * (double)PICOQUIC_TARGET_SATELLITE_RTT / (double)PICOQUIC_TARGET_RENO_RTT); + } + else { + min_cwnd = (uint64_t)((double)PICOQUIC_CWIN_INITIAL * (double)path_x->rtt_min / (double)PICOQUIC_TARGET_RENO_RTT); + } + + if (min_cwnd > path_x->cwin) { + path_x->cwin = min_cwnd; + } + } + + /* Using RTT increases as congestion signal. This is used + * for getting out of slow start, but also for ending a cycle + * during congestion avoidance */ + if (picoquic_hystart_test(&cubic_state->rtt_filter, (cnx->is_time_stamp_enabled) ? one_way_delay : rtt_measurement, + cnx->path[0]->pacing_packet_time_microsec, current_time, cnx->is_time_stamp_enabled)) { + dcubic_exit_slow_start(cnx, path_x, notification, cubic_state, current_time); + } + break; + case picoquic_congestion_notification_cwin_blocked: + break; + case picoquic_congestion_notification_reset: + picoquic_cubic_reset(cubic_state, path_x, current_time); + break; + default: + /* ignore */ + break; + } + break; + case picoquic_cubic_alg_recovery: + /* If the notification is coming less than 1RTT after start, + * ignore it, unless it is a spurious retransmit detection */ + switch (notification) { + case picoquic_congestion_notification_acknowledgement: + /* exit recovery, move to CA or SS, depending on CWIN */ + cubic_state->alg_state = picoquic_cubic_alg_slow_start; + path_x->cwin += nb_bytes_acknowledged; + /* if cnx->cwin exceeds SSTHRESH, exit and go to CA */ + if (path_x->cwin >= cubic_state->ssthresh) { + cubic_state->alg_state = picoquic_cubic_alg_congestion_avoidance; + } + break; + case picoquic_congestion_notification_spurious_repeat: + /* DO nothing */ + break; + case picoquic_congestion_notification_ecn_ec: + case picoquic_congestion_notification_repeat: + case picoquic_congestion_notification_timeout: + /* do nothing */ + case picoquic_congestion_notification_rtt_measurement: + /* if in slow start, increase the window for long delay RTT */ + if (path_x->rtt_min > PICOQUIC_TARGET_RENO_RTT && cubic_state->ssthresh == UINT64_MAX) { + uint64_t min_cwnd; + + if (path_x->rtt_min > PICOQUIC_TARGET_SATELLITE_RTT) { + min_cwnd = (uint64_t)((double)PICOQUIC_CWIN_INITIAL * (double)PICOQUIC_TARGET_SATELLITE_RTT / (double)PICOQUIC_TARGET_RENO_RTT); + } + else { + min_cwnd = (uint64_t)((double)PICOQUIC_CWIN_INITIAL * (double)path_x->rtt_min / (double)PICOQUIC_TARGET_RENO_RTT); + } + + if (min_cwnd > path_x->cwin) { + path_x->cwin = min_cwnd; + } + } + + if (picoquic_hystart_test(&cubic_state->rtt_filter, (cnx->is_time_stamp_enabled) ? one_way_delay : rtt_measurement, + cnx->path[0]->pacing_packet_time_microsec, current_time, cnx->is_time_stamp_enabled)) { + if (current_time - cubic_state->start_of_epoch > path_x->smoothed_rtt || + cubic_state->recovery_sequence <= picoquic_cc_get_ack_number(cnx)) { + /* re-enter recovery if this is a new event */ + picoquic_cubic_enter_recovery(cnx, path_x, notification, cubic_state, current_time); + } + } + break; + case picoquic_congestion_notification_cwin_blocked: + break; + case picoquic_congestion_notification_reset: + picoquic_cubic_reset(cubic_state, path_x, current_time); + break; + default: + /* ignore */ + break; + } + break; + + case picoquic_cubic_alg_congestion_avoidance: + switch (notification) { + case picoquic_congestion_notification_acknowledgement: + if (path_x->last_time_acked_data_frame_sent > path_x->last_sender_limited_time) { + double W_cubic; + uint64_t win_cubic; + /* Protection against limited senders. */ + if (cubic_state->start_of_epoch < path_x->last_sender_limited_time) { + cubic_state->start_of_epoch = path_x->last_sender_limited_time; + } + /* Compute the cubic formula */ + W_cubic = picoquic_cubic_W_cubic(cubic_state, current_time); + win_cubic = (uint64_t)(W_cubic * (double)path_x->send_mtu); + /* Also compute the Reno formula */ + cubic_state->W_reno += ((double)nb_bytes_acknowledged) * ((double)path_x->send_mtu) / cubic_state->W_reno; + + /* Pick the largest */ + if (win_cubic > cubic_state->W_reno) { + /* if cubic is larger than threshold, switch to cubic mode */ + path_x->cwin = win_cubic; + } + else { + path_x->cwin = (uint64_t)cubic_state->W_reno; + } + } + break; + case picoquic_congestion_notification_ecn_ec: + /* Do nothing */ + break; + case picoquic_congestion_notification_repeat: + case picoquic_congestion_notification_timeout: + /* In contrast to Cubic, only exit on high losses */ + if (picoquic_hystart_loss_test(&cubic_state->rtt_filter, notification, lost_packet_number) && + lost_packet_number > cubic_state->recovery_sequence) { + /* re-enter recovery */ + picoquic_cubic_enter_recovery(cnx, path_x, notification, cubic_state, current_time); + } + break; + case picoquic_congestion_notification_spurious_repeat: + /* Do nothing */ + break; + case picoquic_congestion_notification_cwin_blocked: + break; + case picoquic_congestion_notification_rtt_measurement: + if (picoquic_hystart_test(&cubic_state->rtt_filter, (cnx->is_time_stamp_enabled) ? one_way_delay : rtt_measurement, + cnx->path[0]->pacing_packet_time_microsec, current_time, cnx->is_time_stamp_enabled)) { + if (current_time - cubic_state->start_of_epoch > path_x->smoothed_rtt || + cubic_state->recovery_sequence <= picoquic_cc_get_ack_number(cnx)) { + /* re-enter recovery */ + picoquic_cubic_enter_recovery(cnx, path_x, notification, cubic_state, current_time); + } + } + break; + case picoquic_congestion_notification_reset: + picoquic_cubic_reset(cubic_state, path_x, current_time); + break; + default: + /* ignore */ + break; + } + break; + default: + break; + } + + /* Compute pacing data */ + picoquic_update_pacing_data(cnx, path_x, + cubic_state->alg_state == picoquic_cubic_alg_slow_start && cubic_state->ssthresh == UINT64_MAX); + } +} + + +/* Release the state of the congestion control algorithm */ +static void picoquic_cubic_delete(picoquic_path_t* path_x) +{ + if (path_x->congestion_alg_state != NULL) { + free(path_x->congestion_alg_state); + path_x->congestion_alg_state = NULL; + } +} + +/* Observe the state of congestion control */ + +void picoquic_cubic_observe(picoquic_path_t* path_x, uint64_t* cc_state, uint64_t* cc_param) +{ + picoquic_cubic_state_t* cubic_state = (picoquic_cubic_state_t*)path_x->congestion_alg_state; + *cc_state = (uint64_t)cubic_state->alg_state; + *cc_param = (uint64_t)cubic_state->W_max; +} + + +/* Definition record for the Cubic algorithm */ + +#define picoquic_cubic_ID "cubic" /* CBIC */ +#define picoquic_dcubic_ID "dcubic" /* DBIC */ + +picoquic_congestion_algorithm_t picoquic_cubic_algorithm_struct = { + picoquic_cubic_ID, 2, + picoquic_cubic_init, + picoquic_cubic_notify, + picoquic_cubic_delete, + picoquic_cubic_observe +}; + +picoquic_congestion_algorithm_t picoquic_dcubic_algorithm_struct = { + picoquic_dcubic_ID, 3, + picoquic_cubic_init, + picoquic_dcubic_notify, + picoquic_cubic_delete, + picoquic_cubic_observe +}; + +picoquic_congestion_algorithm_t* picoquic_cubic_algorithm = &picoquic_cubic_algorithm_struct; +picoquic_congestion_algorithm_t* picoquic_dcubic_algorithm = &picoquic_dcubic_algorithm_struct; diff --git a/deps/picoquic/fastcc.c b/deps/picoquic/fastcc.c new file mode 100644 index 0000000..68b22dd --- /dev/null +++ b/deps/picoquic/fastcc.c @@ -0,0 +1,327 @@ +/* +* Author: Christian Huitema +* Copyright (c) 2019, Private Octopus, Inc. +* All rights reserved. +* +* Permission to use, copy, modify, and distribute this software for any +* purpose with or without fee is hereby granted, provided that the above +* copyright notice and this permission notice appear in all copies. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL Private Octopus, Inc. BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "picoquic_internal.h" +#include +#include +#include "cc_common.h" + + +#define FASTCC_MIN_ACK_DELAY_FOR_BANDWIDTH 5000 +#define FASTCC_BANDWIDTH_FRACTION 0.5 +#define FASTCC_REPEAT_THRESHOLD 4 +#define FASTCC_BETA 0.125 +#define FASTCC_BETA_HEAVY_LOSS 0.5 +#define FASTCC_EVAL_ALPHA 0.25 +#define FASTCC_DELAY_THRESHOLD_MAX 25000 +#define FASTCC_NB_PERIOD 6 +#define FASTCC_PERIOD 1000000 + + +typedef enum { + picoquic_fastcc_initial = 0, + picoquic_fastcc_eval, + picoquic_fastcc_freeze +} picoquic_fastcc_alg_state_t; + +typedef struct st_picoquic_fastcc_state_t { + picoquic_fastcc_alg_state_t alg_state; + uint64_t end_of_freeze; /* When to exit the freeze state */ + uint64_t last_ack_time; + uint64_t ack_interval; + uint64_t nb_bytes_ack; + uint64_t nb_bytes_ack_since_rtt; /* accumulate byte count until RTT measured */ + uint64_t end_of_epoch; + uint64_t recovery_sequence; + uint64_t rtt_min; + uint64_t delay_threshold; + uint64_t rolling_rtt_min; /* Min RTT measured for this epoch */ + uint64_t last_rtt_min[FASTCC_NB_PERIOD]; + int nb_cc_events; + unsigned int last_freeze_was_timeout : 1; + unsigned int last_freeze_was_not_delay : 1; + unsigned int rtt_min_is_trusted : 1; + picoquic_min_max_rtt_t rtt_filter; +} picoquic_fastcc_state_t; + +uint64_t picoquic_fastcc_delay_threshold(uint64_t rtt_min) +{ + uint64_t delay = rtt_min / 8; + if (delay > FASTCC_DELAY_THRESHOLD_MAX) { + delay = FASTCC_DELAY_THRESHOLD_MAX; + } + return delay; +} + +void picoquic_fastcc_reset(picoquic_fastcc_state_t* fastcc_state, picoquic_path_t* path_x, uint64_t current_time) +{ + memset(fastcc_state, 0, sizeof(picoquic_fastcc_state_t)); + fastcc_state->alg_state = picoquic_fastcc_initial; + fastcc_state->rtt_min = path_x->smoothed_rtt; + fastcc_state->rolling_rtt_min = fastcc_state->rtt_min; + fastcc_state->delay_threshold = picoquic_fastcc_delay_threshold(fastcc_state->rtt_min); + fastcc_state->end_of_epoch = current_time + FASTCC_PERIOD; + path_x->cwin = PICOQUIC_CWIN_INITIAL; +} + +void picoquic_fastcc_init(picoquic_path_t* path_x, uint64_t current_time) +{ + /* Initialize the state of the congestion control algorithm */ + picoquic_fastcc_state_t* fastcc_state = path_x->congestion_alg_state; + + if (fastcc_state == NULL) { + fastcc_state = (picoquic_fastcc_state_t*)malloc(sizeof(picoquic_fastcc_state_t)); + } + + if (fastcc_state != NULL) { + memset(fastcc_state, 0, sizeof(picoquic_fastcc_state_t)); + fastcc_state->alg_state = picoquic_fastcc_initial; + fastcc_state->rtt_min = path_x->smoothed_rtt; + fastcc_state->rolling_rtt_min = fastcc_state->rtt_min; + fastcc_state->delay_threshold = picoquic_fastcc_delay_threshold(fastcc_state->rtt_min); + fastcc_state->end_of_epoch = current_time + FASTCC_PERIOD; + path_x->cwin = PICOQUIC_CWIN_INITIAL; + } + + path_x->congestion_alg_state = (void*)fastcc_state; +} + +/* Reaction to ECN/CE or sustained losses. + * This is more or less the same code as added to bbr. + * + * This code is called if an ECN/EC event is received, or a timeout + * event, or a lost event indicating a high loss rate + */ +static void fastcc_notify_congestion( + picoquic_cnx_t* cnx, + picoquic_path_t* path_x, + picoquic_fastcc_state_t* fastcc_state, + uint64_t current_time, + int is_delay, + int is_timeout) +{ + if (fastcc_state->alg_state == picoquic_fastcc_freeze && + (!is_timeout || !fastcc_state->last_freeze_was_timeout) && + (!is_delay || !fastcc_state->last_freeze_was_not_delay)) { + /* Do not treat additional events during same freeze interval */ + return; + } + fastcc_state->last_freeze_was_not_delay = !is_delay; + fastcc_state->last_freeze_was_timeout = is_timeout; + fastcc_state->alg_state = picoquic_fastcc_freeze; + fastcc_state->end_of_freeze = current_time + fastcc_state->rtt_min; + fastcc_state->recovery_sequence = picoquic_cc_get_sequence_number(cnx); + fastcc_state->nb_cc_events = 0; + + if (is_delay) { + path_x->cwin -= (uint64_t)(FASTCC_BETA * (double)path_x->cwin); + } + else { + path_x->cwin = path_x->cwin / 2; + } + + if (is_timeout || path_x->cwin < PICOQUIC_CWIN_MINIMUM) { + path_x->cwin = PICOQUIC_CWIN_MINIMUM; + } + + picoquic_update_pacing_data(cnx, path_x, 0); +} + +/* + * Properly implementing fastcc requires managing a number of + * signals, such as packet losses or acknowledgements. We attempt + * to condensate all that in a single API, which could be shared + * by many different congestion control algorithms. + */ +void picoquic_fastcc_notify( + picoquic_cnx_t* cnx, picoquic_path_t* path_x, + picoquic_congestion_notification_t notification, + uint64_t rtt_measurement, + uint64_t one_way_delay, + uint64_t nb_bytes_acknowledged, + uint64_t lost_packet_number, + uint64_t current_time) +{ +#ifdef _WINDOWS + UNREFERENCED_PARAMETER(lost_packet_number); +#endif + picoquic_fastcc_state_t* fastcc_state = (picoquic_fastcc_state_t*)path_x->congestion_alg_state; + + if (fastcc_state != NULL) { + if (fastcc_state->alg_state == picoquic_fastcc_freeze && + (current_time > fastcc_state->end_of_freeze || + fastcc_state->recovery_sequence <= picoquic_cc_get_ack_number(cnx))) { + if (fastcc_state->last_freeze_was_timeout) { + fastcc_state->alg_state = picoquic_fastcc_initial; + } + else { + fastcc_state->alg_state = picoquic_fastcc_eval; + } + fastcc_state->last_freeze_was_not_delay = 0; + fastcc_state->last_freeze_was_timeout = 0; + + fastcc_state->nb_cc_events = 0; + fastcc_state->nb_bytes_ack_since_rtt = 0; + } + + switch (notification) { + case picoquic_congestion_notification_acknowledgement: + if (fastcc_state->alg_state != picoquic_fastcc_freeze) { + /* Count the bytes since last RTT measurement */ + fastcc_state->nb_bytes_ack_since_rtt += nb_bytes_acknowledged; + /* Compute pacing data. */ + picoquic_update_pacing_data(cnx, path_x, 0); + } + break; + + case picoquic_congestion_notification_ecn_ec: + fastcc_notify_congestion(cnx, path_x, fastcc_state, current_time, 0, 0); + break; + case picoquic_congestion_notification_repeat: + case picoquic_congestion_notification_timeout: + if (picoquic_hystart_loss_test(&fastcc_state->rtt_filter, notification, lost_packet_number)) { + fastcc_notify_congestion(cnx, path_x, fastcc_state, current_time, 0, + (notification == picoquic_congestion_notification_timeout) ? 1 : 0); + } + break; + case picoquic_congestion_notification_spurious_repeat: + if (fastcc_state->nb_cc_events > 0) { + fastcc_state->nb_cc_events--; + } + break; + case picoquic_congestion_notification_rtt_measurement: + { + uint64_t delta_rtt = 0; + + picoquic_filter_rtt_min_max(&fastcc_state->rtt_filter, rtt_measurement); + + if (fastcc_state->rtt_filter.is_init) { + /* We use the maximum of the last samples as the candidate for the + * min RTT, in order to filter the rtt jitter */ + if (current_time > fastcc_state->end_of_epoch) { + /* If end of epoch, reset the min RTT to min of remembered periods, + * and roll the period. */ + fastcc_state->rtt_min = UINT64_MAX; + for (int i = FASTCC_NB_PERIOD - 1; i > 0; i--) { + fastcc_state->last_rtt_min[i] = fastcc_state->last_rtt_min[i - 1]; + if (fastcc_state->last_rtt_min[i] > 0 && + fastcc_state->last_rtt_min[i] < fastcc_state->rtt_min) { + fastcc_state->rtt_min = fastcc_state->last_rtt_min[i]; + } + } + fastcc_state->delay_threshold = picoquic_fastcc_delay_threshold(fastcc_state->rtt_min); + fastcc_state->last_rtt_min[0] = fastcc_state->rolling_rtt_min; + fastcc_state->rolling_rtt_min = fastcc_state->rtt_filter.sample_max; + fastcc_state->end_of_epoch = current_time + FASTCC_PERIOD; + } + else if (fastcc_state->rtt_filter.sample_max < fastcc_state->rolling_rtt_min || fastcc_state->rolling_rtt_min == 0) { + /* If not end of epoch, update the rolling minimum */ + fastcc_state->rolling_rtt_min = fastcc_state->rtt_filter.sample_max; + if (fastcc_state->rolling_rtt_min < fastcc_state->rtt_min) { + fastcc_state->rtt_min = fastcc_state->rolling_rtt_min; + } + } + } + + if (fastcc_state->alg_state != picoquic_fastcc_freeze) { + if (rtt_measurement < fastcc_state->rtt_min) { + fastcc_state->delay_threshold = picoquic_fastcc_delay_threshold(fastcc_state->rtt_min); + } + else if (fastcc_state->rtt_min_is_trusted){ + delta_rtt = rtt_measurement - fastcc_state->rtt_min; + } + else { + fastcc_state->rtt_min = rtt_measurement; + fastcc_state->rolling_rtt_min = rtt_measurement; + fastcc_state->rtt_min_is_trusted = 1; + delta_rtt = 0; + } + + if (delta_rtt < fastcc_state->delay_threshold) { + double alpha = 1.0; + fastcc_state->nb_cc_events = 0; + + if (fastcc_state->alg_state != picoquic_fastcc_initial) { + alpha -= ((double)delta_rtt / (double)fastcc_state->delay_threshold); + alpha *= FASTCC_EVAL_ALPHA; + } + + /* Increase the window if it is not frozen */ + if (path_x->last_time_acked_data_frame_sent > path_x->last_sender_limited_time) { + path_x->cwin += (uint64_t)(alpha * (double)fastcc_state->nb_bytes_ack_since_rtt); + } + fastcc_state->nb_bytes_ack_since_rtt = 0; + } + else { + /* May well be congested */ + fastcc_state->nb_cc_events++; + if (fastcc_state->nb_cc_events >= FASTCC_REPEAT_THRESHOLD) { + /* Too many events, reduce the window */ + fastcc_notify_congestion(cnx, path_x, fastcc_state, current_time, 1, 0); + } + } + } + } + break; + case picoquic_congestion_notification_cwin_blocked: + break; + case picoquic_congestion_notification_reset: + picoquic_fastcc_reset(fastcc_state, path_x, current_time); + break; + default: + /* ignore */ + break; + } + } +} + +/* Release the state of the congestion control algorithm */ +void picoquic_fastcc_delete(picoquic_path_t* path_x) +{ + if (path_x->congestion_alg_state != NULL) { + free(path_x->congestion_alg_state); + path_x->congestion_alg_state = NULL; + } +} + + +/* Observe the state of congestion control */ + +void picoquic_fastcc_observe(picoquic_path_t* path_x, uint64_t* cc_state, uint64_t* cc_param) +{ + picoquic_fastcc_state_t* fastcc_state = (picoquic_fastcc_state_t*)path_x->congestion_alg_state; + *cc_state = (uint64_t)fastcc_state->alg_state; + *cc_param = fastcc_state->rolling_rtt_min; +} + +/* Definition record for the FAST CC algorithm */ + +#define picoquic_fastcc_ID "fast" + +picoquic_congestion_algorithm_t picoquic_fastcc_algorithm_struct = { + picoquic_fastcc_ID, 4, + picoquic_fastcc_init, + picoquic_fastcc_notify, + picoquic_fastcc_delete, + picoquic_fastcc_observe +}; + +picoquic_congestion_algorithm_t* picoquic_fastcc_algorithm = &picoquic_fastcc_algorithm_struct; diff --git a/deps/picoquic/frames.c b/deps/picoquic/frames.c new file mode 100644 index 0000000..57a9caf --- /dev/null +++ b/deps/picoquic/frames.c @@ -0,0 +1,4092 @@ +/* +* Author: Christian Huitema +* Copyright (c) 2017, Private Octopus, Inc. +* All rights reserved. +* +* Permission to use, copy, modify, and distribute this software for any +* purpose with or without fee is hereby granted, provided that the above +* copyright notice and this permission notice appear in all copies. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL Private Octopus, Inc. BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/* Decoding of the various frames, and application to context */ +#include +#include +#include "picoquic_internal.h" +#include "tls_api.h" + +static const size_t challenge_length = 8; + +picoquic_stream_head_t* picoquic_create_missing_streams(picoquic_cnx_t* cnx, uint64_t stream_id, int is_remote) +{ + /* Verify the stream ID control conditions */ + picoquic_stream_head_t* stream = NULL; + unsigned int expect_client_stream = cnx->client_mode ^ is_remote; + + if (is_remote && stream_id < cnx->next_stream_id[STREAM_TYPE_FROM_ID(stream_id)]) { + return NULL; + } else if (IS_CLIENT_STREAM_ID(stream_id) != expect_client_stream){ + /* TODO: not an error if lower than next stream, would be just an old stream. */ + picoquic_connection_error(cnx, PICOQUIC_TRANSPORT_STREAM_LIMIT_ERROR, 0); + } + else if (is_remote && stream_id > (IS_BIDIR_STREAM_ID(stream_id) ? cnx->max_stream_id_bidir_local : cnx->max_stream_id_unidir_local)){ + /* Protocol error, stream ID too high */ + picoquic_connection_error(cnx, PICOQUIC_TRANSPORT_STREAM_LIMIT_ERROR, 0); + } + else if (stream_id < cnx->next_stream_id[STREAM_TYPE_FROM_ID(stream_id)]) { + /* Protocol error, stream already closed */ + picoquic_connection_error(cnx, PICOQUIC_TRANSPORT_STREAM_STATE_ERROR, 0); + } else { + while (stream_id >= cnx->next_stream_id[STREAM_TYPE_FROM_ID(stream_id)]) { + stream = picoquic_create_stream(cnx, cnx->next_stream_id[STREAM_TYPE_FROM_ID(stream_id)]); + if (stream == NULL) { + picoquic_log_app_message(cnx, "Create stream %" PRIu64 " returns error 0x%x", + stream_id, PICOQUIC_TRANSPORT_INTERNAL_ERROR); + picoquic_connection_error(cnx, PICOQUIC_TRANSPORT_INTERNAL_ERROR, 0); + break; + } + else if (!IS_BIDIR_STREAM_ID(stream_id)) { + if (!IS_LOCAL_STREAM_ID(stream_id, cnx->client_mode)) { + /* Mark the stream as already finished in our direction */ + stream->fin_requested = 1; + stream->fin_sent = 1; + } + } + } + } + + return stream; +} + +int picoquic_is_stream_closed(picoquic_stream_head_t* stream, int client_mode) +{ + int is_closed = 0; + + if (!stream->is_output_stream) { + if (IS_BIDIR_STREAM_ID(stream->stream_id)) { + is_closed = ((stream->fin_requested && stream->fin_sent) || (stream->reset_requested && stream->reset_sent)) && + ((stream->fin_received && stream->fin_signalled) || (stream->reset_received && stream->reset_signalled)); + } + else if (IS_LOCAL_STREAM_ID(stream->stream_id, client_mode)) { + /* Unidir from local host*/ + is_closed = ((stream->fin_requested && stream->fin_sent) || (stream->reset_requested && stream->reset_sent)); + } + else { + is_closed = ((stream->fin_received && stream->fin_signalled) || (stream->reset_received && stream->reset_signalled)); + } + } + + return is_closed; +} + +int picoquic_is_stream_acked(picoquic_stream_head_t* stream) +{ + int is_acked = 0; + + if (stream->is_closed) { + if (stream->reset_sent) { + is_acked = 1; + } + else { + /* Check whether the ack was already received */ + is_acked = picoquic_check_sack_list(&stream->first_sack_item, 0, stream->sent_offset); + } + } + + return is_acked; +} + +int picoquic_delete_stream_if_closed(picoquic_cnx_t* cnx, picoquic_stream_head_t* stream) +{ + int ret = 0; + + if (!stream->is_closed && picoquic_is_stream_closed(stream, cnx->client_mode)) { + picoquic_update_max_stream_ID_local(cnx, stream); + stream->is_closed = 1; + ret = 1; + } + + /* We only delete the stream if there are no pending retransmissions */ + if (stream->is_closed && picoquic_is_stream_acked(stream)) { + picoquic_delete_stream(cnx, stream); + } + + return ret; +} + +/* if the initial remote has changed, update the existing streams. + * By definition, this is only needed for streams locally created for 0-RTT traffic. + */ + +void picoquic_update_stream_initial_remote(picoquic_cnx_t* cnx) +{ + picoquic_stream_head_t* stream = picoquic_first_stream(cnx); + + while (stream) { + if (IS_LOCAL_STREAM_ID(stream->stream_id, cnx->client_mode)) { + if (IS_BIDIR_STREAM_ID(stream->stream_id)) { + if (stream->maxdata_remote < cnx->remote_parameters.initial_max_stream_data_bidi_remote) { + stream->maxdata_remote = cnx->remote_parameters.initial_max_stream_data_bidi_remote; + } + } + else { + if (stream->maxdata_remote < cnx->remote_parameters.initial_max_stream_data_uni) { + stream->maxdata_remote = cnx->remote_parameters.initial_max_stream_data_uni; + } + } + } + else if (IS_BIDIR_STREAM_ID(stream->stream_id)) { + if (stream->maxdata_remote < cnx->remote_parameters.initial_max_stream_data_bidi_local) { + stream->maxdata_remote = cnx->remote_parameters.initial_max_stream_data_bidi_local; + } + } + stream = picoquic_next_stream(stream); + }; +} + +picoquic_stream_head_t* picoquic_find_or_create_stream(picoquic_cnx_t* cnx, uint64_t stream_id, int is_remote) +{ + picoquic_stream_head_t* stream = picoquic_find_stream(cnx, stream_id); + + if (stream == NULL) { + stream = picoquic_create_missing_streams(cnx, stream_id, is_remote); + } + + return stream; +} + +/* + * Check of the number of newly received bytes, or newly committed bytes + * when a new max offset is learnt for a stream. + */ + +int picoquic_flow_control_check_stream_offset(picoquic_cnx_t* cnx, picoquic_stream_head_t* stream, + uint64_t new_fin_offset) +{ + int ret = 0; + + if (new_fin_offset > stream->maxdata_local) { + /* protocol violation */ + ret = picoquic_connection_error(cnx, PICOQUIC_TRANSPORT_FLOW_CONTROL_ERROR, 0); + } else if (new_fin_offset > stream->fin_offset) { + /* Checking the flow control limits. Need to pay attention + * to possible integer overflow */ + + uint64_t new_bytes = new_fin_offset - stream->fin_offset; + + if (new_bytes > cnx->maxdata_local || cnx->maxdata_local - new_bytes < cnx->data_received) { + /* protocol violation */ + ret = picoquic_connection_error(cnx, PICOQUIC_TRANSPORT_FLOW_CONTROL_ERROR, 0); + } else { + cnx->data_received += new_bytes; + stream->fin_offset = new_fin_offset; + } + } + + return ret; +} + +/* + * RST_STREAM Frame + * + * An endpoint may use a RST_STREAM frame (type=0x01) to abruptly terminate a stream. + */ + +uint8_t * picoquic_format_stream_reset_frame(picoquic_cnx_t* cnx, picoquic_stream_head_t* stream, + uint8_t* bytes, uint8_t * bytes_max, int * more_data, int * is_pure_ack) +{ + uint8_t* bytes0 = bytes; + + if (stream->reset_requested && !stream->reset_sent) { + if ((bytes = picoquic_frames_uint8_encode(bytes, bytes_max, picoquic_frame_type_reset_stream)) != NULL && + (bytes = picoquic_frames_varint_encode(bytes, bytes_max, stream->stream_id)) != NULL && + (bytes = picoquic_frames_varint_encode(bytes, bytes_max, stream->local_error)) != NULL && + (bytes = picoquic_frames_varint_encode(bytes, bytes_max, stream->sent_offset)) != NULL) + { + *is_pure_ack = 0; + stream->reset_sent = 1; + stream->fin_sent = 1; + + picoquic_update_max_stream_ID_local(cnx, stream); + + /* Free the queued data */ + while (stream->send_queue != NULL) { + picoquic_stream_data_node_t* next = stream->send_queue->next_stream_data; + if (stream->send_queue->bytes != NULL) { + free(stream->send_queue->bytes); + } + free(stream->send_queue); + stream->send_queue = next; + } + (void)picoquic_delete_stream_if_closed(cnx, stream); + } + else { + *more_data = 1; + bytes = bytes0; + } + } + + return bytes; +} + +const uint8_t* picoquic_decode_stream_reset_frame(picoquic_cnx_t* cnx, const uint8_t* bytes, const uint8_t* bytes_max) +{ + uint64_t stream_id = 0; + uint64_t error_code_64 = 0; + uint64_t final_offset = 0; + picoquic_stream_head_t* stream; + + if ((bytes = picoquic_frames_varint_decode(bytes + 1, bytes_max, &stream_id)) != NULL) { + bytes = picoquic_frames_varint_decode(bytes, bytes_max, &error_code_64); + if (bytes != NULL) { + bytes = picoquic_frames_varint_decode(bytes, bytes_max, &final_offset); + } + } + if (bytes == NULL){ + picoquic_connection_error(cnx, PICOQUIC_TRANSPORT_FRAME_FORMAT_ERROR, + picoquic_frame_type_reset_stream); + } else if ((stream = picoquic_find_or_create_stream(cnx, stream_id, 1)) == NULL) { + bytes = NULL; // error already signaled + + } else if ((stream->fin_received || stream->reset_received) && final_offset != stream->fin_offset) { + picoquic_connection_error(cnx, PICOQUIC_TRANSPORT_FINAL_OFFSET_ERROR, + picoquic_frame_type_reset_stream); + bytes = NULL; + + } else if (picoquic_flow_control_check_stream_offset(cnx, stream, final_offset) != 0) { + bytes = NULL; // error already signaled + + } else if (!stream->reset_received) { + stream->reset_received = 1; + stream->remote_error = error_code_64; + + picoquic_update_max_stream_ID_local(cnx, stream); + + if (cnx->callback_fn != NULL && !stream->reset_signalled) { + if (cnx->callback_fn(cnx, stream->stream_id, NULL, 0, picoquic_callback_stream_reset, cnx->callback_ctx, stream->app_stream_ctx) != 0) { + picoquic_connection_error(cnx, PICOQUIC_TRANSPORT_INTERNAL_ERROR, + picoquic_frame_type_reset_stream); + } + stream->reset_signalled = 1; + (void)picoquic_delete_stream_if_closed(cnx, stream); + } + } + + return bytes; +} + +/* + * New Connection ID frame + */ + +uint8_t * picoquic_format_new_connection_id_frame(picoquic_cnx_t* cnx, uint8_t* bytes, uint8_t * bytes_max, int * more_data, int * is_pure_ack, picoquic_local_cnxid_t* l_cid) +{ + /* TODO: Encoding retire before, currently 0. */ + uint8_t* bytes0 = bytes; + + if (l_cid != NULL && l_cid->sequence != 0 && l_cid->cnx_id.id_len > 0) { + if ((bytes = picoquic_frames_uint8_encode(bytes, bytes_max, picoquic_frame_type_new_connection_id)) == NULL || + (bytes = picoquic_frames_varint_encode(bytes, bytes_max, l_cid->sequence)) == NULL || + (bytes = picoquic_frames_uint8_encode(bytes, bytes_max, 0)) == NULL || + (bytes = picoquic_frames_cid_encode(bytes, bytes_max, &l_cid->cnx_id)) == NULL || + (bytes + PICOQUIC_RESET_SECRET_SIZE) > bytes_max) { + *more_data = 1; + bytes = bytes0; + } + else { + *is_pure_ack = 0; + (void)picoquic_create_cnxid_reset_secret(cnx->quic, &l_cid->cnx_id, bytes); + bytes += PICOQUIC_RESET_SECRET_SIZE; + } + } + + return bytes; +} + + +const uint8_t* picoquic_skip_new_connection_id_frame(const uint8_t* bytes, const uint8_t* bytes_max) +{ + uint8_t cid_length = 0; + + + if ((bytes = picoquic_frames_varint_skip(bytes, bytes_max)) != NULL && + (bytes = picoquic_frames_varint_skip(bytes, bytes_max)) != NULL && + (bytes = picoquic_frames_varint_skip(bytes, bytes_max)) != NULL && + (bytes = picoquic_frames_uint8_decode(bytes, bytes_max, &cid_length)) != NULL) { + bytes = picoquic_frames_fixed_skip(bytes, bytes_max, (size_t)cid_length + PICOQUIC_RESET_SECRET_SIZE); + } + + return bytes; +} + +const uint8_t* picoquic_decode_new_connection_id_frame(picoquic_cnx_t* cnx, const uint8_t* bytes, const uint8_t* bytes_max, uint64_t current_time) +{ + /* store the connection ID in order to support migration. */ + uint64_t sequence = 0; + uint64_t retire_before = 0; + uint8_t cid_length = 0; + const uint8_t * cnxid_bytes = NULL; + const uint8_t * secret_bytes = NULL; + + if ((bytes = picoquic_frames_varint_decode(bytes + 1, bytes_max, &sequence)) != NULL) { + bytes = picoquic_frames_varint_decode(bytes, bytes_max, &retire_before); + if (bytes != NULL) { + bytes = picoquic_frames_uint8_decode(bytes, bytes_max, &cid_length); + } + } + + if (bytes != NULL) { + cnxid_bytes = bytes; + secret_bytes = bytes + cid_length; + bytes = picoquic_frames_fixed_skip(bytes, bytes_max, (size_t)cid_length + PICOQUIC_RESET_SECRET_SIZE); + } + + if (bytes == NULL || cid_length > PICOQUIC_CONNECTION_ID_MAX_SIZE || + retire_before > sequence) { + picoquic_connection_error(cnx, (bytes == NULL) ? PICOQUIC_TRANSPORT_FRAME_FORMAT_ERROR : PICOQUIC_TRANSPORT_PROTOCOL_VIOLATION, + picoquic_frame_type_new_connection_id); + bytes = NULL; + } else { + uint16_t ret = 0; + + if (bytes != NULL && retire_before > cnx->retire_cnxid_before) { + /* TODO: retire the now deprecated CID */ + ret = (uint16_t)picoquic_remove_not_before_cid(cnx, retire_before, current_time); + } + if (ret == 0 && sequence >= cnx->retire_cnxid_before) { + ret = (uint16_t)picoquic_enqueue_cnxid_stash(cnx, sequence, cid_length, cnxid_bytes, secret_bytes, NULL); + } + if (ret != 0) { + picoquic_connection_error(cnx, ret, picoquic_frame_type_new_connection_id); + bytes = NULL; + } + } + + return bytes; +} + +/* + * Format a retire connection ID frame. + */ + +uint8_t * picoquic_format_retire_connection_id_frame(uint8_t* bytes, uint8_t* bytes_max, int * more_data, int * is_pure_ack, uint64_t sequence) +{ + uint8_t * bytes0 = bytes; + + if ((bytes = picoquic_frames_uint8_encode(bytes, bytes_max, picoquic_frame_type_retire_connection_id)) == NULL || + (bytes = picoquic_frames_varint_encode(bytes, bytes_max, sequence)) == NULL){ + bytes = bytes0; + *more_data = 1; + } + else { + *is_pure_ack = 0; + } + + return bytes; +} + + +/* + * Queue a retire connection id frame when a probe or a path is abandoned. + */ + +int picoquic_queue_retire_connection_id_frame(picoquic_cnx_t * cnx, uint64_t sequence) +{ + int ret = 0; + size_t consumed = 0; + uint8_t frame_buffer[258]; + int is_pure_ack = 1; + int more_data = 0; + uint8_t * bytes_next = picoquic_format_retire_connection_id_frame(frame_buffer, frame_buffer + sizeof(frame_buffer), &more_data, &is_pure_ack, sequence); + + if ((consumed = bytes_next - frame_buffer) > 0) { + ret = picoquic_queue_misc_frame(cnx, frame_buffer, consumed, is_pure_ack); + } + + return ret; +} + +/* + * Skip retire connection ID frame. + */ + +const uint8_t* picoquic_skip_retire_connection_id_frame(const uint8_t* bytes, const uint8_t* bytes_max) +{ + bytes = picoquic_frames_varint_skip(bytes + 1, bytes_max); + + return bytes; +} + +/* + * Decode retire connection ID frame. + * Mark the corresponding paths as retired. This should trigger resending a new connection ID. + * Applications MAY note an error if the connection ID does not exist, but then they + * MUST be damn sure that this not just a repeat of a previous retire connection ID message... + */ + +const uint8_t* picoquic_decode_retire_connection_id_frame(picoquic_cnx_t* cnx, const uint8_t* bytes, const uint8_t* bytes_max, uint64_t current_time, picoquic_path_t * path_x) +{ + /* store the connection ID in order to support migration. */ + uint64_t sequence; + + if ((bytes = picoquic_frames_varint_decode(bytes + 1, bytes_max, &sequence)) == NULL) { + picoquic_connection_error(cnx, PICOQUIC_TRANSPORT_FRAME_FORMAT_ERROR, + picoquic_frame_type_retire_connection_id); + } + else if (sequence >= cnx->local_cnxid_sequence_next) { + /* If there is no matching path, trigger an error */ + picoquic_connection_error(cnx, PICOQUIC_TRANSPORT_PROTOCOL_VIOLATION, + picoquic_frame_type_retire_connection_id); + bytes = NULL; + } + else if (path_x->p_local_cnxid != NULL && + sequence == path_x->p_local_cnxid->sequence) { + /* Cannot delete the path through which it arrives */ + picoquic_connection_error(cnx, PICOQUIC_TRANSPORT_PROTOCOL_VIOLATION, + picoquic_frame_type_retire_connection_id); + bytes = NULL; + } + else { + /* Go through the list of paths to find the connection ID */ + picoquic_retire_local_cnxid(cnx, sequence); + } + + return bytes; +} + +/* + * New Retry Token frame + */ + +uint8_t * picoquic_format_new_token_frame(uint8_t* bytes, uint8_t * bytes_max, int * more_data, int * is_pure_ack, + uint8_t* token, size_t token_length) +{ + uint8_t* bytes0 = bytes; + if ((bytes = picoquic_frames_uint8_encode(bytes, bytes_max, picoquic_frame_type_new_token)) != NULL && + (bytes = picoquic_frames_length_data_encode(bytes, bytes_max, token_length, token)) != NULL) { + *is_pure_ack = 0; + } + else { + *more_data = 1; + bytes = bytes0; + } + + return bytes; +} + +int picoquic_queue_new_token_frame(picoquic_cnx_t * cnx, uint8_t * token, size_t token_length) +{ + int ret = 0; + int more_data = 0; + int is_pure_ack = 1; + uint8_t frame_buffer[258]; + uint8_t* bytes = picoquic_format_new_token_frame(frame_buffer, frame_buffer + sizeof(frame_buffer), &more_data, &is_pure_ack, token, token_length); + + if (bytes > frame_buffer) { + ret = picoquic_queue_misc_frame(cnx, frame_buffer, bytes - frame_buffer, 1); + } + + return ret; +} + +const uint8_t* picoquic_skip_new_token_frame(const uint8_t* bytes, const uint8_t* bytes_max) +{ + return picoquic_frames_length_data_skip(bytes+1, bytes_max); +} + +const uint8_t* picoquic_decode_new_token_frame(picoquic_cnx_t* cnx, const uint8_t* bytes, const uint8_t* bytes_max, + uint64_t current_time, struct sockaddr* addr_to) +{ + /* TODO: store the new token in order to support immediate connection on some servers. */ + + uint64_t length = 0; + const uint8_t * token = NULL; + + if ((bytes = picoquic_frames_varint_decode(bytes + 1, bytes_max, &length)) != NULL) { + token = bytes; + bytes = picoquic_frames_fixed_skip(bytes, bytes_max, (size_t)length); + } + + if (bytes == NULL) { + picoquic_connection_error(cnx, PICOQUIC_TRANSPORT_FRAME_FORMAT_ERROR, + picoquic_frame_type_new_token); + } + else if (addr_to != NULL && cnx->client_mode && cnx->sni != NULL){ + uint8_t * ip_addr; + uint8_t ip_addr_length; + picoquic_get_ip_addr(addr_to, &ip_addr, &ip_addr_length); + (void)picoquic_store_token(&cnx->quic->p_first_token, current_time, cnx->sni, (uint16_t)strlen(cnx->sni), + ip_addr, ip_addr_length, token, (uint16_t)length); + } + + return bytes; +} + +/* + * STOP SENDING Frame + */ + +uint8_t* picoquic_format_stop_sending_frame(picoquic_stream_head_t* stream, + uint8_t* bytes, uint8_t* bytes_max, int* more_data, int* is_pure_ack) +{ + if (!stream->stop_sending_requested || stream->stop_sending_sent || stream->fin_received || stream->reset_received) { + /* set this, so we will not be called again */ + stream->stop_sending_sent = 1; + } + else + { + uint8_t* bytes0 = bytes; + + if ((bytes = picoquic_frames_uint8_encode(bytes, bytes_max, picoquic_frame_type_stop_sending)) != NULL && + (bytes = picoquic_frames_varint_encode(bytes, bytes_max, (uint64_t)stream->stream_id)) != NULL && + (bytes = picoquic_frames_varint_encode(bytes, bytes_max, stream->local_stop_error)) != NULL + ) { + *is_pure_ack = 0; + stream->stop_sending_sent = 1; + } + else { + bytes = bytes0; + *more_data = 1; + } + } + + return bytes; +} + + +const uint8_t* picoquic_decode_stop_sending_frame(picoquic_cnx_t* cnx, const uint8_t* bytes, const uint8_t* bytes_max) +{ + uint64_t stream_id = 0; + uint64_t error_code = 0; + picoquic_stream_head_t* stream; + + if ((bytes = picoquic_frames_varint_decode(bytes+1, bytes_max, &stream_id)) == NULL || + (bytes = picoquic_frames_varint_decode(bytes, bytes_max, &error_code)) == NULL) + { + picoquic_connection_error(cnx, PICOQUIC_TRANSPORT_FRAME_FORMAT_ERROR, + picoquic_frame_type_stop_sending); + + } + else if ((stream = picoquic_find_or_create_stream(cnx, stream_id, 1)) == NULL) { + bytes = NULL; // Error already signaled + } else if (!IS_BIDIR_STREAM_ID(stream_id) && !IS_LOCAL_STREAM_ID(stream_id, cnx->client_mode)) { + picoquic_connection_error(cnx, PICOQUIC_TRANSPORT_PROTOCOL_VIOLATION, + picoquic_frame_type_stop_sending); + bytes = NULL; + } else if (!stream->stop_sending_received && !stream->reset_requested) { + stream->stop_sending_received = 1; + stream->remote_stop_error = error_code; + + if (cnx->callback_fn != NULL && !stream->stop_sending_signalled) { + if (cnx->callback_fn(cnx, stream->stream_id, NULL, 0, picoquic_callback_stop_sending, cnx->callback_ctx, stream->app_stream_ctx) != 0) { + picoquic_log_app_message(cnx, "Stop sending callback on stream %" PRIu64 " returns error 0x%x", + stream->stream_id, PICOQUIC_TRANSPORT_INTERNAL_ERROR); + picoquic_connection_error(cnx, PICOQUIC_TRANSPORT_INTERNAL_ERROR, + picoquic_frame_type_stop_sending); + } + stream->stop_sending_signalled = 1; + } + } + + return bytes; +} + +const uint8_t* picoquic_skip_stop_sending_frame(const uint8_t* bytes, const uint8_t* bytes_max) +{ + if ((bytes = picoquic_frames_varint_skip(bytes+1, bytes_max)) != NULL) { + bytes = picoquic_frames_varint_skip(bytes, bytes_max); + } + return bytes; +} + + +/* + * STREAM frames implicitly create a stream and carry stream data. + */ + +int picoquic_is_stream_frame_unlimited(const uint8_t* bytes) +{ + return PICOQUIC_BITS_CLEAR_IN_RANGE(bytes[0], picoquic_frame_type_stream_range_min, picoquic_frame_type_stream_range_max, 0x02); +} + +int picoquic_parse_stream_header(const uint8_t* bytes, size_t bytes_max, + uint64_t* stream_id, uint64_t* offset, size_t* data_length, int* fin, + size_t* consumed) +{ + int ret = 0; + int len = bytes[0] & 2; + int off = bytes[0] & 4; + uint64_t length = 0; + size_t l_stream = 0; + size_t l_len = 0; + size_t l_off = 0; + size_t byte_index = 1; + + *fin = bytes[0] & 1; + + if (bytes_max > byte_index) { + l_stream = picoquic_varint_decode(bytes + byte_index, bytes_max - byte_index, stream_id); + byte_index += l_stream; + } + + if (off == 0) { + *offset = 0; + } else if (bytes_max > byte_index) { + l_off = picoquic_varint_decode(bytes + byte_index, bytes_max - byte_index, offset); + byte_index += l_off; + } + + if (bytes_max < byte_index || l_stream == 0 || (off != 0 && l_off == 0)) { + DBG_PRINTF("stream frame header too large: first_byte=0x%02x, bytes_max=%" PRIst, + bytes[0], bytes_max); + *data_length = 0; + byte_index = bytes_max; + ret = -1; + } else if (len == 0) { + *data_length = bytes_max - byte_index; + } else { + if (bytes_max > byte_index) { + l_len = picoquic_varint_decode(bytes + byte_index, bytes_max - byte_index, &length); + byte_index += l_len; + *data_length = (size_t)length; + } + + if (l_len == 0 || bytes_max < byte_index) { + DBG_PRINTF("stream frame header too large: first_byte=0x%02x, bytes_max=%" PRIst, + bytes[0], bytes_max); + byte_index = bytes_max; + ret = -1; + } else if (byte_index + length > bytes_max) { + DBG_PRINTF("stream data past the end of the packet: first_byte=0x%02x, data_length=%" PRIst ", max_bytes=%" PRIst, + bytes[0], *data_length, bytes_max); + ret = -1; + } + } + + *consumed = byte_index; + return ret; +} + +void picoquic_stream_data_callback(picoquic_cnx_t* cnx, picoquic_stream_head_t* stream) +{ + picoquic_stream_data_node_t* data; + + while ((data = (picoquic_stream_data_node_t*)picosplay_first(&stream->stream_data_tree)) != NULL && data->offset <= stream->consumed_offset) { + size_t start = (size_t)(stream->consumed_offset - data->offset); + size_t data_length = data->length - start; + picoquic_call_back_event_t fin_now = picoquic_callback_stream_data; + + stream->consumed_offset += data_length; + + if (stream->consumed_offset >= stream->fin_offset && stream->fin_received && !stream->fin_signalled){ + fin_now = picoquic_callback_stream_fin; + stream->fin_signalled = 1; + } + + if (cnx->callback_fn(cnx, stream->stream_id, data->bytes + start, data_length, fin_now, + cnx->callback_ctx, stream->app_stream_ctx) != 0) { + picoquic_log_app_message(cnx, "Data callback on stream %" PRIu64 " returns error 0x%x", + stream->stream_id, PICOQUIC_TRANSPORT_INTERNAL_ERROR); + picoquic_connection_error(cnx, PICOQUIC_TRANSPORT_INTERNAL_ERROR, 0); + } + + picosplay_delete_hint(&stream->stream_data_tree, &data->stream_data_node); + } + + /* handle the case where the fin frame does not carry any data */ + + if (stream->consumed_offset >= stream->fin_offset && stream->fin_received && !stream->fin_signalled) { + stream->fin_signalled = 1; + if (cnx->callback_fn(cnx, stream->stream_id, NULL, 0, picoquic_callback_stream_fin, + cnx->callback_ctx, stream->app_stream_ctx) != 0) { + picoquic_log_app_message(cnx, "FIN callback on stream %" PRIu64 " returns error 0x%x", + stream->stream_id, PICOQUIC_TRANSPORT_INTERNAL_ERROR); + picoquic_connection_error(cnx, PICOQUIC_TRANSPORT_INTERNAL_ERROR, 0); + } + } +} + +static int add_chunk_node(picosplay_tree_t* tree, uint64_t offset, size_t length, const uint8_t* bytes, int* chunk_added) +{ + int ret = 0; + + picoquic_stream_data_node_t* node = (picoquic_stream_data_node_t*)malloc(sizeof(picoquic_stream_data_node_t)); + uint8_t* chunk_bytes = (uint8_t*)malloc(length); + + if (node == NULL || chunk_bytes == NULL) { + free(node); + free(chunk_bytes); + ret = PICOQUIC_ERROR_MEMORY; + } else { + memcpy(chunk_bytes, bytes, length); + memset(node, 0, sizeof(picoquic_stream_data_node_t)); + node->offset = offset; + node->length = length; + node->bytes = chunk_bytes; + + picosplay_insert(tree, node); + *chunk_added = 1; + } + + return ret; +} + +/* Common code to data stream and crypto hs stream */ +int picoquic_queue_network_input(picosplay_tree_t* tree, uint64_t consumed_offset, + uint64_t frame_data_offset, const uint8_t* bytes, size_t length, int* new_data_available) +{ + const uint64_t input_begin = frame_data_offset; + const uint64_t input_end = frame_data_offset + length; + + int ret = 0; + + /* Remove data that is already consumed */ + if (frame_data_offset < consumed_offset) { + frame_data_offset = consumed_offset; + } + + /* check for data that is already received in chunks with offset <= end */ + if (frame_data_offset < input_end) { + + picoquic_stream_data_node_t target; + memset(&target, 0, sizeof(picoquic_stream_data_node_t)); + target.offset = frame_data_offset; + + picoquic_stream_data_node_t* prev = (picoquic_stream_data_node_t*)picosplay_find_previous(tree, &target); + if (prev != NULL) { + /* By definition, prev->offset <= frame_data_offset. Check whether the + * beginning of the frame is already received and skip if necessary */ + const uint64_t prev_end = prev->offset + prev->length; + frame_data_offset = frame_data_offset > prev_end ? frame_data_offset : prev_end; + } + + picoquic_stream_data_node_t* next = (prev == NULL) ? + (picoquic_stream_data_node_t*)picosplay_first(tree) : + (picoquic_stream_data_node_t*)picosplay_next(&prev->stream_data_node); + + /* Check whether parts of the new frame are covered by already received chunks */ + while (ret == 0 && frame_data_offset < input_end && next != NULL && next->offset < input_end) { + + /* the tail of the frame overlaps with the next frame received */ + const uint64_t chunk_ofs = frame_data_offset; + const uint64_t chunk_len = next->offset > frame_data_offset ? next->offset - frame_data_offset : 0; + + if (chunk_len > 0) { + /* There is a gap between previous and next frame, and it will be at least partially filled */ + ret = add_chunk_node(tree, chunk_ofs, (size_t)chunk_len, bytes + frame_data_offset - input_begin, new_data_available); + } + + frame_data_offset = next->offset + next->length; + next = (picoquic_stream_data_node_t*)picosplay_next(&next->stream_data_node); + } + + /* no further already received chunk within the new frame */ + if (ret == 0 && frame_data_offset < input_end) { + const uint64_t chunk_ofs = frame_data_offset; + const uint64_t chunk_len = input_end - frame_data_offset; + ret = add_chunk_node(tree, chunk_ofs, (size_t)chunk_len, bytes + frame_data_offset - input_begin, new_data_available); + } + } + + return ret; +} + +static int picoquic_stream_network_input(picoquic_cnx_t* cnx, uint64_t stream_id, + uint64_t offset, int fin, const uint8_t* bytes, size_t length, uint64_t current_time) +{ + int ret = 0; + uint64_t should_notify = 0; + /* Is there such a stream, is it still open? */ + picoquic_stream_head_t* stream; + uint64_t new_fin_offset = offset + length; + + if ((stream = picoquic_find_or_create_stream(cnx, stream_id, 1)) == NULL) { + if (stream_id < cnx->next_stream_id[STREAM_TYPE_FROM_ID(stream_id)]) { + return 0; + } + else { + ret = 1; // Error already signaled + } + } + else if (stream->fin_received) { + if (fin != 0 ? stream->fin_offset != new_fin_offset : new_fin_offset > stream->fin_offset) { + ret = picoquic_connection_error(cnx, PICOQUIC_TRANSPORT_FINAL_OFFSET_ERROR, 0); + } + } + else { + if (fin) { + stream->fin_received = 1; + should_notify = 1; + cnx->latest_progress_time = current_time; + picoquic_update_max_stream_ID_local(cnx, stream); + } + + if (new_fin_offset > stream->fin_offset) { + ret = picoquic_flow_control_check_stream_offset(cnx, stream, new_fin_offset); + } + } + + /* If the application provided a direct receive callback, it wil receive the data as they + * arrive. If not, the data segments are organized in a splay and passed to the + * application in strict order. + */ + + if (ret == 0) { + if (stream->direct_receive_fn != NULL) { + ret = stream->direct_receive_fn(cnx, stream_id, fin, bytes, offset, length, stream->direct_receive_ctx); + if (ret == PICOQUIC_STREAM_RECEIVE_COMPLETE && stream->fin_received) { + stream->fin_signalled = 1; + ret = 0; + } + else if (ret != 0) { + int err = (ret >= PICOQUIC_ERROR_CLASS) ? PICOQUIC_TRANSPORT_INTERNAL_ERROR : ret; + ret = picoquic_connection_error(cnx, (uint16_t)err, 0); + } + } + else { + int new_data_available = 0; + + ret = picoquic_queue_network_input(&stream->stream_data_tree, stream->consumed_offset, + offset, bytes, length, &new_data_available); + if (ret != 0) { + ret = picoquic_connection_error(cnx, (int16_t)ret, 0); + } + else if (new_data_available) { + should_notify = 1; + cnx->latest_progress_time = current_time; + } + + if (ret == 0 && should_notify != 0 && cnx->callback_fn != NULL) { + /* check how much data there is to send */ + picoquic_stream_data_callback(cnx, stream); + } + } + } + + /* Either the direct receive or the data queueing can set the "fin_signalled" bit when all data expected + * on the stream has been received. The stream can be closed when all data is sent and received */ + + if (ret == 0) { + int is_deleted = 0; + + if (stream->fin_signalled) { + is_deleted = picoquic_delete_stream_if_closed(cnx, stream); + } + + if (!is_deleted) { + if (!stream->fin_signalled) { + if (!stream->fin_received && !stream->reset_received && 2 * stream->consumed_offset > stream->maxdata_local) { + cnx->max_stream_data_needed = 1; + } + } + if (stream->fin_received || stream->reset_received) { + cnx->pkt_ctx[picoquic_packet_context_application].ack_after_fin = 1; + } + } + } + + return ret; +} + +const uint8_t* picoquic_decode_stream_frame(picoquic_cnx_t* cnx, const uint8_t* bytes, const uint8_t* bytes_max, uint64_t current_time) +{ + uint64_t stream_id; + size_t data_length; + uint64_t offset; + int fin; + size_t consumed; + + if (picoquic_parse_stream_header(bytes, bytes_max - bytes, &stream_id, &offset, &data_length, &fin, &consumed) != 0) { + bytes = NULL; + }else if (offset + data_length >= (1ull<<62)){ + picoquic_connection_error(cnx, PICOQUIC_TRANSPORT_FINAL_OFFSET_ERROR, 0); + bytes = NULL; + } else if (picoquic_stream_network_input(cnx, stream_id, offset, fin, (bytes += consumed), data_length, current_time) != 0) { + bytes = NULL; + } else { + bytes += data_length; + } + + return bytes; +} + +picoquic_stream_head_t* picoquic_find_ready_stream(picoquic_cnx_t* cnx) +{ + picoquic_stream_head_t* first_stream = cnx->first_output_stream; + picoquic_stream_head_t* stream = first_stream; + picoquic_stream_head_t* found_stream = NULL; + picoquic_stream_head_t* previous_stream = NULL; + + + /* Look for a ready stream */ + while (stream != NULL) { + if ((cnx->maxdata_remote > cnx->data_sent&& stream->sent_offset < stream->maxdata_remote && (stream->is_active || + (stream->send_queue != NULL && stream->send_queue->length > stream->send_queue->offset) || + (stream->fin_requested && !stream->fin_sent))) || + (stream->reset_requested && !stream->reset_sent) || + (stream->stop_sending_requested && !stream->stop_sending_sent)) { + /* Something can be sent */ + found_stream = stream; + break; + } + else if (((stream->fin_requested && stream->fin_sent) || (stream->reset_requested && stream->reset_sent)) && (!stream->stop_sending_requested || stream->stop_sending_sent)) { + picoquic_stream_head_t* next_stream = stream->next_output_stream; + /* If stream is exhausted, remove from output list */ + picoquic_remove_output_stream(cnx, stream, previous_stream); + + picoquic_delete_stream_if_closed(cnx, stream); + stream = next_stream; + } + else { + if (stream->is_active || + (stream->send_queue != NULL && stream->send_queue->length > stream->send_queue->offset)) { + if (stream->sent_offset >= stream->maxdata_remote) { + cnx->stream_blocked = 1; + } + else if (cnx->maxdata_remote <= cnx->data_sent) { + cnx->flow_blocked = 1; + } + } + previous_stream = stream; + stream = stream->next_output_stream; + } + } + + return found_stream; +} + +/* Management of BLOCKED signals + */ + +uint8_t * picoquic_format_data_blocked_frame(picoquic_cnx_t * cnx, uint8_t* bytes, + uint8_t * bytes_max, int * more_data, int * is_pure_ack) +{ + uint8_t* bytes0 = bytes; + + if ((bytes = picoquic_frames_uint8_encode(bytes, bytes_max, picoquic_frame_type_data_blocked)) != NULL && + (bytes = picoquic_frames_varint_encode(bytes, bytes_max, cnx->maxdata_remote)) != NULL) { + *is_pure_ack = 0; + cnx->sent_blocked_frame = 1; + } + else { + *more_data = 1; + bytes = bytes0; + } + + return bytes; +} + +uint8_t * picoquic_format_stream_data_blocked_frame(uint8_t* bytes, + uint8_t* bytes_max, int* more_data, int* is_pure_ack, picoquic_stream_head_t* stream) +{ + uint8_t* bytes0 = bytes; + + if ((bytes=picoquic_frames_uint8_encode(bytes, bytes_max, picoquic_frame_type_stream_data_blocked)) != NULL && + (bytes = picoquic_frames_varint_encode(bytes, bytes_max, stream->stream_id)) != NULL && + (bytes = picoquic_frames_varint_encode(bytes, bytes_max, stream->maxdata_remote)) != NULL) + { + *is_pure_ack = 0; + stream->stream_data_blocked_sent = 1; + } + else { + *more_data = 1; + bytes = bytes0; + } + + return bytes; +} + +uint8_t * picoquic_format_stream_blocked_frame(picoquic_cnx_t* cnx, uint8_t* bytes, + uint8_t* bytes_max, int* more_data, int* is_pure_ack, picoquic_stream_head_t* stream) +{ + uint8_t* bytes0 = bytes; + uint8_t f_type = 0; + uint64_t stream_limit = 0; + int should_not_send = 0; + + if (IS_BIDIR_STREAM_ID(stream->stream_id)) { + f_type = picoquic_frame_type_streams_blocked_bidir; + stream_limit = STREAM_RANK_FROM_ID(stream->stream_id); + should_not_send = cnx->stream_blocked_bidir_sent; + } + else { + f_type = picoquic_frame_type_streams_blocked_unidir; + stream_limit = STREAM_RANK_FROM_ID(stream->stream_id); + should_not_send = cnx->stream_blocked_unidir_sent; + } + if (!should_not_send) { + if ((bytes = picoquic_frames_uint8_encode(bytes, bytes_max, f_type)) != NULL && + (bytes = picoquic_frames_varint_encode(bytes, bytes_max, stream_limit)) != NULL) { + *is_pure_ack = 0; + if (IS_BIDIR_STREAM_ID(stream->stream_id)) { + cnx->stream_blocked_bidir_sent = 1; + } + else { + cnx->stream_blocked_unidir_sent = 1; + } + } + else { + *more_data = 1; + bytes = bytes0; + } + } + + return bytes; +} + +uint8_t * picoquic_format_one_blocked_frame(picoquic_cnx_t* cnx, uint8_t* bytes, + uint8_t* bytes_max, int* more_data, int* is_pure_ack, picoquic_stream_head_t* stream) +{ + if (stream->is_active || + (stream->send_queue != NULL && stream->send_queue->length > stream->send_queue->offset)) { + /* The stream has some data to send */ + /* if the stream is not active yet, verify that it fits under + * the max stream id limit, which depends of the type of stream */ + if (IS_CLIENT_STREAM_ID(stream->stream_id) != cnx->client_mode && + stream->stream_id > ((IS_BIDIR_STREAM_ID(stream->stream_id)) ? cnx->max_stream_id_bidir_remote : cnx->max_stream_id_unidir_remote)) { + if (!(IS_BIDIR_STREAM_ID(stream->stream_id) ? cnx->stream_blocked_bidir_sent : cnx->stream_blocked_unidir_sent)) + { + /* Prepare a stream blocked frame */ + bytes = picoquic_format_stream_blocked_frame(cnx, bytes, bytes_max, more_data, is_pure_ack, stream); + } + } + else { + if (cnx->maxdata_remote <= cnx->data_sent && !cnx->sent_blocked_frame) { + /* Prepare a blocked frame */ + bytes = picoquic_format_data_blocked_frame(cnx, bytes, bytes_max, more_data, is_pure_ack); + } + + if (stream->sent_offset >= stream->maxdata_remote && !stream->stream_data_blocked_sent) { + /* Prepare a stream data blocked frame */ + bytes = picoquic_format_stream_data_blocked_frame(bytes, bytes_max, more_data, is_pure_ack, stream); + } + } + } + + return bytes; +} + +uint8_t * picoquic_format_blocked_frames(picoquic_cnx_t* cnx, uint8_t* bytes, uint8_t * bytes_max, int * more_data, int * is_pure_ack) +{ + picoquic_stream_head_t* stream = picoquic_first_stream(cnx); + picoquic_stream_head_t* hi_pri_stream = NULL; + + /* Check whether there is a high priority stream declared */ + if (cnx->high_priority_stream_id != (uint64_t)((int64_t)-1)) { + hi_pri_stream = picoquic_find_stream(cnx, cnx->high_priority_stream_id); + } + + /* Look for blocked streams, as long as there is message space available */ + while (stream != NULL) { + if (hi_pri_stream == NULL || stream == hi_pri_stream) { + bytes = picoquic_format_one_blocked_frame(cnx, bytes, bytes_max, more_data, is_pure_ack, stream); + if (*more_data) { + break; + } + } + + stream = picoquic_next_stream(stream); + } + + return bytes; +} + +/* handling of stream frames + */ + +typedef struct st_picoquic_stream_data_buffer_argument_t { + uint8_t* bytes; /* Points to the beginning of the encoding of the stream frame */ + size_t byte_index; /* Current index position after encoding type, stream-id and offset */ + size_t byte_space; /* Number of bytes available in the packet after the current index */ + size_t allowed_space; /* Maximum number of bytes that the application is authorized to write */ + size_t length; /* number of bytes that the application commits to write */ + int is_fin; /* Whether this is the end of the stream */ + int is_still_active; /* whether the stream is still considered active after this call */ + uint8_t* app_buffer; /* buffer provided to the application. */ +} picoquic_stream_data_buffer_argument_t; + +static size_t picoquic_encode_length_of_stream_frame( + uint8_t* bytes, size_t byte_index, size_t byte_space, size_t length, size_t *start_index) +{ + if (length < byte_space) { + if (length == byte_space - 1) { + /* Special case: there are N bytes available, the application wants to write N-1 bytes. + * We can encode N bytes because then we don't need a length field, just a flag in the + * first byte. But if we had to encode "length=N-1", that would typically require 2 + * bytes, for a total of (2 + N-1)=N+1 bytes, larger than the packet size. We also + * don't want to avoid the length field, because the encoding would be shorter than + * the packet size, and other parts of the code might add a byte after that, e.g. padding, + * which the receiver would mistake as data because of the "implicit length" encoding. + * So we work against that issue by inserting a single padding byte in front of the + * stream header.*/ + memmove(bytes + 1, bytes, byte_index); + bytes[0] = picoquic_frame_type_padding; + *start_index = 1; + byte_index++; + } + else { + /* Short frame, length field is required */ + /* We checked above that there are enough bytes to encode length */ + byte_index += picoquic_varint_encode(bytes + byte_index, byte_space, (uint64_t)length); + bytes[0] |= 2; /* Indicates presence of length */ + } + } + + return byte_index; +} + +uint8_t* picoquic_provide_stream_data_buffer(void* context, size_t length, int is_fin, int is_still_active) +{ + picoquic_stream_data_buffer_argument_t * data_ctx = (picoquic_stream_data_buffer_argument_t*)context; + uint8_t* buffer = NULL; + size_t start_index = 0; + + if (length <= data_ctx->allowed_space) { + data_ctx->length = length; + + if (is_fin) { + data_ctx->is_fin = 1; + data_ctx->bytes[0] |= 1; + } + + data_ctx->is_still_active = is_still_active; + + data_ctx->byte_index = picoquic_encode_length_of_stream_frame(data_ctx->bytes, + data_ctx->byte_index, data_ctx->byte_space, length, &start_index); + + buffer = data_ctx->bytes + data_ctx->byte_index; + data_ctx->app_buffer = buffer; + } + + return buffer; +} + +static uint8_t* picoquic_format_stream_frame_header(uint8_t* bytes, uint8_t* bytes_max, uint64_t stream_id, uint64_t offset) +{ + uint8_t* bytes0 = bytes; + if ((bytes = picoquic_frames_uint8_encode(bytes, bytes_max, picoquic_frame_type_stream_range_min)) != NULL && + (bytes = picoquic_frames_varint_encode(bytes, bytes_max, stream_id)) != NULL) { + if (offset > 0) { + *bytes0 |= 4; /* Indicates presence of offset */ + bytes = picoquic_frames_varint_encode(bytes, bytes_max, offset); + } + } + + return bytes; +} + +uint8_t * picoquic_format_stream_frame(picoquic_cnx_t* cnx, picoquic_stream_head_t* stream, + uint8_t* bytes, uint8_t* bytes_max, int * more_data, int * is_pure_ack, int* is_still_active, int * ret) +{ + int may_close = 0; + *ret = 0; + + /* Check parity */ + if (IS_CLIENT_STREAM_ID(stream->stream_id) == cnx->client_mode) { + if (stream->stream_id > ((IS_BIDIR_STREAM_ID(stream->stream_id)) ? cnx->max_stream_id_bidir_remote : cnx->max_stream_id_unidir_remote)) { + return bytes; + } + } + + if (stream->reset_requested && !stream->reset_sent) { + return picoquic_format_stream_reset_frame(cnx, stream, bytes, bytes_max, more_data, is_pure_ack); + } + + if (stream->stop_sending_requested && !stream->stop_sending_sent) { + return picoquic_format_stop_sending_frame(stream, bytes, bytes_max, more_data, is_pure_ack); + } + + if (!stream->is_active && + (stream->send_queue == NULL || stream->send_queue->length <= stream->send_queue->offset) && + (!stream->fin_requested || stream->fin_sent)) { + /* Nothing to send */ + } + else { + uint8_t* bytes0 = bytes; + size_t byte_index = 0; + size_t length = 0; + + if ((bytes = picoquic_format_stream_frame_header(bytes, bytes_max, stream->stream_id, stream->sent_offset)) == NULL) { + bytes = bytes0; + *more_data = 1; + } else { + /* Compute the length */ + size_t byte_space = bytes_max - bytes; + size_t allowed_space = byte_space; + + /* Enforce maxdata per stream on all streams, including stream 0 + * This may result in very short encoding, but we still send whatever is + * allowed by flow control. Doing otherwise may cause a loop if the + * "find_ready_stream" function did not completely replicate the + * flow control test */ + if (allowed_space > (stream->maxdata_remote - stream->sent_offset)) { + allowed_space = (size_t)(stream->maxdata_remote - stream->sent_offset); + } + + if (allowed_space > (cnx->maxdata_remote - cnx->data_sent)) { + allowed_space = (size_t)(cnx->maxdata_remote - cnx->data_sent); + } + + if (stream->is_active && stream->send_queue == NULL && !stream->fin_requested) { + /* The application requested active polling for this stream */ + picoquic_stream_data_buffer_argument_t stream_data_context; + + stream_data_context.bytes = bytes0; + stream_data_context.byte_index = bytes - bytes0; + stream_data_context.allowed_space = allowed_space; + stream_data_context.byte_space = bytes_max - bytes; + stream_data_context.length = 0; + stream_data_context.is_fin = 0; + stream_data_context.is_still_active = 0; + stream_data_context.app_buffer = NULL; + + if ((cnx->callback_fn)(cnx, stream->stream_id, (uint8_t*)&stream_data_context, allowed_space, picoquic_callback_prepare_to_send, cnx->callback_ctx, stream->app_stream_ctx) != 0) { + /* something went wrong */ + picoquic_log_app_message(cnx, "Prepare to send returns error 0x%x", PICOQUIC_TRANSPORT_INTERNAL_ERROR); + *ret = picoquic_connection_error(cnx, PICOQUIC_TRANSPORT_INTERNAL_ERROR, 0); + bytes = bytes0; /* CHECK: SHOULD THIS BE NULL ? */ + } + else { + bytes = bytes0 + stream_data_context.byte_index + stream_data_context.length; + stream->sent_offset += stream_data_context.length; + cnx->data_sent += stream_data_context.length; + + if (stream_data_context.length > 0) { + if (stream_data_context.app_buffer == NULL || + stream_data_context.app_buffer < bytes0 || + stream_data_context.app_buffer >= bytes_max) { + long long delta_buf = (long long)(stream_data_context.app_buffer - bytes); + DBG_PRINTF("Stream data buffer corruption, delta = %lld\n", delta_buf); + *ret = PICOQUIC_ERROR_UNEXPECTED_ERROR; + /* CHECK: SHOULD bytes BE NULL ? */ + } + } + + if (stream_data_context.is_fin) { + stream->is_active = 0; + stream->fin_requested = 1; + stream->fin_sent = 1; + picoquic_update_max_stream_ID_local(cnx, stream); + may_close = 1; + + if (is_still_active != NULL) { + *is_still_active = 0; + } + } + else { + stream->is_active = stream_data_context.is_still_active; + if (is_still_active != NULL) { + *is_still_active = stream_data_context.is_still_active; + } + } + } + } + else { + /* The application queued data for this stream */ + size_t start_index = 0; + + byte_index = bytes - bytes0; + + if (stream->send_queue == NULL) { + length = 0; + } + else { + length = (size_t)(stream->send_queue->length - stream->send_queue->offset); + } + + if (length >= allowed_space) { + length = allowed_space; + } + + byte_index = picoquic_encode_length_of_stream_frame(bytes0, byte_index, byte_space, length, &start_index); + + if (length > 0 && stream->send_queue != NULL && stream->send_queue->bytes != NULL) { + memcpy(&bytes0[byte_index], stream->send_queue->bytes + stream->send_queue->offset, length); + byte_index += length; + + stream->send_queue->offset += length; + if (stream->send_queue->offset >= stream->send_queue->length) { + picoquic_stream_data_node_t* next = stream->send_queue->next_stream_data; + free(stream->send_queue->bytes); + free(stream->send_queue); + stream->send_queue = next; + } + + stream->sent_offset += length; + cnx->data_sent += length; + } + + bytes = bytes0 + byte_index; + + if (stream->send_queue == NULL) { + if (stream->fin_requested) { + /* Set the fin bit -- target the start_index octet, to match behavior of length encoding */ + stream->fin_sent = 1; + bytes0[start_index] |= 1; + + picoquic_update_max_stream_ID_local(cnx, stream); + may_close = 1; + } + } + else if (length == 0) { + /* No point in sending a silly packet */ + bytes = bytes0; + *more_data = 1; + } + } + } + + if (*ret == 0) { + *is_pure_ack &= (bytes == bytes0); + + if (!may_close || !picoquic_delete_stream_if_closed(cnx, stream)) { + /* mark the stream as unblocked since we sent something */ + stream->stream_data_blocked_sent = 0; + cnx->sent_blocked_frame = 0; + } + } + } + + return bytes; +} + +/* Format all available stream frames that fit in the packet. + * Update more_data if more stream data is available + * Update is_pure_ack if formated frames require ack + * Set stream_tried_and_failed if there was nothing to send, indicating the app limited condition. + */ +uint8_t* picoquic_format_available_stream_frames(picoquic_cnx_t* cnx, uint8_t* bytes_next, uint8_t* bytes_max, int* more_data, + int* is_pure_ack, int* stream_tried_and_failed, int* ret) +{ + uint8_t* bytes_previous = bytes_next; + picoquic_stream_head_t* stream = picoquic_find_ready_stream(cnx); + int more_stream_data = 0; + + while (*ret == 0 && stream != NULL && bytes_next < bytes_max) { + int is_still_active = 0; + + bytes_next = picoquic_format_stream_frame(cnx, stream, bytes_next, bytes_max, &more_stream_data, is_pure_ack, &is_still_active, ret); + + if (*ret == 0) { + if (bytes_next + 17 < bytes_max) { + stream = picoquic_find_ready_stream(cnx); + } + else { + more_stream_data = 1; + break; + } + } + else { + break; + } + } + + *stream_tried_and_failed = (!more_stream_data && bytes_next == bytes_previous); + *more_data |= more_stream_data; + + return bytes_next; +} + +/* Format the stream frames that were queued for retransmit */ + +uint8_t* picoquic_format_stream_frame_for_retransmit(picoquic_cnx_t* cnx, + uint8_t* bytes_next, uint8_t* bytes_max, int* is_pure_ack) +{ + picoquic_misc_frame_header_t* misc = cnx->stream_frame_retransmit_queue; + uint8_t* frame = ((uint8_t*)misc) + sizeof(picoquic_misc_frame_header_t); + uint64_t stream_id; + uint64_t offset; + size_t data_length; + size_t consumed; + int fin; + int all_sent = 0; + + if (picoquic_parse_stream_header(frame, misc->length, &stream_id, &offset, &data_length, &fin, &consumed) != 0) { + /* Malformed stream frame. Log an error, and ignore. */ + picoquic_log_app_message(cnx, "Malformed copied stream frame, type %d, length %zu", + frame[0], misc->length); + all_sent = 1; + } + else { + uint8_t* bytes_first = bytes_next; + size_t available = bytes_max - bytes_next; + picoquic_stream_head_t* stream = picoquic_find_stream(cnx, stream_id); + if (stream == NULL || stream->reset_sent || picoquic_check_sack_list(&stream->first_sack_item, offset, offset + data_length)) { + /* That frame is not needed anymore */ + all_sent = 1; + } else if (bytes_next + misc->length <= bytes_max) { + /* The frame can be copied in full */ + if ((frame[0] & 2) == 0) { + /* Length is not encoded. If it fits just fine, copy. Else, need to be smarter */ + size_t insert_pad = (bytes_max - bytes_next) - misc->length; + if (insert_pad <= 2) { + /* pad, and then copy frame */ + while (insert_pad > 0) { + *bytes_next = 0; + bytes_next++; + insert_pad--; + } + memcpy(bytes_next, frame, misc->length); + bytes_next += misc->length; + } + else { + /* Need to reencode the header, then copy */ + if ((bytes_next = picoquic_format_stream_frame_header(bytes_next, bytes_max, stream_id, offset)) != NULL && + (bytes_next = picoquic_frames_varint_encode(bytes_next, bytes_max, data_length)) != NULL) { + memcpy(bytes_next, frame + consumed, data_length); + bytes_next += data_length; + *bytes_first |= 2; /* length present */ + *bytes_first |= fin; + } + else { + bytes_next = bytes_first; + } + } + } + else { + memcpy(bytes_next, frame, misc->length); + bytes_next += misc->length; + } + all_sent = 1; + *is_pure_ack = 0; + } + else { + int success = 0; + if (available > consumed && + (bytes_next = picoquic_format_stream_frame_header(bytes_next, bytes_max, stream_id, offset)) != NULL) { + uint8_t* after_length = picoquic_frames_varint_encode(bytes_next, bytes_max, available - 2); + if (after_length != NULL && after_length < bytes_max && + (available = bytes_max - after_length) > 0) { + size_t remain = data_length - available; + uint8_t trial_pad[32] = { 0 }; /* max header = 1 + 8(stream) + 8(offset) +8(length)*/ + uint8_t* trial_max = trial_pad + sizeof(trial_pad); + uint8_t* trial_next; + size_t trial_size = 0; + + if ((trial_next = picoquic_format_stream_frame_header(trial_pad, trial_max, stream_id, offset + available)) != NULL && + (trial_next = picoquic_frames_varint_encode(trial_next, trial_max, remain)) != NULL && + (trial_size = trial_next - trial_pad) <= consumed + available) { + /* There are enough bytes available to reformat the frame after copying */ + /* Finish encoding the copied bytes */ + bytes_next = picoquic_frames_varint_encode(bytes_next, bytes_max, available); + *bytes_first |= 2; + memcpy(bytes_next, frame + consumed, available); + bytes_next += available; + /* Reformat the stored bytes to only keep the remains */ + trial_pad[0] |= 2; + memcpy(frame, trial_pad, trial_size); + memmove(frame + trial_size, frame + consumed + available, remain); + misc->length = trial_size + remain; + frame[0] |= fin; + success = 1; + *is_pure_ack = 0; + } + } + } + + if (!success) { + bytes_next = bytes_first; + } + } + } + + if (all_sent) { + picoquic_delete_misc_or_dg(&cnx->stream_frame_retransmit_queue, &cnx->stream_frame_retransmit_queue_last, misc); + } + + return bytes_next; +} + +uint8_t* picoquic_format_stream_frames_queued_for_retransmit(picoquic_cnx_t* cnx, + uint8_t* bytes_next, uint8_t* bytes_max, int* more_data, int* is_pure_ack) +{ + picoquic_misc_frame_header_t* misc; + + while ((misc = cnx->stream_frame_retransmit_queue) != NULL && bytes_next < bytes_max) { + bytes_next = picoquic_format_stream_frame_for_retransmit(cnx, bytes_next, bytes_max, is_pure_ack); + if (misc == cnx->stream_frame_retransmit_queue) { + break; + } + } + + *more_data |= (cnx->stream_frame_retransmit_queue != NULL); + + return bytes_next; +} + +/* + * Crypto HS frames + */ + +int picoquic_is_tls_stream_ready(picoquic_cnx_t* cnx) +{ + int ret = 0; + + for (int epoch = 0; epoch < 4; epoch++) { + picoquic_stream_head_t* stream = &cnx->tls_stream[epoch]; + + if (stream->send_queue != NULL && + stream->send_queue->length > stream->send_queue->offset && + cnx->crypto_context[epoch].aead_encrypt != NULL) { + ret = 1; + break; + } + } + + return ret; +} + + +const uint8_t* picoquic_decode_crypto_hs_frame(picoquic_cnx_t* cnx, const uint8_t* bytes, const uint8_t* bytes_max, int epoch) +{ + uint64_t offset; + uint64_t data_length; + int new_data_available; // Unused + + if ((bytes = picoquic_frames_varint_decode(bytes+1, bytes_max, &offset)) == NULL || + (bytes = picoquic_frames_varint_decode(bytes, bytes_max, &data_length)) == NULL ) + { + picoquic_connection_error(cnx, PICOQUIC_TRANSPORT_FRAME_FORMAT_ERROR, picoquic_frame_type_crypto_hs); + + } else if ((uint64_t)(bytes_max - bytes) < data_length) { + DBG_PRINTF("crypto hs data past the end of the packet: data_length=%" PRIst ", remaining_space=%" PRIst, data_length, bytes_max - bytes); + picoquic_connection_error(cnx, PICOQUIC_TRANSPORT_FRAME_FORMAT_ERROR, picoquic_frame_type_crypto_hs); + bytes = NULL; + + } else { + picoquic_stream_head_t* stream = &cnx->tls_stream[epoch]; + int ret = picoquic_queue_network_input(&stream->stream_data_tree, stream->consumed_offset, + offset, bytes, (size_t)data_length, &new_data_available); + if (ret != 0) { + picoquic_connection_error(cnx, (int16_t)ret, picoquic_frame_type_crypto_hs); + bytes = NULL; + } else { + bytes += data_length; + } + } + + return bytes; +} + +uint8_t* picoquic_format_crypto_hs_frame(picoquic_stream_head_t* stream, uint8_t* bytes, uint8_t* bytes_max, int* more_data, int* is_pure_ack) +{ + uint8_t* bytes0 = bytes; + + if (stream->send_queue != NULL && stream->send_queue->length > stream->send_queue->offset) { + /* Check that there is enough room for at least 2 content bytes */ + if ((bytes = picoquic_frames_uint8_encode(bytes, bytes_max, picoquic_frame_type_crypto_hs)) != NULL && + (bytes = picoquic_frames_varint_encode(bytes, bytes_max, stream->sent_offset)) != NULL) { + /* As there is enough room, estimate the actual length, then encode the packet */ + size_t length = stream->send_queue->length - (size_t)stream->send_queue->offset; + uint8_t* bytes_l; + + if (bytes + length > bytes_max) { + length = bytes_max - bytes; + } + + if ((bytes_l = picoquic_frames_varint_encode(bytes, bytes_max, length)) == NULL) { + /* *more_data = 1; */ + bytes = bytes0; + } + else { + if (bytes_l + length > bytes_max) { + length = bytes_max - bytes_l; + bytes = picoquic_frames_varint_encode(bytes, bytes_max, length); + } + else { + bytes = bytes_l; + } + if (bytes != NULL && length > 0) { + memcpy(bytes, stream->send_queue->bytes + stream->send_queue->offset, length); + bytes += length; + + stream->send_queue->offset += length; + if (stream->send_queue->offset >= stream->send_queue->length) { + picoquic_stream_data_node_t* next = stream->send_queue->next_stream_data; + free(stream->send_queue->bytes); + free(stream->send_queue); + stream->send_queue = next; + } + + stream->sent_offset += length; + *is_pure_ack = 0; + } + } + } + else { + /* *more_data = 1; */ + bytes = bytes0; + } + } + + return bytes; +} + + +/* + * ACK Frames + */ + +int picoquic_parse_ack_header(uint8_t const* bytes, size_t bytes_max, + uint64_t* num_block, + uint64_t* largest, uint64_t* ack_delay, size_t* consumed, + uint8_t ack_delay_exponent) +{ + int ret = 0; + size_t byte_index = 1; + size_t l_largest = 0; + size_t l_delay = 0; + size_t l_blocks = 0; + + if (bytes_max > byte_index) { + l_largest = picoquic_varint_decode(bytes + byte_index, bytes_max - byte_index, largest); + byte_index += l_largest; + } + + if (bytes_max > byte_index) { + l_delay = picoquic_varint_decode(bytes + byte_index, bytes_max - byte_index, ack_delay); + *ack_delay <<= ack_delay_exponent; + byte_index += l_delay; + } + + if (bytes_max > byte_index) { + l_blocks = picoquic_varint_decode(bytes + byte_index, bytes_max - byte_index, num_block); + byte_index += l_blocks; + } + + if (l_largest == 0 || l_delay == 0 || l_blocks == 0 || bytes_max < byte_index) { + DBG_PRINTF("ack frame fixed header too large: first_byte=0x%02x, bytes_max=%" PRIst, + bytes[0], bytes_max); + byte_index = bytes_max; + ret = -1; + } + + *consumed = byte_index; + return ret; +} + + +picoquic_packet_t* picoquic_check_spurious_retransmission(picoquic_cnx_t* cnx, + uint64_t start_of_range, uint64_t end_of_range, uint64_t current_time, + picoquic_packet_context_enum pc, picoquic_packet_t* p) +{ + picoquic_packet_context_t * pkt_ctx = &cnx->pkt_ctx[pc]; + + while (p != NULL && p->sequence_number >= start_of_range) { + picoquic_packet_t* should_delete = NULL; + + if ( p->sequence_number <= end_of_range) { + + uint64_t max_spurious_rtt = current_time - p->send_time; + uint64_t max_reorder_delay = pkt_ctx->latest_time_acknowledged - p->send_time; + uint64_t max_reorder_gap = pkt_ctx->highest_acknowledged - p->sequence_number; + picoquic_path_t * old_path = p->send_path; + + if (old_path != NULL) { + if (p->length + p->checksum_overhead > old_path->send_mtu) { + old_path->send_mtu = p->length + p->checksum_overhead; + if (old_path->send_mtu > old_path->send_mtu_max_tried) { + old_path->send_mtu_max_tried = old_path->send_mtu; + } + old_path->mtu_probe_sent = 0; + } + + if (max_spurious_rtt > old_path->max_spurious_rtt) { + old_path->max_spurious_rtt = max_spurious_rtt; + } + + if (max_reorder_delay > old_path->max_reorder_delay) { + old_path->max_reorder_delay = max_reorder_delay; + } + + if (max_reorder_gap > old_path->max_reorder_gap) { + old_path->max_reorder_gap = max_reorder_gap; + } + + if (old_path->smoothed_rtt == PICOQUIC_INITIAL_RTT && old_path->rtt_variant == 0) { + /* If the RTT has not been set, use it to update the path RTT */ + picoquic_update_path_rtt(cnx, old_path, p->send_time, current_time, 0); + } + + if (old_path->nb_losses_found > 0) { + old_path->nb_losses_found--; + } + + if (old_path->total_bytes_lost > p->length) { + old_path->total_bytes_lost -= p->length; + } + else { + old_path->total_bytes_lost = 0; + } + + if (cnx->congestion_alg != NULL) { + cnx->congestion_alg->alg_notify(cnx, old_path, picoquic_congestion_notification_spurious_repeat, + 0, 0, 0, p->sequence_number, current_time); + } + } + + cnx->nb_spurious++; + should_delete = p; + } + + p = p->previous_packet; + + if (should_delete != NULL) { + picoquic_dequeue_retransmitted_packet(cnx, should_delete); + } + } + + return p; +} + +void picoquic_dequeue_old_retransmitted_packets(picoquic_cnx_t* cnx, picoquic_packet_context_enum pc) +{ + picoquic_packet_t* p = cnx->pkt_ctx[pc].retransmitted_oldest; + + if (p != NULL) { + uint64_t oldest_possible = cnx->pkt_ctx[pc].latest_time_acknowledged; + + if (oldest_possible > PICOQUIC_SPURIOUS_RETRANSMIT_DELAY_MAX) { + oldest_possible -= PICOQUIC_SPURIOUS_RETRANSMIT_DELAY_MAX; + + while (p != NULL && p->send_time < oldest_possible) { + picoquic_packet_t* should_delete = p; + + p = p->next_packet; + + if (should_delete != NULL) { + picoquic_dequeue_retransmitted_packet(cnx, should_delete); + } + } + } + } +} + +void picoquic_estimate_path_bandwidth(picoquic_cnx_t * cnx, picoquic_path_t* path_x, uint64_t send_time, + uint64_t delivered_prior, uint64_t delivered_time_prior, uint64_t delivered_sent_prior, + uint64_t delivery_time, uint64_t current_time, int rs_is_path_limited) +{ + if (send_time >= path_x->delivered_sent_last) { + if (path_x->delivered_time_last == 0) { + /* No estimate yet, need to initialize the variables */ + path_x->delivered_last = path_x->delivered; + path_x->delivered_time_last = delivery_time; + path_x->delivered_sent_last = send_time; + } + else { + uint64_t receive_interval = delivery_time - delivered_time_prior; + + if (receive_interval > PICOQUIC_BANDWIDTH_TIME_INTERVAL_MIN) { + uint64_t delivered = path_x->delivered - delivered_prior; + uint64_t send_interval = send_time - delivered_sent_prior; + uint64_t bw_estimate; + + if (send_interval > receive_interval) { + receive_interval = send_interval; + } + + bw_estimate = delivered * 1000000; + bw_estimate /= receive_interval; + + if (!rs_is_path_limited || bw_estimate > path_x->bandwidth_estimate) { + path_x->bandwidth_estimate = bw_estimate; + if (path_x == cnx->path[0]){ + if (cnx->is_ack_frequency_negotiated && + cnx->ack_gap_local != picoquic_compute_ack_gap(cnx, bw_estimate)){ + cnx->is_ack_frequency_updated = 1; + } + } + } + + /* Bandwidth was estimated, update the references */ + path_x->delivered_last = path_x->delivered; + path_x->delivered_time_last = delivery_time; + path_x->delivered_sent_last = send_time; + path_x->delivered_last_packet = delivered_prior; + path_x->last_bw_estimate_path_limited = rs_is_path_limited; + if (path_x->delivered > path_x->delivered_limited_index) { + path_x->delivered_limited_index = 0; + } + } + } + } +} + +void picoquic_estimate_max_path_bandwidth(picoquic_cnx_t* cnx, picoquic_path_t* path_x, uint64_t send_time, + uint64_t delivery_time, uint64_t current_time) +{ + /* Test whether there is enough time since the last max bandwidth estimate */ + if (send_time >= path_x->max_sample_sent_time) { + if (path_x->max_sample_sent_time == 0) { + /* No sample set yet, need to initialize the variables */ + path_x->max_sample_delivered = path_x->delivered; + path_x->max_sample_acked_time = delivery_time; + path_x->max_sample_sent_time = send_time; + } + else { + /* Compute a max bandwidth estimate */ + uint64_t receive_interval = delivery_time - path_x->max_sample_acked_time; + + if (receive_interval > PICOQUIC_MAX_BANDWIDTH_TIME_INTERVAL_MIN) { + uint64_t delivered = path_x->delivered - path_x->max_sample_delivered; + uint64_t send_interval = send_time - path_x->max_sample_sent_time; + uint64_t bw_estimate; + + if (send_interval > receive_interval) { + receive_interval = send_interval; + } + + bw_estimate = delivered * 1000000; + bw_estimate /= receive_interval; + /* Retain if larger than previous estimate */ + if (bw_estimate > path_x->max_bandwidth_estimate) { + path_x->max_bandwidth_estimate = bw_estimate; + } + + /* Change the reference point if estimate duration is long enough */ + path_x->max_sample_delivered = path_x->delivered; + path_x->max_sample_acked_time = delivery_time; + path_x->max_sample_sent_time = send_time; + } + } + } +} + +/* Compute the desired number of packets coalesce in a single ACK. + * This will be used to compute the value sent to the peer in the ACK FREQUENCY frame, + * using the bandwidth estimate computed from received ACKs. + * When the ACK FREQUENCY is not negotiated, this will be computed locally, + * using the estimated received rate. + * The computed value is only used if it is not overriden: + * - For the Initial and Handshake contexts the gap is always 1. + * - If "ack_after_fin" is set the gap is always 1. + * - If packets are received out of order and the peer is sensitive, the gap is 1. + */ + +uint64_t picoquic_compute_ack_gap(picoquic_cnx_t* cnx, uint64_t data_rate) +{ + uint64_t ack_gap = 1; + + if (data_rate > PICOQUIC_BANDWIDTH_MEDIUM) { + if (cnx->path[0]->rtt_min > PICOQUIC_TARGET_RENO_RTT) { + ack_gap = 10; + } + else { + ack_gap = 4; + } + } + else { + ack_gap = 2; + } + + return ack_gap; +} + +uint64_t picoquic_compute_ack_delay_max(uint64_t rtt, uint64_t remote_min_ack_delay) +{ + uint64_t ack_delay_max = rtt / 4; + + if (ack_delay_max > PICOQUIC_ACK_DELAY_MAX) { + ack_delay_max = PICOQUIC_ACK_DELAY_MAX; + } + + if (ack_delay_max < remote_min_ack_delay) { + ack_delay_max = remote_min_ack_delay; + } + return ack_delay_max; +} + +void picoquic_update_1wd(picoquic_cnx_t * cnx, picoquic_path_t * old_path, + uint64_t send_time, uint64_t ack_delay, uint64_t remote_time_stamp) +{ + int64_t one_way_delay = 0; + + if (remote_time_stamp > 0) { + int64_t time_stamp_local = remote_time_stamp - ack_delay + cnx->start_time + old_path->phase_delay; + + one_way_delay = time_stamp_local - send_time; + + if (one_way_delay < 0) { + int64_t correct_1wd = old_path->rtt_sample / 2; + picoquic_log_app_message(cnx, + "BAD 1WD! RTS=%" PRIu64 ", AD=%"PRIu64 ", Start=%" PRIu64 ", Phi=%"PRIi64 ", Send=%" PRIu64 ", OWD=%"PRIu64 "\n", + remote_time_stamp, ack_delay, cnx->start_time, old_path->phase_delay, send_time, one_way_delay); + old_path->phase_delay += correct_1wd - one_way_delay; + one_way_delay = correct_1wd; + } + old_path->one_way_delay_sample = one_way_delay; + } +} + +void picoquic_update_path_rtt(picoquic_cnx_t* cnx, picoquic_path_t * old_path, uint64_t send_time, + uint64_t current_time, uint64_t ack_delay) +{ + uint64_t acknowledged_time = current_time - ack_delay; + int64_t rtt_estimate = acknowledged_time - send_time; + + if (rtt_estimate > 0 && old_path != NULL) { + if (ack_delay > old_path->max_ack_delay) { + old_path->max_ack_delay = ack_delay; + } + + if (rtt_estimate > 2000000) { + DBG_PRINTF("Measured RTT = %llu", (unsigned long long)rtt_estimate); + } + + if (old_path->smoothed_rtt == PICOQUIC_INITIAL_RTT && old_path->rtt_variant == 0) { + old_path->smoothed_rtt = rtt_estimate; + old_path->rtt_variant = rtt_estimate / 2; + old_path->phase_delay = rtt_estimate / 2; + + if (!cnx->client_mode) { + old_path->phase_delay = -old_path->phase_delay; + } + + old_path->rtt_min = rtt_estimate; + old_path->retransmit_timer = 3 * rtt_estimate + + cnx->remote_parameters.max_ack_delay; + if (old_path == cnx->path[0]) { + /* Only update the ack delay upon measuring the default path */ + cnx->is_ack_frequency_updated = cnx->is_ack_frequency_negotiated; + if (!cnx->is_ack_frequency_negotiated || cnx->cnx_state != picoquic_state_ready) { + cnx->ack_delay_remote = picoquic_compute_ack_delay_max(old_path->rtt_min, PICOQUIC_ACK_DELAY_MIN); + } + } + } + else { + /* Computation per RFC 6298 */ + int64_t delta_rtt = rtt_estimate - old_path->smoothed_rtt; + int64_t delta_rtt_average = 0; + old_path->smoothed_rtt += delta_rtt / 8; + + if (delta_rtt < 0) { + delta_rtt_average = (-delta_rtt) - old_path->rtt_variant; + } + else { + delta_rtt_average = delta_rtt - old_path->rtt_variant; + } + old_path->rtt_variant += delta_rtt_average / 4; + + if (rtt_estimate < (int64_t)old_path->rtt_min) { + old_path->rtt_min = rtt_estimate; + + if (old_path == cnx->path[0]) { + cnx->is_ack_frequency_updated = cnx->is_ack_frequency_negotiated; + if (!cnx->is_ack_frequency_negotiated || cnx->cnx_state != picoquic_state_ready) { + cnx->ack_delay_remote = picoquic_compute_ack_delay_max(old_path->rtt_min, PICOQUIC_ACK_DELAY_MIN); + } + } + } + + if (4 * old_path->rtt_variant < old_path->rtt_min && + old_path->rtt_min < PICOQUIC_TARGET_SATELLITE_RTT) { + old_path->rtt_variant = old_path->rtt_min / 4; + } + + old_path->retransmit_timer = old_path->smoothed_rtt + 4 * old_path->rtt_variant + + cnx->remote_parameters.max_ack_delay; + } + old_path->rtt_sample = rtt_estimate; + + if (PICOQUIC_MIN_RETRANSMIT_TIMER > old_path->retransmit_timer) { + old_path->retransmit_timer = PICOQUIC_MIN_RETRANSMIT_TIMER; + } + + if (cnx->congestion_alg != NULL && !cnx->is_time_stamp_enabled) { + cnx->congestion_alg->alg_notify(cnx, old_path, + picoquic_congestion_notification_rtt_measurement, + rtt_estimate, 0, 0, 0, current_time); + } + } +} + +static picoquic_packet_t* picoquic_find_acked_packet(picoquic_cnx_t* cnx, uint64_t largest, + uint64_t current_time, uint64_t ack_delay, uint64_t remote_time_stamp, picoquic_packet_context_enum pc, int* is_new_ack) +{ + picoquic_packet_context_t* pkt_ctx = &cnx->pkt_ctx[pc]; + picoquic_packet_t* packet = pkt_ctx->retransmit_oldest; + + /* Check whether this is a new acknowledgement */ + if (largest > pkt_ctx->highest_acknowledged || pkt_ctx->highest_acknowledged == (uint64_t)((int64_t)-1)) { + pkt_ctx->highest_acknowledged = largest; + pkt_ctx->highest_acknowledged_time = current_time; + pkt_ctx->ack_of_ack_requested = 0; + *is_new_ack = 1; + + if (ack_delay < PICOQUIC_ACK_DELAY_MAX) { + /* if the ACK is reasonably recent, use it to update the RTT */ + /* find the stored copy of the largest acknowledged packet */ + + while (packet != NULL && packet->previous_packet != NULL && packet->sequence_number < largest) { + packet = packet->previous_packet; + } + + if (packet == NULL || packet->sequence_number != largest) { + /* There is no copy of this packet in store. It may have + * been deleted because too old, or maybe already + * retransmitted */ + } + else { + picoquic_path_t* old_path = packet->send_path; + + if (old_path != NULL) { + picoquic_update_path_rtt(cnx, old_path, packet->send_time, current_time, ack_delay); + } + } + } + else { + *is_new_ack = 0; + } + } + + return packet; +} + +static picoquic_sack_item_t* picoquic_process_ack_of_ack_range(picoquic_sack_item_t* first_sack, picoquic_sack_item_t* previous, + uint64_t start_of_range, uint64_t end_of_range) +{ + picoquic_sack_item_t* next = (previous == NULL)? first_sack: previous->next_sack; + + while (next != NULL) { + if (next->start_of_sack_range == start_of_range) { + if (next == first_sack) { + if (end_of_range < first_sack->end_of_sack_range) { + first_sack->start_of_sack_range = end_of_range + 1; + } + else { + first_sack->start_of_sack_range = first_sack->end_of_sack_range; + } + } + else if (next->end_of_sack_range == end_of_range) { + /* Matching range should be removed */ + previous->next_sack = next->next_sack; + free(next); + } + break; + } else if (next->end_of_sack_range > end_of_range) { + previous = next; + next = next->next_sack; + } + else { + break; + } + } + + return previous; +} + +int picoquic_process_ack_of_ack_frame( + picoquic_sack_item_t* first_sack, + uint8_t* bytes, size_t bytes_max, size_t* consumed, int is_ecn) +{ + int ret; + uint64_t largest; + uint64_t ack_delay; + uint64_t num_block; + + ret = picoquic_parse_ack_header(bytes, bytes_max, + &num_block, + &largest, &ack_delay, consumed, 0); + + if (ret == 0) { + size_t byte_index = *consumed; + picoquic_sack_item_t* previous_sack_item = NULL; + + /* Process each successive range */ + + while (1) { + uint64_t range; + size_t l_range; + uint64_t block_to_block; + + if (byte_index >= bytes_max) { + ret = -1; + break; + } + + l_range = picoquic_varint_decode(bytes + byte_index, bytes_max - byte_index, &range); + if (l_range == 0) { + byte_index = bytes_max; + ret = -1; + break; + } else { + byte_index += l_range; + } + + range++; + if (largest + 1 < range) { + DBG_PRINTF("ack range error: largest=%" PRIx64 ", range=%" PRIx64, largest, range); + ret = -1; + break; + } + + if (range > 0) { + previous_sack_item = picoquic_process_ack_of_ack_range(first_sack, previous_sack_item, largest + 1 - range, largest); + } + + if (num_block-- == 0) + break; + + /* Skip the gap */ + + if (byte_index >= bytes_max) { + ret = -1; + break; + } else { + size_t l_gap = picoquic_varint_decode(bytes + byte_index, bytes_max - byte_index, &block_to_block); + if (l_gap == 0) { + byte_index = bytes_max; + ret = -1; + break; + } else { + byte_index += l_gap; + block_to_block += 1; /* Add 1, since there are never 0 gaps -- see spec. */ + block_to_block += range; + } + } + + if (largest < block_to_block) { + DBG_PRINTF("ack gap error: largest=%" PRIx64 ", range=%" PRIx64 ", gap=%" PRIu64, + largest, range, block_to_block - range); + ret = -1; + break; + } + + largest -= block_to_block; + } + + if (ret == 0 && is_ecn) { + if (byte_index >= bytes_max) { + ret = -1; + } + else { + for (int ecnx = 0; ecnx < 3; ecnx++) { + uint64_t ecn; + size_t l_ecn = picoquic_varint_decode(bytes + byte_index, bytes_max - byte_index, &ecn); + if (l_ecn == 0) { + byte_index = bytes_max; + ret = -1; + break; + } + else { + byte_index += l_ecn; + } + } + } + } + + *consumed = byte_index; + } + + return ret; +} + +int picoquic_check_frame_needs_repeat(picoquic_cnx_t* cnx, const uint8_t* bytes, + size_t bytes_max, int* no_need_to_repeat) +{ + int ret = 0; + int fin; + size_t data_length; + uint64_t stream_id; + uint64_t offset; + uint64_t maxdata; + uint64_t max_stream_rank; + picoquic_stream_head_t* stream = NULL; + size_t consumed = 0; + + *no_need_to_repeat = 0; + + if (PICOQUIC_IN_RANGE(bytes[0], picoquic_frame_type_stream_range_min, picoquic_frame_type_stream_range_max)) { + ret = picoquic_parse_stream_header(bytes, bytes_max, + &stream_id, &offset, &data_length, &fin, &consumed); + + if (ret == 0) { + stream = picoquic_find_stream(cnx, stream_id); + if (stream == NULL) { + /* the stream was destroyed. That only happens if it was fully acked. */ + *no_need_to_repeat = 1; + } + else { + if (stream->reset_sent) { + *no_need_to_repeat = 1; + } + else { + /* Check whether the ack was already received */ + *no_need_to_repeat = picoquic_check_sack_list(&stream->first_sack_item, offset, offset + data_length); + } + } + } + } + else { + const uint8_t* p_last_byte = bytes + bytes_max; + switch (bytes[0]) { + case picoquic_frame_type_max_data: + if ((bytes = picoquic_frames_varint_decode(bytes + 1, p_last_byte, &maxdata)) == NULL) { + /* Malformed frame, do not retransmit */ + *no_need_to_repeat = 1; + } + else if (maxdata < cnx->maxdata_local) { + /* already updated */ + *no_need_to_repeat = 1; + } + break; + case picoquic_frame_type_max_stream_data: + if ((bytes = picoquic_frames_varint_decode(bytes + 1, p_last_byte, &stream_id)) == NULL || + (bytes = picoquic_frames_varint_decode(bytes, p_last_byte, &maxdata)) == NULL) { + /* Malformed frame, do not retransmit */ + *no_need_to_repeat = 1; + } + else if ((stream = picoquic_find_stream(cnx, stream_id)) == NULL) { + /* No such stream do not retransmit */ + *no_need_to_repeat = 1; + } + else if (stream->fin_received || stream->reset_received || stream->stop_sending_sent) { + /* Stream stopped, no need to increase the window */ + *no_need_to_repeat = 1; + } + else if (maxdata < stream->maxdata_local) { + /* Stream max data already increased */ + *no_need_to_repeat = 1; + } + break; + case picoquic_frame_type_max_streams_bidir: + case picoquic_frame_type_max_streams_unidir: + if ((bytes = picoquic_frames_varint_decode(bytes + 1, p_last_byte, &max_stream_rank)) == NULL) { + /* Malformed frame, do not retransmit */ + *no_need_to_repeat = 1; + } + else if (bytes[0] == picoquic_frame_type_max_streams_bidir && + cnx->max_stream_id_bidir_local > STREAM_ID_FROM_RANK(max_stream_rank, !cnx->client_mode, 0)) { + /* Streams bidir already increased */ + *no_need_to_repeat = 1; + } + else if (cnx->max_stream_id_unidir_local > STREAM_ID_FROM_RANK(max_stream_rank, !cnx->client_mode, 1)) { + /* Streams unidir already increased */ + *no_need_to_repeat = 1; + } + break; + case picoquic_frame_type_data_blocked: + if ((bytes = picoquic_frames_varint_decode(bytes + 1, p_last_byte, &maxdata)) == NULL) { + /* Malformed frame, do not retransmit */ + *no_need_to_repeat = 1; + } + else if (maxdata < cnx->maxdata_remote) { + /* already updated */ + *no_need_to_repeat = 1; + } + else { + /* Only repeat if the sent flag is still there */ + *no_need_to_repeat = !cnx->sent_blocked_frame; + } + break; + case picoquic_frame_type_streams_blocked_bidir: + if ((bytes = picoquic_frames_varint_decode(bytes + 1, p_last_byte, &max_stream_rank)) == NULL) { + /* Malformed frame, do not retransmit */ + *no_need_to_repeat = 1; + } + else if (cnx->max_stream_id_bidir_remote > STREAM_ID_FROM_RANK(max_stream_rank, !cnx->client_mode, 0)) { + /* Streams bidir already increased */ + *no_need_to_repeat = 1; + } + else { + /* Only repeat if the sent flag is still there */ + *no_need_to_repeat = !cnx->stream_blocked_bidir_sent; + } + break; + case picoquic_frame_type_streams_blocked_unidir: + if ((bytes = picoquic_frames_varint_decode(bytes + 1, p_last_byte, &max_stream_rank)) == NULL) { + /* Malformed frame, do not retransmit */ + *no_need_to_repeat = 1; + } + else if (cnx->max_stream_id_unidir_remote > STREAM_ID_FROM_RANK(max_stream_rank, !cnx->client_mode, 1)) { + /* Streams unidir already increased */ + *no_need_to_repeat = 1; + } + else { + /* Only repeat if the sent flag is still there */ + *no_need_to_repeat = !cnx->stream_blocked_unidir_sent; + } + break; + case picoquic_frame_type_stream_data_blocked: + if ((bytes = picoquic_frames_varint_decode(bytes + 1, p_last_byte, &stream_id)) == NULL || + (bytes = picoquic_frames_varint_decode(bytes, p_last_byte, &maxdata)) == NULL) { + /* Malformed frame, do not retransmit */ + *no_need_to_repeat = 1; + } + else if ((stream = picoquic_find_stream(cnx, stream_id)) == NULL) { + /* No such stream do not retransmit */ + *no_need_to_repeat = 1; + } + else if (stream->fin_requested || stream->reset_requested || stream->fin_sent || stream->reset_sent) { + /* Stream stopped, no need to increase the window */ + *no_need_to_repeat = 1; + } + else if (maxdata < stream->maxdata_remote || !stream->stream_data_blocked_sent) { + /* Stream max data already increased */ + *no_need_to_repeat = 1; + } + break; + case picoquic_frame_type_path_challenge: + /* Path challenge repeat follows its own logic. */ + *no_need_to_repeat = 1; + break; + default: { + uint64_t frame_id64; + *no_need_to_repeat = 0; + if ((bytes = picoquic_frames_varint_decode(bytes, bytes + bytes_max, &frame_id64)) != NULL) { + switch (frame_id64) { + case picoquic_frame_type_ack_frequency: { + uint64_t seq; + uint64_t packets; + uint64_t microsec; + + if ((bytes = picoquic_parse_ack_frequency_frame(bytes, bytes + bytes_max, &seq, &packets, µsec)) != NULL && + seq == cnx->ack_frequency_sequence_local) { + *no_need_to_repeat = 1; + } + break; + } + case picoquic_frame_type_time_stamp: + *no_need_to_repeat = 1; + break; + default: + break; + } + } + break; + } + } + } + + return ret; +} + +static int picoquic_process_ack_of_stream_frame(picoquic_cnx_t* cnx, uint8_t* bytes, + size_t bytes_max, size_t* consumed) +{ + int ret; + int fin; + size_t data_length; + uint64_t stream_id; + uint64_t offset; + picoquic_stream_head_t* stream = NULL; + + /* skip stream frame */ + ret = picoquic_parse_stream_header(bytes, bytes_max, + &stream_id, &offset, &data_length, &fin, consumed); + + if (ret == 0) { + *consumed += data_length; + + /* record the ack range for the stream */ + stream = picoquic_find_stream(cnx, stream_id); + if (stream != NULL) { + (void)picoquic_update_sack_list(&stream->first_sack_item, + offset, offset + data_length - 1); + + picoquic_delete_stream_if_closed(cnx, stream); + } + } + + return ret; +} + +void picoquic_process_possible_ack_of_ack_frame(picoquic_cnx_t* cnx, picoquic_packet_t* p, uint64_t current_time) +{ + int ret = 0; + size_t byte_index; + int frame_is_pure_ack = 0; + size_t frame_length = 0; + + if (p->ptype == picoquic_packet_0rtt_protected) { + cnx->nb_zero_rtt_acked++; + } + + byte_index = p->offset; + + while (ret == 0 && byte_index < p->length) { + if (p->bytes[byte_index] == picoquic_frame_type_ack) { + ret = picoquic_process_ack_of_ack_frame(&cnx->pkt_ctx[p->pc].first_sack_item, + &p->bytes[byte_index], p->length - byte_index, &frame_length, 0); + byte_index += frame_length; + } else if (p->bytes[byte_index] == picoquic_frame_type_ack_ecn) { + ret = picoquic_process_ack_of_ack_frame(&cnx->pkt_ctx[p->pc].first_sack_item, + &p->bytes[byte_index], p->length - byte_index, &frame_length, 1); + byte_index += frame_length; + } + else if (PICOQUIC_IN_RANGE(p->bytes[byte_index], picoquic_frame_type_stream_range_min, picoquic_frame_type_stream_range_max)) { + ret = picoquic_process_ack_of_stream_frame(cnx, &p->bytes[byte_index], p->length - byte_index, &frame_length); + byte_index += frame_length; + if (p->send_path != NULL && p->send_time > p->send_path->last_time_acked_data_frame_sent) { + p->send_path->last_time_acked_data_frame_sent = p->send_time; + } + } else { + if (PICOQUIC_IN_RANGE(p->bytes[byte_index], picoquic_frame_type_datagram, picoquic_frame_type_datagram_l) && + p->send_path != NULL && p->send_time > p->send_path->last_time_acked_data_frame_sent) { + p->send_path->last_time_acked_data_frame_sent = p->send_time; + } + + ret = picoquic_skip_frame(&p->bytes[byte_index], + p->length - byte_index, &frame_length, &frame_is_pure_ack); + byte_index += frame_length; + } + } +} + +static int picoquic_process_ack_range( + picoquic_cnx_t* cnx, picoquic_packet_context_enum pc, uint64_t highest, uint64_t range, picoquic_packet_t** ppacket, + uint64_t current_time) +{ + picoquic_packet_t* p = *ppacket; + int ret = 0; + + /* Compare the range to the retransmit queue */ + while (p != NULL && range > 0) { + if (p->sequence_number > highest) { + p = p->next_packet; + } else { + if (p->sequence_number == highest) { + /* TODO: RTT Estimate */ + picoquic_packet_t* next = p->next_packet; + picoquic_path_t * old_path = p->send_path; + + if (p->is_ack_trap) { + ret = picoquic_connection_error(cnx, PICOQUIC_TRANSPORT_PROTOCOL_VIOLATION, picoquic_frame_type_ack); + break; + } + + if (old_path != NULL) { + old_path->delivered += p->length; + + if (cnx->congestion_alg != NULL) { +#if 0 + if (cnx->pkt_ctx[pc].nb_retransmit >= 2 && p->sequence_number >= cnx->pkt_ctx[pc].retransmit_sequence) { + cnx->congestion_alg->alg_notify(cnx, old_path, + picoquic_congestion_notification_reset, + 0, 0, p->length, 0, current_time); + } +#endif + cnx->congestion_alg->alg_notify(cnx, old_path, + picoquic_congestion_notification_acknowledgement, + 0, 0, p->length, 0, current_time); + } + + + /* If packet is larger than the current MTU, update the MTU */ + if ((p->length + p->checksum_overhead) == old_path->send_mtu) { + old_path->nb_mtu_losses = 0; + } else if ((p->length + p->checksum_overhead) > old_path->send_mtu) { + old_path->send_mtu = p->length + p->checksum_overhead; + old_path->mtu_probe_sent = 0; + } + } + + /* If the packet contained an ACK frame, perform the ACK of ACK pruning logic */ + picoquic_process_possible_ack_of_ack_frame(cnx, p, current_time); + + /* Keep track of reception of ACK of 1RTT data */ + if (p->ptype == picoquic_packet_1rtt_protected && + (cnx->cnx_state == picoquic_state_client_ready_start || + cnx->cnx_state == picoquic_state_server_false_start)) { + /* Transition to client ready state. + * The handshake is complete, all the handshake packets are implicitly acknowledged */ + picoquic_ready_state_transition(cnx, current_time); + } + + if (cnx->pkt_ctx[pc].nb_retransmit > 0 && p->sequence_number >= cnx->pkt_ctx[pc].retransmit_sequence) { + /* Acknowledgement larger than retransmit number show progress */ + cnx->pkt_ctx[pc].nb_retransmit = 0; + } + + (void)picoquic_dequeue_retransmit_packet(cnx, p, 1); + p = next; + } + + range--; + highest--; + } + } + + *ppacket = p; + return ret; +} + +const uint8_t* picoquic_decode_ack_frame(picoquic_cnx_t* cnx, const uint8_t* bytes, + const uint8_t* bytes_max, uint64_t current_time, int epoch, int is_ecn, picoquic_packet_data_t* packet_data) +{ + uint64_t num_block; + uint64_t largest; + uint64_t ack_delay; + uint64_t remote_time_stamp = 0; + size_t consumed; + picoquic_packet_context_enum pc = picoquic_context_from_epoch(epoch); + uint64_t ecnx3[3] = { 0, 0, 0 }; + uint8_t first_byte = bytes[0]; + + if (picoquic_parse_ack_header(bytes, bytes_max-bytes, &num_block, + &largest, &ack_delay, &consumed, + cnx->remote_parameters.ack_delay_exponent) != 0) { + bytes = NULL; + picoquic_connection_error(cnx, PICOQUIC_TRANSPORT_FRAME_FORMAT_ERROR, first_byte); + } else if (largest >= cnx->pkt_ctx[pc].send_sequence) { + bytes = NULL; + picoquic_connection_error(cnx, PICOQUIC_TRANSPORT_PROTOCOL_VIOLATION, first_byte); + } else { + bytes += consumed; + + /* Attempt to update the RTT */ + int is_new_ack = 0; + picoquic_packet_t* top_packet = picoquic_find_acked_packet(cnx, largest, current_time, ack_delay, remote_time_stamp, pc, &is_new_ack); + picoquic_packet_t* p_retransmitted_previous = cnx->pkt_ctx[pc].retransmitted_newest; + + if (top_packet != NULL && is_new_ack) { + if (cnx->pkt_ctx[pc].latest_time_acknowledged < top_packet->send_time) { + cnx->pkt_ctx[pc].latest_time_acknowledged = top_packet->send_time; + } + cnx->latest_progress_time = current_time; + + if (packet_data != NULL) { + packet_data->acked_path = top_packet->send_path; + packet_data->last_ack_delay = ack_delay; + packet_data->largest_sent_time = top_packet->send_time; + packet_data->delivered_prior = top_packet->delivered_prior; + packet_data->delivered_time_prior = top_packet->delivered_time_prior; + packet_data->delivered_sent_prior = top_packet->delivered_sent_prior; + packet_data->rs_is_path_limited = top_packet->delivered_app_limited; + } + } + + do { + uint64_t range; + uint64_t block_to_block; + + if ((bytes = picoquic_frames_varint_decode(bytes, bytes_max, &range)) == NULL) { + DBG_PRINTF("Malformed ACK RANGE, %d blocks remain.\n", (int)num_block); + picoquic_connection_error(cnx, PICOQUIC_TRANSPORT_FRAME_FORMAT_ERROR, first_byte); + bytes = NULL; + break; + } + + range ++; + if (largest + 1 < range) { + DBG_PRINTF("ack range error: largest=%" PRIx64 ", range=%" PRIx64, largest, range); + picoquic_connection_error(cnx, PICOQUIC_TRANSPORT_FRAME_FORMAT_ERROR, first_byte); + bytes = NULL; + break; + } + + if (picoquic_process_ack_range(cnx, pc, largest, range, &top_packet, current_time) != 0) { + bytes = NULL; + break; + } + + if (range > 0) { + p_retransmitted_previous = picoquic_check_spurious_retransmission(cnx, largest + 1 - range, largest, current_time, pc, p_retransmitted_previous); + } + + if (num_block-- == 0) + break; + + /* Skip the gap */ + if ((bytes = picoquic_frames_varint_decode(bytes, bytes_max, &block_to_block)) == NULL) { + DBG_PRINTF(" Malformed ACK GAP, %d blocks remain.\n", (int)num_block); + picoquic_connection_error(cnx, PICOQUIC_TRANSPORT_FRAME_FORMAT_ERROR, first_byte); + bytes = NULL; + break; + } + + block_to_block += 1; /* add 1, since zero is ruled out by varint, see spec. */ + block_to_block += range; + + if (largest < block_to_block) { + DBG_PRINTF("ack gap error: largest=%" PRIx64 ", range=%" PRIx64 ", gap=%" PRIu64, + largest, range, block_to_block - range); + picoquic_connection_error(cnx, PICOQUIC_TRANSPORT_FRAME_FORMAT_ERROR, first_byte); + bytes = NULL; + break; + } + + largest -= block_to_block; + } while (bytes != NULL); + + picoquic_dequeue_old_retransmitted_packets(cnx, pc); + + } + + if (bytes != 0 && is_ecn) { + for (int ecnx = 0; bytes != NULL && ecnx < 3; ecnx++) { + bytes = picoquic_frames_varint_decode(bytes, bytes_max, &ecnx3[ecnx]); + } + } + + if (bytes != 0 && is_ecn) { + if (ecnx3[0] > cnx->pkt_ctx[pc].ecn_ect0_total_remote) { + cnx->pkt_ctx[pc].ecn_ect0_total_remote = ecnx3[0]; + } + if (ecnx3[1] > cnx->pkt_ctx[pc].ecn_ect1_total_remote) { + cnx->pkt_ctx[pc].ecn_ect1_total_remote = ecnx3[1]; + } + if (ecnx3[2] > cnx->pkt_ctx[pc].ecn_ce_total_remote) { + cnx->pkt_ctx[pc].ecn_ce_total_remote = ecnx3[2]; + + cnx->congestion_alg->alg_notify(cnx, cnx->path[0], + picoquic_congestion_notification_ecn_ec, + 0, 0, 0, cnx->pkt_ctx[pc].first_sack_item.end_of_sack_range, current_time); + } + } + + return bytes; +} + +uint8_t * picoquic_format_ack_frame(picoquic_cnx_t* cnx, uint8_t* bytes, uint8_t * bytes_max, + int * more_data, uint64_t current_time, picoquic_packet_context_enum pc) +{ + uint64_t num_block = 0; + picoquic_packet_context_t* pkt_ctx = &cnx->pkt_ctx[pc]; + picoquic_sack_item_t* next_sack = pkt_ctx->first_sack_item.next_sack; + uint64_t ack_delay = 0; + uint64_t ack_range = 0; + uint64_t ack_gap = 0; + uint64_t lowest_acknowledged = 0; + int is_ecn = cnx->pkt_ctx[pc].sending_ecn_ack; + uint8_t* after_stamp = bytes; + int has_time_stamp = (pc == picoquic_packet_context_application && cnx->is_time_stamp_sent); + uint8_t ack_type_byte = ((is_ecn) ? picoquic_frame_type_ack_ecn : picoquic_frame_type_ack); + + /* Check that there something to acknowledge */ + if (pkt_ctx->first_sack_item.start_of_sack_range != UINT64_MAX) { + uint8_t* num_block_byte = NULL; + + if (current_time > pkt_ctx->time_stamp_largest_received) { + ack_delay = current_time - pkt_ctx->time_stamp_largest_received; + ack_delay >>= cnx->local_parameters.ack_delay_exponent; + } + + if (has_time_stamp) { + bytes = picoquic_format_time_stamp_frame(cnx, bytes, bytes_max, more_data, current_time); + after_stamp = bytes; + } + + if ((bytes = picoquic_frames_uint8_encode(bytes, bytes_max, ack_type_byte)) != NULL && + (bytes = picoquic_frames_varint_encode(bytes, bytes_max, pkt_ctx->first_sack_item.end_of_sack_range)) != NULL && + (bytes = picoquic_frames_varint_encode(bytes, bytes_max, ack_delay)) != NULL) { + /* Reserve one byte for the number of blocks */ + num_block_byte = bytes++; + /* Encode the size of the first ack range */ + ack_range = pkt_ctx->first_sack_item.end_of_sack_range - pkt_ctx->first_sack_item.start_of_sack_range; + bytes = picoquic_frames_varint_encode(bytes, bytes_max, ack_range); + } + + if (bytes == NULL || num_block_byte == NULL) { + bytes = after_stamp; + *more_data = 1; + } + else { + /* Set the lowest acknowledged */ + lowest_acknowledged = pkt_ctx->first_sack_item.start_of_sack_range; + /* Encode the ack blocks that fit in the allocated space */ + while (num_block < 32 && next_sack != NULL) { + uint8_t* bytes_start_range = bytes; + + ack_gap = lowest_acknowledged - next_sack->end_of_sack_range - 2; /* per spec */ + ack_range = next_sack->end_of_sack_range - next_sack->start_of_sack_range; + + if ((bytes = picoquic_frames_varint_encode(bytes, bytes_max, ack_gap)) == NULL || + (bytes = picoquic_frames_varint_encode(bytes, bytes_max, ack_range)) == NULL) { + bytes = bytes_start_range; + *more_data = 1; + break; + } + else { + lowest_acknowledged = next_sack->start_of_sack_range; + next_sack = next_sack->next_sack; + num_block++; + } + } + /* When numbers are lower than 64, varint encoding fits on one byte */ + *num_block_byte = (uint8_t)num_block; + + /* Remember the ACK value and time */ + pkt_ctx->highest_ack_sent = pkt_ctx->first_sack_item.end_of_sack_range; + pkt_ctx->highest_ack_sent_time = current_time; + } + + if (bytes > after_stamp && is_ecn) { + /* Try to encode the ECN bytes */ + uint8_t* bytes_ecn = bytes; + if ((bytes = picoquic_frames_varint_encode(bytes, bytes_max, cnx->pkt_ctx[pc].ecn_ect0_total_local)) == NULL || + (bytes = picoquic_frames_varint_encode(bytes, bytes_max, cnx->pkt_ctx[pc].ecn_ect1_total_local)) == NULL || + (bytes = picoquic_frames_varint_encode(bytes, bytes_max, cnx->pkt_ctx[pc].ecn_ce_total_local)) == NULL) + { + bytes = bytes_ecn; + *more_data = 1; + *after_stamp = picoquic_frame_type_ack; + } + } + } + + if (bytes > after_stamp) { + pkt_ctx->ack_needed = 0; + pkt_ctx->ack_after_fin = 0; + } + + return bytes; +} + +void picoquic_set_ack_needed(picoquic_cnx_t* cnx, uint64_t current_time, picoquic_packet_context_enum pc) +{ + if (!cnx->pkt_ctx[pc].ack_needed) { + cnx->pkt_ctx[pc].ack_needed = 1; + cnx->pkt_ctx[pc].time_oldest_unack_packet_received = current_time; + } +} + +int picoquic_is_ack_needed(picoquic_cnx_t* cnx, uint64_t current_time, uint64_t * next_wake_time, picoquic_packet_context_enum pc) +{ + int ret = 0; + picoquic_packet_context_t * pkt_ctx = &cnx->pkt_ctx[pc]; + + if (pkt_ctx->ack_needed) { + if (pc != picoquic_packet_context_application || pkt_ctx->ack_after_fin) { + ret = 1; + } + else + { + uint64_t ack_gap = (cnx->nb_packets_received < 128) ? 2 : cnx->ack_gap_remote; + if (pkt_ctx->highest_ack_sent + ack_gap <= pkt_ctx->first_sack_item.end_of_sack_range || + pkt_ctx->time_oldest_unack_packet_received + cnx->ack_delay_remote <= current_time) { + ret = 1; + } + else{ + if (pkt_ctx->time_oldest_unack_packet_received + cnx->ack_delay_remote < *next_wake_time) { + *next_wake_time = pkt_ctx->time_oldest_unack_packet_received + cnx->ack_delay_remote; + SET_LAST_WAKE(cnx->quic, PICOQUIC_FRAME); + } + } + } + } + else if (pkt_ctx->highest_ack_sent + 8 <= pkt_ctx->first_sack_item.end_of_sack_range && + pkt_ctx->highest_ack_sent_time + cnx->ack_delay_remote <= current_time) { + /* Force sending an ack-of-ack from time to time, as a low priority action */ + if (pkt_ctx->first_sack_item.end_of_sack_range == (uint64_t)((int64_t)-1)) { + ret = 0; + } + else { + ret = 1; + } + } + return ret; +} + +/* + * Connection close frame + */ + +uint8_t * picoquic_format_connection_close_frame(picoquic_cnx_t* cnx, + uint8_t* bytes, uint8_t* bytes_max, int * more_data, int * is_pure_ack) +{ + uint8_t* bytes0 = bytes; + + if ((bytes = picoquic_frames_uint8_encode(bytes, bytes_max, picoquic_frame_type_connection_close)) != NULL && + (bytes = picoquic_frames_varint_encode(bytes, bytes_max, cnx->local_error)) != NULL && + (bytes = picoquic_frames_varint_encode(bytes, bytes_max, cnx->offending_frame_type)) != NULL && + (bytes = picoquic_frames_uint8_encode(bytes, bytes_max, 0)) != NULL) { + *is_pure_ack = 0; + } + else { + bytes = bytes0; + *more_data = 1; + } + return bytes; +} + +const uint8_t* picoquic_decode_connection_close_frame(picoquic_cnx_t* cnx, const uint8_t* bytes, const uint8_t* bytes_max) +{ + uint64_t error_code = 0; + bytes = picoquic_frames_varint_decode(bytes + 1, bytes_max, &error_code); + cnx->remote_error = (uint16_t)error_code; + + if (bytes == NULL || + (bytes = picoquic_frames_varint_skip(bytes, bytes_max)) == NULL || + (bytes = picoquic_frames_length_data_skip(bytes, bytes_max)) == NULL) + { + picoquic_connection_error(cnx, PICOQUIC_TRANSPORT_FRAME_FORMAT_ERROR, + picoquic_frame_type_connection_close); + } + else { + cnx->cnx_state = (cnx->cnx_state < picoquic_state_client_ready_start || cnx->crypto_context[picoquic_epoch_1rtt].aead_decrypt == NULL) ? picoquic_state_disconnected : picoquic_state_closing_received; + + if (cnx->callback_fn) { + (void)(cnx->callback_fn)(cnx, 0, NULL, 0, picoquic_callback_close, cnx->callback_ctx, NULL); + } + } + + return bytes; +} + +/* + * Application close frame + */ + +uint8_t * picoquic_format_application_close_frame(picoquic_cnx_t* cnx, + uint8_t* bytes, uint8_t* bytes_max, int* more_data, int* is_pure_ack) +{ + uint8_t* bytes0 = bytes; + + if ((bytes = picoquic_frames_uint8_encode(bytes, bytes_max, picoquic_frame_type_application_close)) != NULL && + (bytes = picoquic_frames_varint_encode(bytes, bytes_max, cnx->local_error)) != NULL && + (bytes = picoquic_frames_uint8_encode(bytes, bytes_max, 0)) != NULL) { + *is_pure_ack = 0; + } + else { + bytes = bytes0; + *more_data = 1; + } + return bytes; +} + +const uint8_t* picoquic_decode_application_close_frame(picoquic_cnx_t* cnx, const uint8_t* bytes, const uint8_t* bytes_max) +{ + uint64_t error_code = 0; + bytes = picoquic_frames_varint_decode(bytes + 1, bytes_max, &error_code); + cnx->remote_application_error = (uint16_t)error_code; + + if (bytes == NULL || + /* TODO, maybe: skip frame type for compatibility with draft-13 */ + (bytes = picoquic_frames_length_data_skip(bytes, bytes_max)) == NULL) + { + picoquic_connection_error(cnx, PICOQUIC_TRANSPORT_FRAME_FORMAT_ERROR, + picoquic_frame_type_application_close); + } + else { + cnx->cnx_state = (cnx->cnx_state < picoquic_state_client_ready_start) ? picoquic_state_disconnected : picoquic_state_closing_received; + if (cnx->callback_fn) { + (void)(cnx->callback_fn)(cnx, 0, NULL, 0, picoquic_callback_application_close, cnx->callback_ctx, NULL); + } + } + + return bytes; +} + + + +/* + * Max data frame + */ + +#define PICOQUIC_MAX_MAXDATA ((uint64_t)((int64_t)-1)) +#define PICOQUIC_MAX_MAXDATA_1K (PICOQUIC_MAX_MAXDATA >> 10) +#define PICOQUIC_MAX_MAXDATA_1K_MASK (PICOQUIC_MAX_MAXDATA << 10) + +uint8_t * picoquic_format_max_data_frame(picoquic_cnx_t* cnx, uint8_t * bytes, uint8_t * bytes_max, + int * more_data, int * is_pure_ack, uint64_t maxdata_increase) +{ + uint8_t * bytes0 = bytes; + + if ((bytes = picoquic_frames_uint8_encode(bytes, bytes_max, picoquic_frame_type_max_data)) != NULL && + (bytes = picoquic_frames_varint_encode(bytes, bytes_max, cnx->maxdata_local + maxdata_increase)) != NULL) { + cnx->maxdata_local = (cnx->maxdata_local + maxdata_increase); + *is_pure_ack = 0; + } + else { + *more_data = 1; + bytes = bytes0; + } + + return bytes; +} + +const uint8_t* picoquic_decode_max_data_frame(picoquic_cnx_t* cnx, const uint8_t* bytes, const uint8_t* bytes_max) +{ + uint64_t maxdata; + + if ((bytes = picoquic_frames_varint_decode(bytes+1, bytes_max, &maxdata)) == NULL) { + picoquic_connection_error(cnx, PICOQUIC_TRANSPORT_FRAME_FORMAT_ERROR, picoquic_frame_type_max_data); + } else if (maxdata > cnx->maxdata_remote) { + cnx->maxdata_remote = maxdata; + cnx->sent_blocked_frame = 0; + } + + return bytes; +} + +/* + * Max stream data frame + */ + +uint8_t* picoquic_format_max_stream_data_frame(picoquic_stream_head_t* stream, uint8_t* bytes, uint8_t* bytes_max, + int* more_data, int* is_pure_ack, uint64_t new_max_data) +{ + uint8_t* bytes0 = bytes; + + if ((bytes = picoquic_frames_uint8_encode(bytes, bytes_max, picoquic_frame_type_max_stream_data)) != NULL && + (bytes = picoquic_frames_varint_encode(bytes, bytes_max, stream->stream_id)) != NULL && + (bytes = picoquic_frames_varint_encode(bytes, bytes_max, new_max_data)) != NULL) { + stream->maxdata_local = new_max_data; + *is_pure_ack = 0; + } + else { + *more_data = 1; + bytes = bytes0; + } + + return bytes; +} + + +const uint8_t* picoquic_decode_max_stream_data_frame(picoquic_cnx_t* cnx, const uint8_t* bytes, const uint8_t* bytes_max) +{ + uint64_t stream_id; + uint64_t maxdata = 0; + picoquic_stream_head_t* stream = NULL; + + if ((bytes = picoquic_frames_varint_decode(bytes + 1, bytes_max, &stream_id)) == NULL || + (bytes = picoquic_frames_varint_decode(bytes, bytes_max, &maxdata)) == NULL) + { + picoquic_connection_error(cnx, PICOQUIC_TRANSPORT_FRAME_FORMAT_ERROR, picoquic_frame_type_max_stream_data); + } + else if ((stream = picoquic_find_stream(cnx, stream_id)) == NULL) { + /* Maybe not an error if the stream is already closed, so just be tolerant */ + stream = picoquic_create_missing_streams(cnx, stream_id, 1); + } + + if (stream != NULL && maxdata > stream->maxdata_remote) { + /* TODO: call back if the stream was blocked? */ + stream->maxdata_remote = maxdata; + } + + + return bytes; +} + +uint8_t * picoquic_format_required_max_stream_data_frames(picoquic_cnx_t* cnx, + uint8_t* bytes, uint8_t * bytes_max, int * more_data, int * is_pure_ack) +{ + uint8_t* bytes0; + picoquic_stream_head_t* stream = picoquic_first_stream(cnx); + + while (stream != NULL) { + if (!stream->fin_received) { + uint64_t new_window = picoquic_cc_increased_window(cnx, stream->maxdata_local); + + if (!stream->reset_received && 2 * stream->consumed_offset > stream->maxdata_local) { + bytes0 = bytes; + + if ((bytes = picoquic_format_max_stream_data_frame(stream, bytes, bytes_max, more_data, is_pure_ack, stream->maxdata_local + new_window)) == bytes0) { + /* not enough space for this frame. */ + break; + } + } + } + stream = picoquic_next_stream(stream); + } + + if (stream == NULL) { + cnx->max_stream_data_needed = 0; + } + + return bytes; +} + + +/* + * Max stream ID frames + */ + +uint8_t * picoquic_format_max_streams_frame_if_needed(picoquic_cnx_t* cnx, + uint8_t* bytes, uint8_t * bytes_max, int * more_data, int * is_pure_ack) +{ + uint8_t* bytes0 = bytes; + + if (cnx->max_stream_id_bidir_local_computed + + (cnx->local_parameters.initial_max_stream_id_bidir >> 1) > cnx->max_stream_id_bidir_local) { + uint64_t new_bidir_local = cnx->max_stream_id_bidir_local + + 4 * STREAM_RANK_FROM_ID(cnx->local_parameters.initial_max_stream_id_bidir) + 4; + if ((bytes = picoquic_frames_uint8_encode(bytes, bytes_max, picoquic_frame_type_max_streams_bidir)) != NULL && + (bytes = picoquic_frames_varint_encode(bytes, bytes_max, STREAM_RANK_FROM_ID(new_bidir_local))) != NULL) { + cnx->max_stream_id_bidir_local = new_bidir_local; + *is_pure_ack = 0; + bytes0 = bytes; + } else { + *more_data = 1; + bytes = bytes0; + } + } + + if (cnx->max_stream_id_unidir_local_computed + + (cnx->local_parameters.initial_max_stream_id_unidir >> 1) > cnx->max_stream_id_unidir_local) { + uint64_t new_unidir_local = cnx->max_stream_id_unidir_local + cnx->local_parameters.initial_max_stream_id_unidir + 4; + + if ((bytes = picoquic_frames_uint8_encode(bytes, bytes_max, picoquic_frame_type_max_streams_unidir)) != NULL && + (bytes = picoquic_frames_varint_encode(bytes, bytes_max, STREAM_RANK_FROM_ID(new_unidir_local))) != NULL) { + cnx->max_stream_id_unidir_local = new_unidir_local; + *is_pure_ack = 0; + } + else { + *more_data = 1; + bytes = bytes0; + } + } + + return bytes; +} + +void picoquic_update_max_stream_ID_local(picoquic_cnx_t* cnx, picoquic_stream_head_t* stream) +{ + if (cnx->client_mode != IS_CLIENT_STREAM_ID(stream->stream_id) && !stream->max_stream_updated) { + /* This is a remotely initiated stream */ + if (stream->consumed_offset >= stream->fin_offset && (stream->fin_received || stream->reset_received)) { + /* Receive is complete */ + if (IS_BIDIR_STREAM_ID(stream->stream_id)) { + if (stream->fin_sent || stream->reset_sent) + { + /* Sending is complete */ + stream->max_stream_updated = 1; + cnx->max_stream_id_bidir_local_computed += 4; + } + } else { + /* No need to check receive complete on uni directional streams */ + stream->max_stream_updated = 1; + cnx->max_stream_id_unidir_local_computed += 4; + } + } + } +} + +const uint8_t* picoquic_decode_max_streams_frame(picoquic_cnx_t* cnx, const uint8_t* bytes, const uint8_t* bytes_max, int max_streams_frame_type) +{ + uint64_t max_stream_rank; + + if ((bytes = picoquic_frames_varint_decode(bytes + 1, bytes_max, &max_stream_rank)) == NULL) { + picoquic_connection_error(cnx, PICOQUIC_TRANSPORT_FRAME_FORMAT_ERROR, max_streams_frame_type); + } + else { + uint64_t max_stream_id; + if (max_streams_frame_type == picoquic_frame_type_max_streams_bidir) { + /* Bidir */ + max_stream_id = STREAM_ID_FROM_RANK(max_stream_rank, !cnx->client_mode, 0); + if (max_stream_id > cnx->max_stream_id_bidir_remote) { + picoquic_add_output_streams(cnx, cnx->max_stream_id_bidir_remote, max_stream_id, 1); + cnx->max_stream_id_bidir_remote = max_stream_id; + cnx->stream_blocked_bidir_sent = 0; + } + } + else { + /* Unidir */ + max_stream_id = STREAM_ID_FROM_RANK(max_stream_rank, !cnx->client_mode, 1); + if (max_stream_id > cnx->max_stream_id_unidir_remote) { + picoquic_add_output_streams(cnx, cnx->max_stream_id_unidir_remote, max_stream_id, 0); + cnx->max_stream_id_unidir_remote = max_stream_id; + cnx->stream_blocked_unidir_sent = 0; + } + } + + if (max_stream_id >= (1ull << 62)) { + (void)picoquic_connection_error(cnx, PICOQUIC_TRANSPORT_STREAM_LIMIT_ERROR, max_streams_frame_type); + bytes = NULL; + } + } + + return bytes; +} + +/* Common code for datagrams and misc frames + */ + +uint8_t * picoquic_format_first_misc_or_dg_frame(uint8_t* bytes, uint8_t * bytes_max, int * more_data, int * is_pure_ack, + picoquic_misc_frame_header_t** first, picoquic_misc_frame_header_t** last) +{ + picoquic_misc_frame_header_t* misc_frame = *first; + + if (bytes + misc_frame->length > bytes_max) { + *more_data = 1; + } else { + uint8_t* frame = ((uint8_t*)misc_frame) + sizeof(picoquic_misc_frame_header_t); + memcpy(bytes, frame, misc_frame->length); + bytes += misc_frame->length; + *is_pure_ack &= misc_frame->is_pure_ack; + picoquic_delete_misc_or_dg(first, last, *first); + } + + return bytes; +} + +/* + * Sending of miscellaneous frames + */ + +uint8_t* picoquic_format_first_misc_frame(picoquic_cnx_t* cnx, uint8_t* bytes, uint8_t* bytes_max, int* more_data, int* is_pure_ack) +{ + return picoquic_format_first_misc_or_dg_frame(bytes, bytes_max, more_data, is_pure_ack, &cnx->first_misc_frame, &cnx->last_misc_frame); +} + +/* + * Path Challenge and Response frames + */ + +uint8_t* picoquic_format_path_challenge_frame(uint8_t* bytes, uint8_t* bytes_max, int* more_data, int* is_pure_ack, + uint64_t challenge) +{ + uint8_t* bytes0 = 0; + if ((bytes = picoquic_frames_uint8_encode(bytes, bytes_max, picoquic_frame_type_path_challenge)) != NULL && + (bytes = picoquic_frames_uint64_encode(bytes, bytes_max, challenge)) != NULL) { + *is_pure_ack = 0; + } + else { + *more_data = 1; + bytes = bytes0; + } + return bytes; +} + + +const uint8_t* picoquic_decode_path_challenge_frame(picoquic_cnx_t* cnx, const uint8_t* bytes, const uint8_t* bytes_max, + picoquic_path_t * path_x, struct sockaddr* addr_from, struct sockaddr* addr_to) +{ + if (bytes_max - bytes <= (int) challenge_length) { + picoquic_connection_error(cnx, PICOQUIC_TRANSPORT_FRAME_FORMAT_ERROR, picoquic_frame_type_path_challenge); + bytes = NULL; + } else if (path_x != NULL) { + /* + * Queue a response frame as response to path challenge. + * TODO: ensure it goes out on the same path as the incoming challenge. + */ + uint64_t challenge_response; + + bytes++; + challenge_response = PICOPARSE_64(bytes); + bytes += challenge_length; + if ((addr_from == NULL || picoquic_compare_addr(addr_from, (struct sockaddr *)&path_x->peer_addr) == 0) && + (addr_to == NULL || picoquic_compare_addr(addr_to, (struct sockaddr *)&path_x->local_addr) == 0)) { + path_x->challenge_response = challenge_response; + path_x->response_required = 1; + } else { + DBG_PRINTF("%s", "Path challenge ignored, wrong addresses\n"); + } + } + + return bytes; +} + +uint8_t * picoquic_format_path_response_frame(uint8_t* bytes, uint8_t* bytes_max, int* more_data, int* is_pure_ack, + uint64_t challenge) +{ + uint8_t* bytes0 = 0; + if ((bytes = picoquic_frames_uint8_encode(bytes, bytes_max, picoquic_frame_type_path_response)) != NULL && + (bytes = picoquic_frames_uint64_encode(bytes, bytes_max, challenge)) != NULL) { + *is_pure_ack = 0; + } + else { + *more_data = 1; + bytes = bytes0; + } + return bytes; +} + + +const uint8_t* picoquic_decode_path_response_frame(picoquic_cnx_t* cnx, const uint8_t* bytes, const uint8_t* bytes_max) +{ + uint64_t response; + + if ((bytes = picoquic_frames_uint64_decode(bytes+1, bytes_max, &response)) == NULL) { + picoquic_connection_error(cnx, PICOQUIC_TRANSPORT_FRAME_FORMAT_ERROR, picoquic_frame_type_path_response); + + } else { + int found_challenge = 0; + + /* + * Check that the challenge corresponds to something that was sent locally + */ + for (int i = 0; i < cnx->nb_paths; i++) { + for (int ichal = 0; ichal < PICOQUIC_CHALLENGE_REPEAT_MAX; ichal++) { + if (response == cnx->path[i]->challenge[ichal]) { + found_challenge = 1; + break; + } + } + + if (found_challenge) { + cnx->path[i]->challenge_verified = 1; + break; + } + } + } + + return bytes; +} + + +const uint8_t* picoquic_decode_blocked_frame(picoquic_cnx_t* cnx, const uint8_t* bytes, const uint8_t* bytes_max) +{ + if ((bytes = picoquic_frames_varint_skip(bytes+1, bytes_max)) == NULL) { + picoquic_connection_error(cnx, PICOQUIC_TRANSPORT_FRAME_FORMAT_ERROR, + picoquic_frame_type_data_blocked); + } + return bytes; +} + + +const uint8_t* picoquic_decode_stream_blocked_frame(picoquic_cnx_t* cnx, const uint8_t* bytes, const uint8_t* bytes_max) +{ + /* TODO: check that the stream number is valid */ + if ((bytes = picoquic_frames_varint_skip(bytes+1, bytes_max)) == NULL || + (bytes = picoquic_frames_varint_skip(bytes, bytes_max)) == NULL) + { + picoquic_connection_error(cnx, PICOQUIC_TRANSPORT_FRAME_FORMAT_ERROR, + picoquic_frame_type_stream_data_blocked); + } + return bytes; +} + + +const uint8_t* picoquic_decode_streams_blocked_frame(picoquic_cnx_t* cnx, const uint8_t* bytes, const uint8_t* bytes_max, uint8_t frame_id) +{ + if ((bytes = picoquic_frames_varint_skip(bytes+1, bytes_max)) == NULL) { + picoquic_connection_error(cnx, PICOQUIC_TRANSPORT_FRAME_FORMAT_ERROR, + frame_id); + } + return bytes; +} + + +static const uint8_t* picoquic_skip_0len_frame(const uint8_t* bytes, const uint8_t* bytes_max) +{ + uint8_t frame = bytes[0]; + do { + bytes++; + } while (bytes < bytes_max && *bytes == frame); + return bytes; +} + +/* Handling of Handshake Done frame. + * The decode function is defined here, as well as a queue function. + * There is no prepare function or skip function for this single byte frame. + */ +const uint8_t* picoquic_decode_handshake_done_frame(picoquic_cnx_t* cnx, const uint8_t* bytes, uint64_t current_time) +{ + if (!cnx->client_mode) { + DBG_PRINTF("Handshake done (0x%x) not expected from client", bytes[0]); + picoquic_connection_error(cnx, PICOQUIC_TRANSPORT_PROTOCOL_VIOLATION, bytes[0]); + bytes = NULL; + } + else { + bytes++; + + /* The connection is now confirmed */ + if (cnx->cnx_state == picoquic_state_client_ready_start) { + /* Transition to client ready state. + * The handshake is complete, all the handshake packets are implicitly acknowledged */ + picoquic_ready_state_transition(cnx, current_time); + } + else if (cnx->cnx_state < picoquic_state_client_ready_start) { + DBG_PRINTF("Handshake done (0x%x) not expected in state %d", bytes[0], cnx->cnx_state); + picoquic_connection_error(cnx, PICOQUIC_TRANSPORT_PROTOCOL_VIOLATION, picoquic_frame_type_handshake_done); + bytes = NULL; + } + } + return bytes; +} + +int picoquic_queue_handshake_done_frame(picoquic_cnx_t* cnx) +{ + uint8_t frame_buffer = picoquic_frame_type_handshake_done; + + return picoquic_queue_misc_or_dg_frame(cnx, &cnx->first_datagram, &cnx->last_datagram, + &frame_buffer, 1, 0); +} + +/* Handling of datagram frames. + * We follow the spec in + * https://datatracker.ietf.org/doc/draft-pauly-quic-datagram/?include_text=1 + */ + +const uint8_t* picoquic_skip_datagram_frame(const uint8_t* bytes, const uint8_t* bytes_max) +{ + uint8_t frame_id = *bytes++; + unsigned int has_length = frame_id & 1; + uint64_t length = 0; + + if (bytes != NULL) { + if (has_length) { + bytes = picoquic_frames_varint_decode(bytes, bytes_max, &length); + } + else { + length = bytes_max - bytes ; + } + + if (bytes != NULL) { + bytes += length; + if (bytes > bytes_max) { + bytes = NULL; + } + } + } + + return bytes; +} + +const uint8_t* picoquic_decode_datagram_frame(picoquic_cnx_t* cnx, const uint8_t* bytes, const uint8_t* bytes_max) +{ + uint8_t frame_id = *bytes++; + unsigned int has_length = frame_id & 1; + uint64_t length = 0; + + if (has_length) { + if (bytes != NULL && (bytes = picoquic_frames_varint_decode(bytes, bytes_max, &length)) == NULL) { + picoquic_connection_error(cnx, PICOQUIC_TRANSPORT_FRAME_FORMAT_ERROR, + frame_id); + } + if (bytes != NULL && bytes + length > bytes_max) { + picoquic_connection_error(cnx, PICOQUIC_TRANSPORT_FRAME_FORMAT_ERROR, + frame_id); + bytes = NULL; + } + } + else { + length = bytes_max - bytes; + } + + if (bytes != NULL) { + if (cnx->callback_fn != NULL) { + /* submit the data to the app */ + if (cnx->callback_fn(cnx, 0, (uint8_t *)bytes, (size_t)length, picoquic_callback_datagram, + cnx->callback_ctx, NULL) != 0) { + picoquic_connection_error(cnx, PICOQUIC_TRANSPORT_INTERNAL_ERROR, picoquic_frame_type_datagram); + bytes = NULL; + } + } + + bytes += length; + } + + return bytes; +} + +uint8_t * picoquic_format_datagram_frame(uint8_t* bytes, uint8_t* bytes_max, int* more_data, int* is_pure_ack, size_t length, const uint8_t* src) +{ + uint8_t* bytes0 = bytes; + + if ((bytes = picoquic_frames_uint8_encode(bytes, bytes_max, picoquic_frame_type_datagram_l)) != NULL && + (bytes = picoquic_frames_varint_encode(bytes, bytes_max, length)) != NULL && + bytes + length <= bytes_max) { + memcpy(bytes, src, length); + bytes += length; + *is_pure_ack = 0; + } + else { + *more_data = 1; + bytes = bytes0; + } + + return bytes; +} + +int picoquic_queue_datagram_frame(picoquic_cnx_t * cnx, size_t length, const uint8_t * src) +{ + int ret; + size_t consumed = 0; + uint8_t frame_buffer[PICOQUIC_MAX_PACKET_SIZE]; + int more_data = 0; + int is_pure_ack = 1; + uint8_t * bytes_next = picoquic_format_datagram_frame(frame_buffer, frame_buffer + sizeof(frame_buffer), &more_data, &is_pure_ack, length, src); + + if ((consumed = bytes_next - frame_buffer) > 0) { + ret = picoquic_queue_misc_or_dg_frame(cnx, &cnx->first_datagram, &cnx->last_datagram, + frame_buffer, consumed, 0); + } + else { + ret = PICOQUIC_ERROR_FRAME_BUFFER_TOO_SMALL; + } + + return ret; +} + +uint8_t * picoquic_format_first_datagram_frame(picoquic_cnx_t* cnx, uint8_t* bytes, + uint8_t *bytes_max, int * more_data, int * is_pure_ack) +{ + if (bytes + cnx->first_datagram->length > bytes_max) { + /* TODO: don't do that if this is a coalesced packet... */ + /* This datagram is not compatible with the path. Just drop. */ + picoquic_delete_misc_or_dg(&cnx->first_datagram, &cnx->last_datagram, cnx->first_datagram); + } + else { + bytes = picoquic_format_first_misc_or_dg_frame(bytes, bytes_max, more_data, is_pure_ack, + &cnx->first_datagram, &cnx->last_datagram); + } + + return bytes; +} + +/* ACK Frequency frames + */ +const uint8_t* picoquic_skip_ack_frequency_frame(const uint8_t* bytes, const uint8_t* bytes_max) +{ + /* This code assumes that the frame type is already skipped */ + if ((bytes = picoquic_frames_varint_skip(bytes, bytes_max)) != NULL && + (bytes = picoquic_frames_varint_skip(bytes, bytes_max)) != NULL) { + bytes = picoquic_frames_varint_skip(bytes, bytes_max); + } + return bytes; +} + +const uint8_t* picoquic_parse_ack_frequency_frame(const uint8_t* bytes, const uint8_t* bytes_max, + uint64_t* seq, uint64_t* packets, uint64_t* microsec) +{ + if ((bytes = picoquic_frames_varint_decode(bytes, bytes_max, seq)) != NULL && + (bytes = picoquic_frames_varint_decode(bytes, bytes_max, packets)) != NULL) { + bytes = picoquic_frames_varint_decode(bytes, bytes_max, microsec); + } + return bytes; +} + +const uint8_t* picoquic_decode_ack_frequency_frame(const uint8_t* bytes, const uint8_t* bytes_max, picoquic_cnx_t * cnx) +{ + uint64_t seq = 0; + uint64_t packets = 0; + uint64_t microsec = 0; + + /* This code assumes that the frame type is already skipped */ + if ((bytes = picoquic_parse_ack_frequency_frame(bytes, bytes_max, &seq, &packets, µsec)) != NULL){ + if (!cnx->is_ack_frequency_negotiated || + microsec < cnx->local_parameters.min_ack_delay || + packets == 0) { + picoquic_connection_error(cnx, PICOQUIC_TRANSPORT_FRAME_FORMAT_ERROR, + picoquic_frame_type_ack_frequency); + bytes = NULL; + } + else { + int64_t delta = seq - cnx->ack_frequency_sequence_remote; + if (delta > 0) { + cnx->ack_frequency_sequence_remote = seq; + cnx->ack_gap_remote = packets; + cnx->ack_delay_remote = microsec; + } + } + } + return bytes; +} + +uint8_t* picoquic_format_ack_frequency_frame(picoquic_cnx_t* cnx, uint8_t* bytes, uint8_t* bytes_max, int* more_data) +{ + uint8_t* bytes0 = bytes; + uint64_t seq = cnx->ack_frequency_sequence_local + 1; + uint64_t ack_gap; + uint64_t ack_delay_max; + + /* Compute the desired value of the ack frequency*/ + ack_delay_max = picoquic_compute_ack_delay_max(cnx->path[0]->rtt_min, cnx->remote_parameters.min_ack_delay); + ack_gap = picoquic_compute_ack_gap(cnx, cnx->path[0]->bandwidth_estimate); + + if (ack_gap <= cnx->ack_gap_local && + ack_delay_max == cnx->ack_frequency_delay_local) { + cnx->is_ack_frequency_updated = 0; + } + else { + if (ack_gap < cnx->ack_gap_local) { + ack_gap = cnx->ack_gap_local; + } + if ((bytes = picoquic_frames_varint_encode(bytes, bytes_max, picoquic_frame_type_ack_frequency)) != NULL && + (bytes = picoquic_frames_varint_encode(bytes, bytes_max, seq)) != NULL && + (bytes = picoquic_frames_varint_encode(bytes, bytes_max, ack_gap)) != NULL && + (bytes = picoquic_frames_varint_encode(bytes, bytes_max, ack_delay_max)) != NULL) { + cnx->ack_frequency_sequence_local = seq; + cnx->ack_gap_local = ack_gap; + cnx->ack_frequency_delay_local = ack_delay_max; + cnx->is_ack_frequency_updated = 0; + } + else { + bytes = bytes0; + *more_data = 1; + } + } + return bytes; +} + +/* ACK Frequency frames + */ +const uint8_t* picoquic_skip_time_stamp_frame(const uint8_t* bytes, const uint8_t* bytes_max) +{ + /* This code assumes that the frame type is already skipped */ + bytes = picoquic_frames_varint_skip(bytes, bytes_max); + return bytes; +} + +const uint8_t* picoquic_parse_time_stamp_frame(const uint8_t* bytes, const uint8_t* bytes_max, + uint64_t* time_stamp) +{ + bytes = picoquic_frames_varint_decode(bytes, bytes_max, time_stamp); + return bytes; +} + +const uint8_t* picoquic_decode_time_stamp_frame(const uint8_t* bytes, const uint8_t* bytes_max, picoquic_cnx_t* cnx, + picoquic_packet_data_t * packet_data) +{ + uint64_t time_stamp = 0; + + /* This code assumes that the frame type is already skipped */ + if ((bytes = picoquic_parse_time_stamp_frame(bytes, bytes_max, &time_stamp)) != NULL) { + if (!cnx->is_time_stamp_enabled) { + picoquic_connection_error(cnx, PICOQUIC_TRANSPORT_PROTOCOL_VIOLATION, + picoquic_frame_type_time_stamp); + bytes = NULL; + } + else { + time_stamp <<= cnx->remote_parameters.ack_delay_exponent; + + if (time_stamp > packet_data->last_time_stamp_received) { + packet_data->last_time_stamp_received = time_stamp; + } + } + } + return bytes; +} + +uint8_t* picoquic_format_time_stamp_frame(picoquic_cnx_t* cnx, uint8_t* bytes, uint8_t* bytes_max, int* more_data, uint64_t current_time) +{ + uint8_t* bytes0 = bytes; + uint64_t time_stamp = (current_time - cnx->start_time) >> cnx->local_parameters.ack_delay_exponent; + + if ((bytes = picoquic_frames_varint_encode(bytes, bytes_max, picoquic_frame_type_time_stamp)) == NULL || + (bytes = picoquic_frames_varint_encode(bytes, bytes_max, time_stamp)) == NULL) { + bytes = bytes0; + *more_data = 1; + } + + return bytes; +} + +size_t picoquic_encode_time_stamp_length(picoquic_cnx_t* cnx, uint64_t current_time) +{ + uint64_t time_stamp = (current_time - cnx->start_time) >> cnx->local_parameters.ack_delay_exponent; + + return (2 + picoquic_encode_varint_length(time_stamp)); +} + +/* + * Decoding of the received frames. + * + * In some cases, the expected frames are "restricted" to only ACK, STREAM 0 and PADDING. + */ + +void process_decoded_packet_data(picoquic_cnx_t* cnx, uint64_t current_time, picoquic_packet_data_t * packet_data) +{ + if (packet_data->acked_path != NULL) { + uint64_t one_way_delay = 0; + + if (packet_data->acked_path->rtt_sample == 0) { + uint64_t cnx_time = current_time - cnx->start_time; + picoquic_log_app_message(cnx, "RTT Sample = 0 after %" PRIu64 "us.", cnx_time); + } + + if (cnx->congestion_alg != NULL && cnx->is_time_stamp_enabled && packet_data->acked_path->rtt_sample > 0) { + if (packet_data->last_time_stamp_received > 0) { + picoquic_update_1wd(cnx, packet_data->acked_path, + packet_data->largest_sent_time, packet_data->last_ack_delay, packet_data->last_time_stamp_received); + one_way_delay = packet_data->acked_path->one_way_delay_sample; + } + + /* CC notification of RTT are only delayed if waiting for one way delay assessment */ + cnx->congestion_alg->alg_notify(cnx, packet_data->acked_path, + picoquic_congestion_notification_rtt_measurement, + packet_data->acked_path->rtt_sample, one_way_delay, 0, 0, current_time); + } + + picoquic_estimate_path_bandwidth(cnx, packet_data->acked_path, packet_data->largest_sent_time, + packet_data->delivered_prior, packet_data->delivered_time_prior, packet_data->delivered_sent_prior, + (packet_data->last_time_stamp_received == 0) ? current_time : packet_data->last_time_stamp_received, + current_time, packet_data->rs_is_path_limited); + + picoquic_estimate_max_path_bandwidth(cnx, packet_data->acked_path, packet_data->largest_sent_time, + (packet_data->last_time_stamp_received == 0) ? current_time : packet_data->last_time_stamp_received, + current_time); + + if (cnx->congestion_alg != NULL && packet_data->acked_path->rtt_sample > 0) { + cnx->congestion_alg->alg_notify(cnx, packet_data->acked_path, + picoquic_congestion_notification_bw_measurement, + packet_data->acked_path->rtt_sample, one_way_delay, 0, 0, current_time); + } + } +} + +int picoquic_decode_frames(picoquic_cnx_t* cnx, picoquic_path_t * path_x, const uint8_t* bytes, + size_t bytes_maxsize, int epoch, + struct sockaddr* addr_from, + struct sockaddr* addr_to, + uint64_t current_time) +{ + const uint8_t *bytes_max = bytes + bytes_maxsize; + int ack_needed = 0; + picoquic_packet_context_enum pc = picoquic_context_from_epoch(epoch); + picoquic_packet_data_t packet_data; + + memset(&packet_data, 0, sizeof(packet_data)); + + while (bytes != NULL && bytes < bytes_max) { + uint8_t first_byte = bytes[0]; + + if (PICOQUIC_IN_RANGE(first_byte, picoquic_frame_type_stream_range_min, picoquic_frame_type_stream_range_max)) { + if (epoch != 1 && epoch != 3) { + DBG_PRINTF("Data frame (0x%x), when only TLS stream is expected", first_byte); + picoquic_connection_error(cnx, PICOQUIC_TRANSPORT_PROTOCOL_VIOLATION, first_byte); + bytes = NULL; + break; + } + + bytes = picoquic_decode_stream_frame(cnx, bytes, bytes_max, current_time); + ack_needed = 1; + + } + else if (first_byte == picoquic_frame_type_ack) { + if (epoch == picoquic_epoch_0rtt) { + DBG_PRINTF("Ack frame (0x%x) not expected in 0-RTT packet", first_byte); + picoquic_connection_error(cnx, PICOQUIC_TRANSPORT_PROTOCOL_VIOLATION, first_byte); + bytes = NULL; + break; + } + bytes = picoquic_decode_ack_frame(cnx, bytes, bytes_max, current_time, epoch, 0, &packet_data); + } + else if (first_byte == picoquic_frame_type_ack_ecn) { + if (epoch == picoquic_epoch_0rtt) { + DBG_PRINTF("Ack-ECN frame (0x%x) not expected in 0-RTT packet", first_byte); + picoquic_connection_error(cnx, PICOQUIC_TRANSPORT_PROTOCOL_VIOLATION, first_byte); + bytes = NULL; + break; + } + bytes = picoquic_decode_ack_frame(cnx, bytes, bytes_max, current_time, epoch, 1, &packet_data); + } + else if (epoch != picoquic_epoch_0rtt && epoch != picoquic_epoch_1rtt && first_byte != picoquic_frame_type_padding + && first_byte != picoquic_frame_type_ping + && first_byte != picoquic_frame_type_connection_close + && first_byte != picoquic_frame_type_crypto_hs) { + picoquic_connection_error(cnx, PICOQUIC_TRANSPORT_PROTOCOL_VIOLATION, first_byte); + bytes = NULL; + break; + } + else if (epoch == picoquic_epoch_0rtt && (first_byte == picoquic_frame_type_crypto_hs + || first_byte == picoquic_frame_type_handshake_done + || first_byte == picoquic_frame_type_new_token + || first_byte == picoquic_frame_type_path_response + || first_byte == picoquic_frame_type_retire_connection_id)) { + /* From draft-31: + * Note that it is not possible to send the following frames in 0-RTT + * packets for various reasons : ACK, CRYPTO, HANDSHAKE_DONE, NEW_TOKEN, + * PATH_RESPONSE, and RETIRE_CONNECTION_ID.A server MAY treat receipt + * of these frames in 0 - RTT packets as a connection error of type + * PROTOCOL_VIOLATION. + */ + picoquic_connection_error(cnx, PICOQUIC_TRANSPORT_PROTOCOL_VIOLATION, first_byte); + bytes = NULL; + break; + } + else { + switch (first_byte) { + case picoquic_frame_type_padding: + bytes = picoquic_skip_0len_frame(bytes, bytes_max); + break; + case picoquic_frame_type_reset_stream: + bytes = picoquic_decode_stream_reset_frame(cnx, bytes, bytes_max); + ack_needed = 1; + break; + case picoquic_frame_type_connection_close: + bytes = picoquic_decode_connection_close_frame(cnx, bytes, bytes_max); + ack_needed = 1; + break; + case picoquic_frame_type_application_close: + bytes = picoquic_decode_application_close_frame(cnx, bytes, bytes_max); + ack_needed = 1; + break; + case picoquic_frame_type_max_data: + bytes = picoquic_decode_max_data_frame(cnx, bytes, bytes_max); + ack_needed = 1; + break; + case picoquic_frame_type_max_stream_data: + bytes = picoquic_decode_max_stream_data_frame(cnx, bytes, bytes_max); + ack_needed = 1; + break; + case picoquic_frame_type_max_streams_bidir: + case picoquic_frame_type_max_streams_unidir: + bytes = picoquic_decode_max_streams_frame(cnx, bytes, bytes_max, first_byte); + ack_needed = 1; + break; + case picoquic_frame_type_ping: + bytes = picoquic_skip_0len_frame(bytes, bytes_max); + ack_needed = 1; + break; + case picoquic_frame_type_data_blocked: + bytes = picoquic_decode_blocked_frame(cnx, bytes, bytes_max); + ack_needed = 1; + break; + case picoquic_frame_type_stream_data_blocked: + bytes = picoquic_decode_stream_blocked_frame(cnx, bytes, bytes_max); + ack_needed = 1; + break; + case picoquic_frame_type_streams_blocked_unidir: + case picoquic_frame_type_streams_blocked_bidir: + bytes = picoquic_decode_streams_blocked_frame(cnx, bytes, bytes_max, first_byte); + ack_needed = 1; + break; + case picoquic_frame_type_new_connection_id: + bytes = picoquic_decode_new_connection_id_frame(cnx, bytes, bytes_max, current_time); + ack_needed = 1; + break; + case picoquic_frame_type_stop_sending: + bytes = picoquic_decode_stop_sending_frame(cnx, bytes, bytes_max); + ack_needed = 1; + break; + case picoquic_frame_type_path_challenge: + bytes = picoquic_decode_path_challenge_frame(cnx, bytes, bytes_max, path_x, addr_from, addr_to); + break; + case picoquic_frame_type_path_response: + bytes = picoquic_decode_path_response_frame(cnx, bytes, bytes_max); + break; + case picoquic_frame_type_crypto_hs: + bytes = picoquic_decode_crypto_hs_frame(cnx, bytes, bytes_max, epoch); + ack_needed = 1; + break; + case picoquic_frame_type_new_token: + bytes = picoquic_decode_new_token_frame(cnx, bytes, bytes_max, current_time, addr_to); + ack_needed = 1; + break; + case picoquic_frame_type_retire_connection_id: + /* the old code point for ACK frames, but this is taken care of in the ACK tests above */ + bytes = picoquic_decode_retire_connection_id_frame(cnx, bytes, bytes_max, current_time, path_x); + ack_needed = 1; + break; + case picoquic_frame_type_handshake_done: + bytes = picoquic_decode_handshake_done_frame(cnx, bytes, current_time); + ack_needed = 1; + break; + case picoquic_frame_type_datagram: + case picoquic_frame_type_datagram_l: + bytes = picoquic_decode_datagram_frame(cnx, bytes, bytes_max); + break; + default: { + uint64_t frame_id64; + if ((bytes = picoquic_frames_varint_decode(bytes, bytes_max, &frame_id64)) != NULL) { + switch (frame_id64) { + case picoquic_frame_type_ack_frequency: + bytes = picoquic_decode_ack_frequency_frame(bytes, bytes_max, cnx); + ack_needed = 1; + break; + case picoquic_frame_type_time_stamp: + bytes = picoquic_decode_time_stamp_frame(bytes, bytes_max, cnx, &packet_data); + ack_needed = 0; + break; + default: + /* Not implemented yet! */ + picoquic_connection_error(cnx, PICOQUIC_TRANSPORT_FRAME_FORMAT_ERROR, frame_id64); + bytes = NULL; + break; + } + } + break; + } + } + } + } + + if (bytes != NULL) { + process_decoded_packet_data(cnx, current_time, &packet_data); + } + + if (bytes != NULL && ack_needed != 0) { + cnx->latest_progress_time = current_time; + picoquic_set_ack_needed(cnx, current_time, pc); + } + + return bytes != NULL ? 0 : PICOQUIC_ERROR_DETECTED; +} + +/* +* The STREAM skipping function only supports the varint format. +* The old "fixed int" versions are supported by code in the skip_frame function +*/ +static const uint8_t* picoquic_skip_stream_frame(const uint8_t* bytes, const uint8_t* bytes_max) +{ + uint8_t len = bytes[0] & 2; + uint8_t off = bytes[0] & 4; + + if ((bytes = picoquic_frames_varint_skip(bytes+1, bytes_max)) != NULL && + (off == 0 || (bytes = picoquic_frames_varint_skip(bytes, bytes_max)) != NULL)) + { + bytes = (len == 0) ? (uint8_t*)bytes_max : picoquic_frames_length_data_skip(bytes, bytes_max); + } + + return bytes; +} + +/* + * Crypto HS skipping, very similar to stream frame + */ + +static const uint8_t* picoquic_skip_crypto_hs_frame(const uint8_t* bytes, const uint8_t* bytes_max) +{ + if ((bytes = picoquic_frames_varint_skip(bytes+1, bytes_max)) != NULL) { + bytes = picoquic_frames_length_data_skip(bytes, bytes_max); + } + return bytes; +} + +/* + * Closing frames + */ +static const uint8_t* picoquic_skip_connection_close_frame(const uint8_t* bytes, const uint8_t* bytes_max) +{ + bytes = picoquic_frames_varint_skip(bytes + 1, bytes_max); + if (bytes != NULL && + (bytes = picoquic_frames_varint_skip(bytes, bytes_max)) != NULL) { + bytes = picoquic_frames_length_data_skip(bytes, bytes_max); + } + return bytes; +} + +static const uint8_t* picoquic_skip_application_close_frame(const uint8_t* bytes, const uint8_t* bytes_max) +{ + + bytes = picoquic_frames_varint_skip(bytes + 1, bytes_max); + + if (bytes != NULL) { + bytes = picoquic_frames_length_data_skip(bytes, bytes_max); + } + return bytes; +} + + +/* + * The ACK skipping function only supports the varint format. + * The old "fixed int" versions are supported by code in the skip_frame function + */ +static const uint8_t* picoquic_skip_ack_frame_maybe_ecn(const uint8_t* bytes, const uint8_t* bytes_max, int is_ecn, int has_1wd) +{ + uint64_t nb_blocks; + + if ((bytes = picoquic_frames_varint_skip(bytes + 1, bytes_max)) != NULL) { + if (has_1wd) { + bytes = picoquic_frames_varint_skip(bytes, bytes_max); + } + if (bytes != NULL && + (bytes = picoquic_frames_varint_skip(bytes, bytes_max)) != NULL && + (bytes = picoquic_frames_varint_decode(bytes, bytes_max, &nb_blocks)) != NULL && + (bytes = picoquic_frames_varint_skip(bytes, bytes_max)) != NULL) + { + while (nb_blocks-- != 0) { + if ((bytes = picoquic_frames_varint_skip(bytes, bytes_max)) == NULL || + (bytes = picoquic_frames_varint_skip(bytes, bytes_max)) == NULL) + { + break; + } + } + } + } + + if (bytes != NULL && is_ecn) { + for (int i = 0; bytes != NULL && i < 3; i++) { + bytes = picoquic_frames_varint_skip(bytes, bytes_max); + } + } + + return bytes; +} + +static const uint8_t* picoquic_skip_ack_frame(const uint8_t* bytes, const uint8_t* bytes_max) { + return picoquic_skip_ack_frame_maybe_ecn(bytes, bytes_max, 0, 0); +} + +static const uint8_t* picoquic_skip_ack_ecn_frame(const uint8_t* bytes, const uint8_t* bytes_max) { + return picoquic_skip_ack_frame_maybe_ecn(bytes, bytes_max, 1, 0); +} + +/* Lots of simple frames... + */ + +static const uint8_t* picoquic_skip_stream_reset_frame(const uint8_t* bytes, const uint8_t* bytes_max) +{ + /* Stream ID */ + bytes = picoquic_frames_varint_skip(bytes + 1, bytes_max); + /* Error code */ + if (bytes != NULL) { + bytes = picoquic_frames_varint_skip(bytes, bytes_max); + } + /* Offset */ + if (bytes != NULL) + { + bytes = picoquic_frames_varint_skip(bytes, bytes_max); + } + return bytes; +} + +static const uint8_t* picoquic_skip_max_stream_data_frame(const uint8_t* bytes, const uint8_t* bytes_max) +{ + if ((bytes = picoquic_frames_varint_skip(bytes+1, bytes_max)) != NULL) { + bytes = picoquic_frames_varint_skip(bytes, bytes_max); + } + return bytes; +} + +static const uint8_t* picoquic_skip_stream_blocked_frame(const uint8_t* bytes, const uint8_t* bytes_max) +{ + if ((bytes = picoquic_frames_varint_skip(bytes+1, bytes_max)) != NULL) { + bytes = picoquic_frames_varint_skip(bytes, bytes_max); + } + return bytes; +} + + +int picoquic_skip_frame(const uint8_t* bytes, size_t bytes_maxsize, size_t* consumed, int* pure_ack) +{ + const uint8_t *bytes_max = bytes + bytes_maxsize; + uint8_t first_byte = bytes[0]; + + *pure_ack = 1; + + if (PICOQUIC_IN_RANGE(first_byte, picoquic_frame_type_stream_range_min, picoquic_frame_type_stream_range_max)) { + *pure_ack = 0; + bytes = picoquic_skip_stream_frame(bytes, bytes_max); + } else if (first_byte == picoquic_frame_type_ack) { + bytes = picoquic_skip_ack_frame(bytes, bytes_max); + } else if (first_byte == picoquic_frame_type_ack_ecn) { + bytes = picoquic_skip_ack_ecn_frame(bytes, bytes_max); + } else { + switch (first_byte) { + case picoquic_frame_type_padding: + bytes = picoquic_skip_0len_frame(bytes, bytes_max); + break; + case picoquic_frame_type_reset_stream: + bytes = picoquic_skip_stream_reset_frame(bytes, bytes_max); + *pure_ack = 0; + break; + case picoquic_frame_type_connection_close: { + bytes = picoquic_skip_connection_close_frame(bytes, bytes_max); + *pure_ack = 0; + break; + } + case picoquic_frame_type_application_close: { + bytes = picoquic_skip_application_close_frame(bytes, bytes_max); + *pure_ack = 0; + break; + } + case picoquic_frame_type_max_data: + bytes = picoquic_frames_varint_skip(bytes+1, bytes_max); + *pure_ack = 0; + break; + case picoquic_frame_type_max_stream_data: + bytes = picoquic_skip_max_stream_data_frame(bytes, bytes_max); + *pure_ack = 0; + break; + case picoquic_frame_type_max_streams_bidir: + case picoquic_frame_type_max_streams_unidir: + bytes = picoquic_frames_varint_skip(bytes+1, bytes_max); + *pure_ack = 0; + break; + case picoquic_frame_type_ping: + bytes = picoquic_skip_0len_frame(bytes, bytes_max); + *pure_ack = 0; + break; + case picoquic_frame_type_data_blocked: + bytes = picoquic_frames_varint_skip(bytes+1, bytes_max); + *pure_ack = 0; + break; + case picoquic_frame_type_stream_data_blocked: + bytes = picoquic_skip_stream_blocked_frame(bytes, bytes_max); + *pure_ack = 0; + break; + case picoquic_frame_type_streams_blocked_bidir: + case picoquic_frame_type_streams_blocked_unidir: + bytes = picoquic_frames_varint_skip(bytes+1, bytes_max); + *pure_ack = 0; + break; + case picoquic_frame_type_new_connection_id: + bytes = picoquic_skip_new_connection_id_frame(bytes, bytes_max); + *pure_ack = 0; + break; + case picoquic_frame_type_stop_sending: + bytes = picoquic_skip_stop_sending_frame(bytes, bytes_max); + *pure_ack = 0; + break; + case picoquic_frame_type_path_challenge: + bytes = picoquic_frames_fixed_skip(bytes+1, bytes_max, challenge_length); + break; + case picoquic_frame_type_path_response: + bytes = picoquic_frames_fixed_skip(bytes+1, bytes_max, challenge_length); + break; + case picoquic_frame_type_crypto_hs: + bytes = picoquic_skip_crypto_hs_frame(bytes, bytes_max); + *pure_ack = 0; + break; + case picoquic_frame_type_new_token: + bytes = picoquic_skip_new_token_frame(bytes, bytes_max); + *pure_ack = 0; + break; + case picoquic_frame_type_retire_connection_id: + /* the old code point for ACK frames, but this is taken care of in the ACK tests above */ + bytes = picoquic_skip_retire_connection_id_frame(bytes, bytes_max); + *pure_ack = 0; + break; + case picoquic_frame_type_handshake_done: + bytes = bytes + 1; + *pure_ack = 0; + break; + case picoquic_frame_type_datagram: + case picoquic_frame_type_datagram_l: + bytes = picoquic_skip_datagram_frame(bytes, bytes_max); + break; + default: { + uint64_t frame_id64; + if ((bytes = picoquic_frames_varint_decode(bytes, bytes_max, &frame_id64)) != NULL) { + switch (frame_id64) { + case picoquic_frame_type_ack_frequency: + bytes = picoquic_skip_ack_frequency_frame(bytes, bytes_max); + *pure_ack = 0; + break; + case picoquic_frame_type_time_stamp: + bytes = picoquic_skip_time_stamp_frame(bytes, bytes_max); + break; + default: + /* Not implemented yet! */ + bytes = NULL; + } + } + break; + } + } + } + + *consumed = (bytes != NULL) ? bytes_maxsize - (bytes_max - bytes) : bytes_maxsize; + + return bytes == NULL; +} + +int picoquic_decode_closing_frames(uint8_t* bytes, size_t bytes_max, int* closing_received) +{ + int ret = 0; + size_t byte_index = 0; + + *closing_received = 0; + while (ret == 0 && byte_index < bytes_max) { + uint8_t first_byte = bytes[byte_index]; + + if (first_byte == picoquic_frame_type_connection_close || first_byte == picoquic_frame_type_application_close) { + *closing_received = 1; + break; + } else { + size_t consumed = 0; + int pure_ack = 0; + + ret = picoquic_skip_frame(bytes + byte_index, + bytes_max - byte_index, &consumed, &pure_ack); + byte_index += consumed; + } + } + + return ret; +} diff --git a/deps/picoquic/intformat.c b/deps/picoquic/intformat.c new file mode 100644 index 0000000..2d45489 --- /dev/null +++ b/deps/picoquic/intformat.c @@ -0,0 +1,160 @@ +/* +* Author: Christian Huitema +* Copyright (c) 2017, Private Octopus, Inc. +* All rights reserved. +* +* Permission to use, copy, modify, and distribute this software for any +* purpose with or without fee is hereby granted, provided that the above +* copyright notice and this permission notice appear in all copies. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL Private Octopus, Inc. BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#ifndef WIN32 +#include +#endif + +void picoformat_16(uint8_t* bytes, uint16_t n16) +{ + bytes[0] = (uint8_t)(n16 >> 8); + bytes[1] = (uint8_t)(n16); +} + +void picoformat_24(uint8_t* bytes, uint32_t n24) +{ + bytes[0] = (uint8_t)(n24 >> 16); + bytes[1] = (uint8_t)(n24 >> 8); + bytes[2] = (uint8_t)(n24); +} + +void picoformat_32(uint8_t* bytes, uint32_t n32) +{ + bytes[0] = (uint8_t)(n32 >> 24); + bytes[1] = (uint8_t)(n32 >> 16); + bytes[2] = (uint8_t)(n32 >> 8); + bytes[3] = (uint8_t)(n32); +} + +void picoformat_64(uint8_t* bytes, uint64_t n64) +{ + bytes[0] = (uint8_t)(n64 >> 56); + bytes[1] = (uint8_t)(n64 >> 48); + bytes[2] = (uint8_t)(n64 >> 40); + bytes[3] = (uint8_t)(n64 >> 32); + bytes[4] = (uint8_t)(n64 >> 24); + bytes[5] = (uint8_t)(n64 >> 16); + bytes[6] = (uint8_t)(n64 >> 8); + bytes[7] = (uint8_t)(n64); +} + +/* + * Summary of Integer Encodings + * 2Bit Length Usable Bits Range + * 00 1 6 0-63 + * 01 2 14 0-16383 + * 10 4 30 0-1073741823 + * 11 8 62 0-4611686018427387903 + */ +size_t picoquic_encode_varint_length(uint64_t n64) +{ + if (n64 < 16384) { + if (n64 < 64) { + return 1u; + } else { + return 2u; + } + } else { + if (n64 < 1073741824) { + return 4u; + } else { + return 8u; + } + } +} + +size_t picoquic_decode_varint_length(uint8_t byte) +{ + return ((size_t)1u) << ((byte & 0xC0) >> 6); +} + +size_t picoquic_varint_encode(uint8_t* bytes, size_t max_bytes, uint64_t n64) +{ + uint8_t* x = bytes; + + if (n64 < 16384) { + if (n64 < 64) { + if (max_bytes > 0) { + *x++ = (uint8_t)(n64); + } + } else { + if (max_bytes >= 2) { + *x++ = (uint8_t)((n64 >> 8) | 0x40); + *x++ = (uint8_t)(n64); + } + } + } else if (n64 < 1073741824) { + if (max_bytes >= 4) { + *x++ = (uint8_t)((n64 >> 24) | 0x80); + *x++ = (uint8_t)(n64 >> 16); + *x++ = (uint8_t)(n64 >> 8); + *x++ = (uint8_t)(n64); + } + } else { + if (max_bytes >= 8) { + *x++ = (uint8_t)((n64 >> 56) | 0xC0); + *x++ = (uint8_t)(n64 >> 48); + *x++ = (uint8_t)(n64 >> 40); + *x++ = (uint8_t)(n64 >> 32); + *x++ = (uint8_t)(n64 >> 24); + *x++ = (uint8_t)(n64 >> 16); + *x++ = (uint8_t)(n64 >> 8); + *x++ = (uint8_t)(n64); + } + } + + return (x - bytes); +} + +void picoquic_varint_encode_16(uint8_t* bytes, uint16_t n16) +{ + uint8_t* x = bytes; + + *x++ = (uint8_t)(((n16 >> 8) | 0x40)&0x7F); + *x++ = (uint8_t)(n16); +} + +size_t picoquic_varint_decode(const uint8_t* bytes, size_t max_bytes, uint64_t* n64) +{ + size_t length = ((size_t)1) << ((bytes[0] & 0xC0) >> 6); + + if (length > max_bytes) { + length = 0; + *n64 = 0; + } else { + uint64_t v = *bytes++ & 0x3F; + + for (size_t i = 1; i < length; i++) { + v <<= 8; + v += *bytes++; + } + + *n64 = v; + } + + return length; +} + +size_t picoquic_varint_skip(const uint8_t* bytes) +{ + return picoquic_decode_varint_length(bytes[0]); +} diff --git a/deps/picoquic/logger.c b/deps/picoquic/logger.c new file mode 100644 index 0000000..1e85f27 --- /dev/null +++ b/deps/picoquic/logger.c @@ -0,0 +1,2186 @@ +/* +* Author: Christian Huitema +* Copyright (c) 2017, Private Octopus, Inc. +* All rights reserved. +* +* Permission to use, copy, modify, and distribute this software for any +* purpose with or without fee is hereby granted, provided that the above +* copyright notice and this permission notice appear in all copies. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL Private Octopus, Inc. BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/* +* Packet logging. +*/ +#include +#include +#include +#include +#include +#include "picoquic_internal.h" +#include "bytestream.h" +#include "tls_api.h" +#include "picoquic_unified_log.h" + +void picoquic_log_bytes(FILE* F, uint8_t* bytes, size_t bytes_max) +{ + for (size_t i = 0; i < bytes_max;) { + fprintf(F, "%04x: ", (int)i); + + for (int j = 0; j < 16 && i < bytes_max; j++, i++) { + fprintf(F, "%02x ", bytes[i]); + } + fprintf(F, "\n"); + } +} + +void picoquic_log_error_packet(FILE* F, uint8_t* bytes, size_t bytes_max, int ret) +{ + fprintf(F, "Packet length %d caused error: %d\n", (int)bytes_max, ret); + + picoquic_log_bytes(F, bytes, bytes_max); + + fprintf(F, "\n"); +} + +void picoquic_log_time(FILE* F, picoquic_cnx_t* cnx, uint64_t current_time, + const char* label1, const char* label2) +{ + uint64_t delta_t = (cnx == NULL) ? current_time : current_time - cnx->start_time; + uint64_t time_sec = delta_t / 1000000; + uint32_t time_usec = (uint32_t)(delta_t % 1000000); + + fprintf(F, "%s%llu.%06d%s", label1, + (unsigned long long)time_sec, time_usec, label2); +} + +const char * picoquic_log_fin_or_event_name(picoquic_call_back_event_t ev) +{ + char const * text = "unknown"; + switch (ev) { + case picoquic_callback_stream_data: + text = "stream data"; + break; + case picoquic_callback_stream_fin: + text = "stream fin"; + break; + case picoquic_callback_stream_reset: + text = "stream reset"; + break; + case picoquic_callback_stop_sending: + text = "stop sending"; + break; + case picoquic_callback_close: + text = "connection close"; + break; + case picoquic_callback_application_close: + text = "application close"; + break; + case picoquic_callback_version_negotiation: + text = "version negotiation"; + break; + case picoquic_callback_stream_gap: + text = "stream gap"; + break; + case picoquic_callback_prepare_to_send: + text = "ready to send"; + break; + case picoquic_callback_almost_ready: + text = "almost ready"; + break; + case picoquic_callback_ready: + text = "ready"; + break; + default: + break; + } + + return text; +} + +void picoquic_log_prefix_initial_cid64(FILE* F, uint64_t log_cnxid64) +{ + if (log_cnxid64 != 0) { + fprintf(F, "%016llx: ", (unsigned long long)log_cnxid64); + } +} + +static void picoquic_log_address(FILE* F, const struct sockaddr* addr_peer) +{ + if (addr_peer->sa_family == AF_INET) { + struct sockaddr_in* s4 = (struct sockaddr_in*)addr_peer; + uint8_t* addr = (uint8_t*)&s4->sin_addr; + + fprintf(F, "%d.%d.%d.%d:%d", + addr[0], addr[1], addr[2], addr[3], + ntohs(s4->sin_port)); + } + else { + struct sockaddr_in6* s6 = (struct sockaddr_in6*)addr_peer; + uint8_t* addr = (uint8_t*)&s6->sin6_addr; + + fprintf(F, "["); + for (int i = 0; i < 8; i++) { + if (i != 0) { + fprintf(F, ":"); + } + + if (addr[2 * i] != 0) { + fprintf(F, "%x%02x", addr[2 * i], addr[(2 * i) + 1]); + } + else { + fprintf(F, "%x", addr[(2 * i) + 1]); + } + } + fprintf(F, "]:%d", ntohs(s6->sin6_port)); + } +} + +void picoquic_log_packet_address(FILE* F, uint64_t log_cnxid64, picoquic_cnx_t* cnx, + const struct sockaddr* addr_peer, int receiving, size_t length, uint64_t current_time) +{ + uint64_t delta_t = 0; + uint64_t time_sec = 0; + uint32_t time_usec = 0; + + picoquic_log_prefix_initial_cid64(F, log_cnxid64); + + fprintf(F, (receiving) ? "Receiving %d bytes from " : "Sending %d bytes to ", + (int)length); + + picoquic_log_address(F, addr_peer); + + if (cnx != NULL) { + delta_t = current_time - cnx->start_time; + time_sec = delta_t / 1000000; + time_usec = (uint32_t)(delta_t % 1000000); + } + + fprintf(F, " at T=%llu.%06d (%llx)\n", + (unsigned long long)time_sec, time_usec, + (unsigned long long)current_time); +} + +char const* picoquic_log_state_name(picoquic_state_enum state) +{ + char const* state_name = "unknown"; + + switch (state) { + case picoquic_state_client_init: + state_name = "client_init"; + break; + case picoquic_state_client_init_sent: + state_name = "client_init_sent"; + break; + case picoquic_state_client_renegotiate: + state_name = "client_renegotiate"; + break; + case picoquic_state_client_retry_received: + state_name = "client_retry_received"; + break; + case picoquic_state_client_init_resent: + state_name = "client_init_resent"; + break; + case picoquic_state_server_init: + state_name = "server_init"; + break; + case picoquic_state_server_handshake: + state_name = "server_handshake"; + break; + case picoquic_state_client_handshake_start: + state_name = "client_handshake_start"; + break; + case picoquic_state_client_almost_ready: + state_name = "client_almost_ready"; + break; + case picoquic_state_handshake_failure: + state_name = "handshake_failure"; + break; + case picoquic_state_handshake_failure_resend: + state_name = "handshake_failure_resend"; + break; + case picoquic_state_server_almost_ready: + state_name = "server_almost_ready"; + break; + case picoquic_state_server_false_start: + state_name = "server_false_start"; + break; + case picoquic_state_client_ready_start: + state_name = "client_ready_start"; + break; + case picoquic_state_ready: + state_name = "ready"; + break; + case picoquic_state_disconnecting: + state_name = "disconnecting"; + break; + case picoquic_state_closing_received: + state_name = "closing_received"; + break; + case picoquic_state_closing: + state_name = "closing"; + break; + case picoquic_state_draining: + state_name = "draining"; + break; + case picoquic_state_disconnected: + state_name = "disconnected"; + break; + default: + break; + } + return state_name; +} + +char const* picoquic_log_ptype_name(picoquic_packet_type_enum ptype) +{ + char const* ptype_name = "unknown"; + + switch (ptype) { + case picoquic_packet_error: + ptype_name = "error"; + break; + case picoquic_packet_version_negotiation: + ptype_name = "version negotiation"; + break; + case picoquic_packet_initial: + ptype_name = "initial"; + break; + case picoquic_packet_retry: + ptype_name = "retry"; + break; + case picoquic_packet_handshake: + ptype_name = "handshake"; + break; + case picoquic_packet_0rtt_protected: + ptype_name = "0rtt protected"; + break; + case picoquic_packet_1rtt_protected: + ptype_name = "1rtt protected"; + break; + default: + break; + } + + return ptype_name; +} + +char const* picoquic_log_frame_names(uint64_t frame_type) +{ + char const * frame_name = "unknown"; + + switch ((picoquic_frame_type_enum_t)frame_type) { + case picoquic_frame_type_padding: + frame_name = "padding"; + break; + case picoquic_frame_type_reset_stream: + frame_name = "reset_stream"; + break; + case picoquic_frame_type_connection_close: + frame_name = "connection_close"; + break; + case picoquic_frame_type_application_close: + frame_name = "application_close"; + break; + case picoquic_frame_type_max_data: + frame_name = "max_data"; + break; + case picoquic_frame_type_max_stream_data: + frame_name = "max_stream_data"; + break; + case picoquic_frame_type_max_streams_bidir: + frame_name = "max_streams_bidir"; + break; + case picoquic_frame_type_max_streams_unidir: + frame_name = "max_streams_unidir"; + break; + case picoquic_frame_type_ping: + frame_name = "ping"; + break; + case picoquic_frame_type_data_blocked: + frame_name = "data_blocked"; + break; + case picoquic_frame_type_stream_data_blocked: + frame_name = "stream_data_blocked"; + break; + case picoquic_frame_type_streams_blocked_bidir: + frame_name = "streams_blocked_bidir"; + break; + case picoquic_frame_type_streams_blocked_unidir: + frame_name = "streams_blocked_unidir"; + break; + case picoquic_frame_type_new_connection_id: + frame_name = "new_connection_id"; + break; + case picoquic_frame_type_stop_sending: + frame_name = "stop_sending"; + break; + case picoquic_frame_type_ack: + frame_name = "ack"; + break; + case picoquic_frame_type_path_challenge: + frame_name = "path_challenge"; + break; + case picoquic_frame_type_path_response: + frame_name = "path_response"; + break; + case picoquic_frame_type_crypto_hs: + frame_name = "crypto_hs"; + break; + case picoquic_frame_type_new_token: + frame_name = "new_token"; + break; + case picoquic_frame_type_ack_ecn: + frame_name = "ack_ecn"; + break; + case picoquic_frame_type_retire_connection_id: + frame_name = "retire_connection_id"; + break; + case picoquic_frame_type_handshake_done: + frame_name = "handshake_done"; + break; + case picoquic_frame_type_datagram: + case picoquic_frame_type_datagram_l: + frame_name = "datagram"; + break; + case picoquic_frame_type_ack_frequency: + frame_name = "ack_frequency"; + break; + case picoquic_frame_type_time_stamp: + frame_name = "time_stamp"; + break; + default: + if (PICOQUIC_IN_RANGE(frame_type, picoquic_frame_type_stream_range_min, picoquic_frame_type_stream_range_max)) { + frame_name = "stream"; + } + break; + } + + return frame_name; +} + +char const* picoquic_log_tp_name(uint64_t tp_number) +{ + char const * tp_name = "unknown"; + + switch (tp_number) { + case picoquic_tp_original_connection_id: + tp_name = "ocid"; + break; + case picoquic_tp_idle_timeout: + tp_name = "idle_timeout"; + break; + case picoquic_tp_stateless_reset_token: + tp_name = "stateless_reset_token"; + break; + case picoquic_tp_max_packet_size: + tp_name = "max_packet_size"; + break; + case picoquic_tp_initial_max_data: + tp_name = "initial_max_data"; + break; + case picoquic_tp_initial_max_stream_data_bidi_local: + tp_name = "max_stream_data_bidi_local"; + break; + case picoquic_tp_initial_max_stream_data_bidi_remote: + tp_name = "max_stream_data_bidi_remote"; + break; + case picoquic_tp_initial_max_stream_data_uni: + tp_name = "max_stream_data_uni"; + break; + case picoquic_tp_initial_max_streams_bidi: + tp_name = "max_streams_bidi"; + break; + case picoquic_tp_initial_max_streams_uni: + tp_name = "max_streams_uni"; + break; + case picoquic_tp_ack_delay_exponent: + tp_name = "ack_delay_exponent"; + break; + case picoquic_tp_max_ack_delay: + tp_name = "max_ack_delay"; + break; + case picoquic_tp_disable_migration: + tp_name = "disable_migration"; + break; + case picoquic_tp_server_preferred_address: + tp_name = "server_preferred_address"; + break; + case picoquic_tp_active_connection_id_limit: + tp_name = "active_connection_id_limit"; + break; + case picoquic_tp_retry_connection_id: + tp_name = "rcid"; + break; + case picoquic_tp_handshake_connection_id: + tp_name = "hcid"; + break; + case picoquic_tp_max_datagram_frame_size: + tp_name = "max_datagram_frame_size"; + break; + case picoquic_tp_test_large_chello: + tp_name = "large_chello"; + break; + case picoquic_tp_enable_loss_bit_old: + tp_name = "enable_loss_bit(old)"; + break; + case picoquic_tp_enable_loss_bit: + tp_name = "enable_loss_bit"; + break; + case picoquic_tp_min_ack_delay: + tp_name = "min_ack_delay"; + break; + case picoquic_tp_enable_time_stamp: + tp_name = "enable_time_stamp"; + break; + case picoquic_tp_grease_quic_bit: + tp_name = "grease_quic_bit"; + break; + default: + break; + } + + return tp_name; +} + +void picoquic_log_connection_id(FILE* F, picoquic_connection_id_t * cid) +{ + fprintf(F, "<"); + for (uint8_t i = 0; i < cid->id_len; i++) { + fprintf(F, "%02x", cid->id[i]); + } + fprintf(F, ">"); +} + +void picoquic_log_packet_header(FILE* F, uint64_t log_cnxid64, picoquic_packet_header* ph, int receiving) +{ + picoquic_log_prefix_initial_cid64(F, log_cnxid64); + + fprintf(F, "%s packet type: %d (%s), ", (receiving != 0)?"Receiving":"Sending", + ph->ptype, picoquic_log_ptype_name(ph->ptype)); + + fprintf(F, "S%d,", ph->spin); + fprintf(F, " Q%d,", !ph->quic_bit_is_zero); + + switch (ph->ptype) { + case picoquic_packet_1rtt_protected: + /* Short packets. Log dest CID and Seq number. */ + fprintf(F, "\n"); + picoquic_log_prefix_initial_cid64(F, log_cnxid64); + fprintf(F, " "); + picoquic_log_connection_id(F, &ph->dest_cnx_id); + fprintf(F, ", Seq: %d (%llu), Phi: %d,", ph->pn, (unsigned long long)ph->pn64, ph->key_phase); + if (ph->has_loss_bits) { + fprintf(F, " Q(%d), L(%d),", ph->loss_bit_Q, ph->loss_bit_L); + } + fprintf(F, "\n"); + break; + case picoquic_packet_version_negotiation: + /* V nego. log both CID */ + fprintf(F, "\n"); + picoquic_log_prefix_initial_cid64(F, log_cnxid64); + fprintf(F, " "); + picoquic_log_connection_id(F, &ph->dest_cnx_id); + fprintf(F, ", "); + picoquic_log_connection_id(F, &ph->srce_cnx_id); + fprintf(F, "\n"); + break; + default: + /* Long packets. Log Vnum, both CID, Seq num, Payload length */ + fprintf(F, " Version %x,", ph->vn); + + fprintf(F, "\n"); + picoquic_log_prefix_initial_cid64(F, log_cnxid64); + fprintf(F, " "); + picoquic_log_connection_id(F, &ph->dest_cnx_id); + fprintf(F, ", "); + picoquic_log_connection_id(F, &ph->srce_cnx_id); + fprintf(F, ", Seq: %d, pl: %zd\n", ph->pn, ph->pl_val); + if (ph->ptype == picoquic_packet_initial) { + picoquic_log_prefix_initial_cid64(F, log_cnxid64); + fprintf(F, " Token length: %zd", ph->token_length); + if (ph->token_length > 0) { + size_t printed_length = (ph->token_length > 16) ? 16 : ph->token_length; + fprintf(F, ", Token: "); + for (size_t i = 0; i < printed_length; i++) { + fprintf(F, "%02x", ph->token_bytes[i]); + } + if (printed_length < ph->token_length) { + fprintf(F, "..."); + } + } + fprintf(F, "\n"); + } + break; + } +} + +void picoquic_log_negotiation_packet(FILE* F, uint64_t log_cnxid64, + const uint8_t* bytes, size_t length, picoquic_packet_header* ph) +{ + size_t byte_index = ph->offset; + uint32_t vn = 0; + + picoquic_log_prefix_initial_cid64(F, log_cnxid64); + + fprintf(F, " versions: "); + + while (byte_index + 4 <= length) { + vn = PICOPARSE_32(bytes + byte_index); + byte_index += 4; + fprintf(F, "%x, ", vn); + } + fprintf(F, "\n"); +} + +void picoquic_log_retry_packet(FILE* F, uint64_t log_cnxid64, + const uint8_t* bytes, picoquic_packet_header* ph) +{ + size_t byte_index = ph->offset; + int token_length = 0; + uint8_t odcil; + int payload_length = (int)(ph->payload_length); + int is_err = 0; + int has_checksum = picoquic_supported_versions[ph->version_index].version_retry_key != NULL; + + if (!has_checksum) { + odcil = bytes[byte_index]; + byte_index++; + payload_length--; + + if ((int)odcil > payload_length) { + picoquic_log_prefix_initial_cid64(F, log_cnxid64); + fprintf(F, " packet too short, ODCIL: %d, only %d bytes available.\n", + odcil, payload_length); + is_err = 1; + } + else { + /* Dump the old connection ID */ + picoquic_log_prefix_initial_cid64(F, log_cnxid64); + fprintf(F, " ODCIL: <"); + for (uint8_t i = 0; i < odcil; i++) { + fprintf(F, "%02x", bytes[byte_index++]); + } + token_length = payload_length - odcil; + fprintf(F, ">, Token length: %d\n", token_length); + } + } + else { + int checksum_length = 16; + + if (checksum_length >= payload_length) { + picoquic_log_prefix_initial_cid64(F, log_cnxid64); + fprintf(F, " packet too short, checksum: %d bytes, only %d bytes available.\n", + checksum_length, payload_length); + is_err = 1; + } + else { + token_length = payload_length - checksum_length; + + picoquic_log_prefix_initial_cid64(F, log_cnxid64); + fprintf(F, " Token length: %d, Checksum length: %d\n", + token_length, checksum_length); + } + } + + /* Print the token if there was no error */ + if (token_length > 0 && !is_err) { + int printed_length = (token_length > 16) ? 16 : token_length; + picoquic_log_prefix_initial_cid64(F, log_cnxid64); + fprintf(F, " Token: "); + for (uint8_t i = 0; i < printed_length; i++) { + fprintf(F, "%02x", bytes[byte_index++]); + } + if (printed_length < token_length) { + fprintf(F, "..."); + } + fprintf(F, "\n"); + } +} + +size_t picoquic_log_stream_frame(FILE* F, const uint8_t* bytes, size_t bytes_max) +{ + size_t byte_index; + uint64_t stream_id; + size_t data_length; + uint64_t offset; + int fin; + int ret = 0; + + int suspended = debug_printf_reset(1); + ret = picoquic_parse_stream_header(bytes, bytes_max, + &stream_id, &offset, &data_length, &fin, &byte_index); + (void)debug_printf_reset(suspended); + + if (ret != 0) + return bytes_max; + + fprintf(F, " Stream %" PRIu64 ", offset %" PRIu64 ", length %d, fin = %d", stream_id, + offset, (int)data_length, fin); + + fprintf(F, ": "); + for (size_t i = 0; i < 8 && i < data_length; i++) { + fprintf(F, "%02x", bytes[byte_index + i]); + } + fprintf(F, "%s\n", (data_length > 8) ? "..." : ""); + + return byte_index + data_length; +} + +size_t picoquic_log_ack_frame(FILE* F, uint64_t cnx_id64, const uint8_t* bytes, size_t bytes_max, int is_ecn) +{ + size_t byte_index; + uint64_t num_block; + uint64_t largest; + uint64_t ack_delay; + uint64_t ecnx3[3]; + int suspended = debug_printf_reset(1); + int ret; + + ret = picoquic_parse_ack_header(bytes, bytes_max, &num_block, + &largest, &ack_delay, &byte_index, 0); + + (void)debug_printf_reset(suspended); + + if (ret != 0) + return bytes_max; + + /* Now that the size is good, print it */ + if (is_ecn) { + fprintf(F, " ACK_ECN (nb=%u)", (int)num_block); + } + else { + fprintf(F, " ACK (nb=%u)", (int)num_block); + } + + /* decoding the acks */ + + for (;;) { + uint64_t range; + uint64_t block_to_block; + + if (byte_index >= bytes_max) { + fprintf(F, " Malformed ACK RANGE, %d blocks remain.\n", (int)num_block); + break; + } + + size_t l_range = picoquic_varint_decode(bytes + byte_index, bytes_max - byte_index, &range); + if (l_range == 0) { + byte_index = bytes_max; + fprintf(F, " Malformed ACK RANGE, requires %d bytes out of %d", (int)picoquic_varint_skip(bytes), + (int)(bytes_max - byte_index)); + break; + } + else { + byte_index += l_range; + } + + range++; + + if (largest + 1 < range) { + fprintf(F, "\n"); + if (cnx_id64 != 0) { + fprintf(F, "%" PRIx64 ": ", cnx_id64); + } + fprintf(F, " ack range error: largest=%" PRIu64 ", range=%" PRIu64, largest, range); + byte_index = bytes_max; + break; + } + + if (range <= 1) + fprintf(F, ", %" PRIu64, largest); + else + fprintf(F, ", %" PRIu64 "-%" PRIu64, largest - range + 1, largest); + + if (num_block-- == 0) + break; + + /* Skip the gap */ + + if (byte_index >= bytes_max) { + fprintf(F, "\n"); + if (cnx_id64 != 0) { + fprintf(F, "%" PRIx64 ": ", cnx_id64); + } + fprintf(F, " Malformed ACK GAP, %d blocks remain.", (int)num_block); + byte_index = bytes_max; + break; + } + else { + size_t l_gap = picoquic_varint_decode(bytes + byte_index, bytes_max - byte_index, &block_to_block); + if (l_gap == 0) { + byte_index = bytes_max; + fprintf(F, "\n"); + if (cnx_id64 != 0) { + fprintf(F, "%" PRIx64 ": ", cnx_id64); + } + fprintf(F, " Malformed ACK GAP, requires %d bytes out of %d", (int)picoquic_varint_skip(bytes), + (int)(bytes_max - byte_index)); + break; + } + else { + byte_index += l_gap; + block_to_block += 1; + block_to_block += range; + } + } + + if (largest < block_to_block) { + fprintf(F, "\n"); + if (cnx_id64 != 0) { + fprintf(F, "%" PRIx64 ": ", cnx_id64); + } + fprintf(F, " ack gap error: largest=%" PRIu64 ", range=%" PRIu64 ", gap=%" PRIu64, + largest, range, block_to_block - range); + byte_index = bytes_max; + break; + } + + largest -= block_to_block; + } + + if (is_ecn) { + /* Decode the ecn counts */ + for (int ecnx = 0; ecnx < 3; ecnx++) { + size_t l_ecnx = picoquic_varint_decode(bytes + byte_index, bytes_max - byte_index, &ecnx3[ecnx]); + + if (l_ecnx == 0) { + fprintf(F, ", incorrect ECN encoding"); + byte_index = bytes_max; + break; + } + else { + byte_index += l_ecnx; + } + } + + fprintf(F, ", ect0=%llu, ect1=%llu, ce=%llu\n", + (unsigned long long)ecnx3[0], (unsigned long long)ecnx3[1], (unsigned long long)ecnx3[2]); + } + else { + fprintf(F, "\n"); + } + + return byte_index; +} + +size_t picoquic_log_reset_stream_frame(FILE* F, const uint8_t* bytes, size_t bytes_max) +{ + size_t byte_index = 1; + uint64_t stream_id = 0; + uint64_t error_code = 0; + uint64_t offset = 0; + + size_t l1 = 0, l2 = 0, l3 = 0; + if (bytes_max > 2) { + l1 = picoquic_varint_decode(bytes + byte_index, bytes_max - byte_index, &stream_id); + byte_index += l1; + if (l1 > 0) { + l2 = picoquic_varint_decode(bytes + byte_index, bytes_max - byte_index, &error_code); + byte_index += l2; + } + if (l2 > 0) { + l3 = picoquic_varint_decode(bytes + byte_index, bytes_max - byte_index, &offset); + byte_index += l3; + } + } + + if (l1 == 0 || l2 == 0 || l3 == 0) { + fprintf(F, " Malformed RESET STREAM, requires %d bytes out of %d\n", (int)(byte_index + ((l1 == 0) ? (picoquic_varint_skip(bytes + 1) + 3) : picoquic_varint_skip(bytes + byte_index))), + (int)bytes_max); + byte_index = bytes_max; + } else { + fprintf(F, " RESET STREAM %llu, Error 0x%08x, Offset 0x%llx.\n", + (unsigned long long)stream_id, (uint32_t)error_code, (unsigned long long)offset); + } + + return byte_index; +} + +size_t picoquic_log_stop_sending_frame(FILE* F, const uint8_t* bytes, size_t bytes_max) +{ + size_t byte_index = 1; + size_t l1 = 0; + size_t l2 = 0; + uint64_t stream_id; + uint64_t error_code = 0; + + l1 = picoquic_varint_decode(bytes + byte_index, bytes_max - byte_index, &stream_id); + if (l1 != 0){ + byte_index += l1; + l2 = picoquic_varint_decode(bytes + byte_index, bytes_max - byte_index, &error_code); + byte_index += l2; + } + + if (l1 == 0 || l2 == 0 || byte_index > bytes_max) { + fprintf(F, " Malformed STOP SENDING, requires more than %d bytes\n", (int)bytes_max); + return bytes_max; + } + + fprintf(F, " STOP SENDING Stream %lld (0x%llx), Error 0x%llx.\n", + (unsigned long long)stream_id, (unsigned long long)stream_id, (unsigned long long)error_code); + + return byte_index; +} + +size_t picoquic_log_generic_close_frame(FILE* F, const uint8_t* bytes, size_t bytes_max, uint8_t ftype, uint64_t cnx_id64) +{ + size_t byte_index = 1; + uint64_t error_code = 0; + uint64_t string_length = 0; + uint64_t offending_frame_type = 0; + size_t lf = 0; + size_t l1 = 0; + size_t l0 = 0; + + if (bytes_max >= 2) { + l0 = picoquic_varint_decode(bytes + byte_index, bytes_max - byte_index, &error_code); + byte_index += l0; + if (ftype == picoquic_frame_type_connection_close && l0 != 0) { + lf = picoquic_varint_decode(bytes + byte_index, bytes_max - byte_index, &offending_frame_type); + if (lf == 0) { + byte_index = bytes_max; + } + else { + byte_index += lf; + } + } + if (ftype != picoquic_frame_type_connection_close || lf != 0) { + l1 = picoquic_varint_decode(bytes + byte_index, bytes_max - byte_index, &string_length); + } + } + + if (l1 == 0 || l0 == 0) { + fprintf(F, " Malformed %s, requires %d bytes out of %d\n", + picoquic_log_frame_names(ftype), + (int)(byte_index + picoquic_varint_skip(bytes + 3)), (int)bytes_max); + byte_index = bytes_max; + } + else { + byte_index += l1; + + fprintf(F, " %s, Error 0x%04x, ", picoquic_log_frame_names(ftype), (uint16_t)error_code); + if (ftype == picoquic_frame_type_connection_close && + offending_frame_type != 0) { + fprintf(F, "Offending frame 0x%llx, ", + (unsigned long long)offending_frame_type); + } + fprintf(F, "Reason length %llu\n", (unsigned long long)string_length); + if (byte_index + string_length > bytes_max) { + fprintf(F, " Malformed %s, requires %llu bytes out of %llu\n", + picoquic_log_frame_names(ftype), + (unsigned long long)(byte_index + string_length), (unsigned long long)bytes_max); + byte_index = bytes_max; + } + else if (string_length > 0) { + /* Print the UTF8 string */ + char reason_string[49]; + uint64_t printed_length = (string_length > 48) ? 48 : string_length; + + for (uint32_t i = 0; i < printed_length; i++) { + int c = bytes[byte_index + i]; + + if (c < 0x20 || c > 0x7E) { + c = '.'; + } + reason_string[i] = (char) c; + } + reason_string[printed_length] = 0; + + if (cnx_id64 != 0) { + fprintf(F, "%" PRIx64 ": ", cnx_id64); + } + + fprintf(F, " Reason: %s", reason_string); + if (string_length > printed_length) { + fprintf(F, "..."); + } + fprintf(F, "\n"); + + byte_index += (size_t)string_length; + } + } + + return byte_index; +} + +size_t picoquic_log_connection_close_frame(FILE* F, const uint8_t* bytes, size_t bytes_max, uint64_t cnx_id64) +{ + return picoquic_log_generic_close_frame(F, bytes, bytes_max, picoquic_frame_type_connection_close, cnx_id64); +} + +size_t picoquic_log_application_close_frame(FILE* F, const uint8_t* bytes, size_t bytes_max, uint64_t cnx_id64) +{ + return picoquic_log_generic_close_frame(F, bytes, bytes_max, picoquic_frame_type_application_close, cnx_id64); +} + +size_t picoquic_log_max_data_frame(FILE* F, const uint8_t* bytes, size_t bytes_max) +{ + size_t byte_index = 1; + uint64_t max_data; + + size_t l1 = picoquic_varint_decode(bytes + 1, bytes_max - 1, &max_data); + + if (1 + l1 > bytes_max) { + fprintf(F, " Malformed MAX DATA, requires %d bytes out of %d\n", (int)(1 + l1), (int)bytes_max); + return bytes_max; + } else { + byte_index = 1 + l1; + } + + fprintf(F, " MAX DATA: 0x%llx.\n", (unsigned long long)max_data); + + return byte_index; +} + +size_t picoquic_log_max_stream_data_frame(FILE* F, const uint8_t* bytes, size_t bytes_max) +{ + size_t byte_index = 1; + uint64_t stream_id; + uint64_t max_data; + + size_t l1 = picoquic_varint_decode(bytes + 1, bytes_max - 1, &stream_id); + size_t l2 = picoquic_varint_decode(bytes + 1 + l1, bytes_max - 1 - l1, &max_data); + + if (l1 == 0 || l2 == 0) { + fprintf(F, " Malformed MAX STREAM DATA, requires %d bytes out of %d\n", + (int)(1 + l1 + l2), (int)bytes_max); + return bytes_max; + } else { + byte_index = 1 + l1 + l2; + } + + fprintf(F, " MAX STREAM DATA, Stream: %" PRIu64 ", max data: 0x%llx.\n", + stream_id, (unsigned long long)max_data); + + return byte_index; +} + +size_t picoquic_log_max_stream_id_frame(FILE* F, const uint8_t* bytes, size_t bytes_max, uint64_t frame_id) +{ + size_t byte_index = 1; + const size_t min_size = 1 + picoquic_varint_skip(bytes + 1); + uint64_t rank; + + if (min_size > bytes_max) { + fprintf(F, " Malformed %s, requires %d bytes out of %d\n", picoquic_log_frame_names(frame_id), + (int)min_size, (int)bytes_max); + return bytes_max; + } + + /* Now that the size is good, parse and print it */ + byte_index += picoquic_varint_decode(bytes + byte_index, bytes_max - byte_index, &rank); + + fprintf(F, " %s: max rank %" PRIu64 ".\n", picoquic_log_frame_names(frame_id), rank); + + return byte_index; +} + +size_t picoquic_log_blocked_frame(FILE* F, const uint8_t* bytes, size_t bytes_max) +{ + size_t byte_index = 1; + const size_t min_size = 1 + picoquic_varint_skip(bytes + 1); + uint64_t blocked_offset = 0; + + if (min_size > bytes_max) { + fprintf(F, " Malformed BLOCKED, requires %d bytes out of %d\n", (int)min_size, (int)bytes_max); + return bytes_max; + } + + /* Now that the size is good, parse and print it */ + byte_index += picoquic_varint_decode(bytes + byte_index, bytes_max - byte_index, &blocked_offset); + + fprintf(F, " BLOCKED: offset %" PRIu64 ".\n", + blocked_offset); + + return byte_index; +} + +size_t picoquic_log_stream_blocked_frame(FILE* F, const uint8_t* bytes, size_t bytes_max) +{ + size_t byte_index = 1; + const size_t min_size = 1 + picoquic_varint_skip(bytes + 1); + uint64_t blocked_stream_id; + + if (min_size > bytes_max) { + fprintf(F, " Malformed STREAM BLOCKED, requires %d bytes out of %d\n", (int)min_size, (int)bytes_max); + return bytes_max; + } + + /* Now that the size is good, parse and print it */ + byte_index += picoquic_varint_decode(bytes + byte_index, bytes_max - byte_index, &blocked_stream_id); + byte_index += picoquic_varint_skip(&bytes[byte_index]); + + fprintf(F, " STREAM BLOCKED: %" PRIu64 ".\n", + blocked_stream_id); + + return byte_index; +} + +size_t picoquic_log_streams_blocked_frame(FILE* F, const uint8_t* bytes, size_t bytes_max, uint64_t frame_id) +{ + size_t byte_index = 1; + const size_t min_size = 1 + picoquic_varint_skip(bytes + 1); + uint64_t blocked_stream_rank; + + if (min_size > bytes_max) { + fprintf(F, " Malformed %s frame, requires %d bytes out of %d\n", picoquic_log_frame_names(frame_id), + (int)min_size, (int)bytes_max); + byte_index = bytes_max; + } + else { + byte_index += picoquic_varint_decode(bytes + byte_index, bytes_max - byte_index, &blocked_stream_rank); + fprintf(F, " %s: %lld\n", picoquic_log_frame_names(frame_id), (unsigned long long) blocked_stream_rank); + } + + return byte_index; +} + +size_t picoquic_log_new_connection_id_frame(FILE* F, const uint8_t* bytes, size_t bytes_max) +{ + size_t byte_index = 1; + size_t min_size = 2u + 16u; + uint64_t sequence; + uint64_t retire_before = 0; + picoquic_connection_id_t new_cnx_id = picoquic_null_connection_id; + uint8_t l_cid = 0; + size_t l_seq = 0; + size_t l_ret = 1; + + l_seq = picoquic_varint_decode(&bytes[byte_index], bytes_max, &sequence); + min_size += l_seq; + byte_index += l_seq; + + l_ret = picoquic_varint_decode(&bytes[byte_index], bytes_max, &retire_before); + min_size += l_ret; + byte_index += l_ret; + + if (byte_index < bytes_max) { + l_cid = bytes[byte_index++]; + } + min_size += l_cid; + + if (l_seq == 0 || l_ret == 0 || min_size > bytes_max) { + fprintf(F, " Malformed NEW CONNECTION ID, requires %d bytes out of %d\n", (int)min_size, (int)bytes_max); + byte_index = bytes_max; + } + else { + byte_index += picoquic_parse_connection_id(bytes + byte_index, l_cid, &new_cnx_id); + fprintf(F, " NEW CONNECTION ID[%d]: 0x", (int)sequence); + for (int x = 0; x < new_cnx_id.id_len; x++) { + fprintf(F, "%02x", new_cnx_id.id[x]); + } + fprintf(F, ", "); + for (int x = 0; x < 16; x++) { + fprintf(F, "%02x", bytes[byte_index++]); + } + if (retire_before != 0) { + fprintf(F, ", retire before: %d", (int)retire_before); + } + fprintf(F, "\n"); + } + + return byte_index; +} + +size_t picoquic_log_retire_connection_id_frame(FILE* F, const uint8_t* bytes, size_t bytes_max) +{ + size_t byte_index = 1; + uint64_t sequence = 0; + size_t l_seq = 0; + + + if (bytes_max > byte_index) { + l_seq = picoquic_varint_decode(bytes + byte_index, bytes_max - byte_index, &sequence); + byte_index += l_seq; + } + + if (l_seq == 0 || byte_index > bytes_max) { + fprintf(F, " Malformed RETIRE CONNECTION ID, requires %d bytes out of %d\n", (int)(byte_index + ((l_seq == 0)?1:0)), (int)bytes_max); + byte_index = bytes_max; + } + else { + fprintf(F, " RETIRE CONNECTION ID[%d]\n", (int)sequence); + } + + return byte_index; +} + +size_t picoquic_log_new_token_frame(FILE* F, const uint8_t* bytes, size_t bytes_max) +{ + size_t byte_index = 1; + size_t min_size = 1; + size_t l_toklen = 0; + uint64_t toklen = 0; + + l_toklen = picoquic_varint_decode(&bytes[byte_index], bytes_max, &toklen); + + min_size += l_toklen + (size_t)toklen; + + if (l_toklen == 0 || min_size > bytes_max) { + fprintf(F, " Malformed NEW CONNECTION ID, requires %d bytes out of %d\n", (int)min_size, (int)bytes_max); + return bytes_max; + } else { + byte_index += l_toklen; + fprintf(F, " NEW TOKEN[%d]: 0x", (int)toklen); + for (uint64_t x = 0; x < toklen && x < 16u; x++) { + fprintf(F, "%02x", bytes[byte_index + x]); + } + byte_index += (size_t)toklen; + + if (toklen > 16) { + fprintf(F, "..."); + } + fprintf(F, "\n"); + } + + return byte_index; +} + +size_t picoquic_log_path_frame(FILE* F, const uint8_t* bytes, size_t bytes_max) +{ + size_t byte_index = 1; + size_t challenge_length = 8; + + if (byte_index + challenge_length > bytes_max) { + fprintf(F, " Malformed %s frame, %d bytes needed, %d available\n", + picoquic_log_frame_names(bytes[0]), + (int)(challenge_length + 1), (int)bytes_max); + byte_index = bytes_max; + } else { + fprintf(F, " %s: ", picoquic_log_frame_names(bytes[0])); + + for (size_t i = 0; i < challenge_length; i++) { + fprintf(F, "%02x", bytes[byte_index + i]); + } + + fprintf(F, "\n"); + + byte_index += challenge_length; + } + + return byte_index; +} + +size_t picoquic_log_crypto_hs_frame(FILE* F, const uint8_t* bytes, size_t bytes_max) +{ + uint64_t offset=0; + uint64_t data_length = 0; + size_t byte_index = 1; + size_t l_off = 0; + size_t l_len = 0; + + if (bytes_max > byte_index) { + l_off = picoquic_varint_decode(bytes + byte_index, bytes_max - byte_index, &offset); + byte_index += l_off; + } + + if (bytes_max > byte_index) { + l_len = picoquic_varint_decode(bytes + byte_index, bytes_max - byte_index, &data_length); + byte_index += l_len; + } + + if (l_off == 0 || l_len == 0 || byte_index + data_length > bytes_max) { + fprintf(F, " Malformed Crypto HS frame.\n"); + byte_index = bytes_max; + } else { + fprintf(F, " Crypto HS frame, offset %" PRIu64 ", length %d", offset, (int)data_length); + + fprintf(F, ": "); + for (size_t i = 0; i < 8 && i < data_length; i++) { + fprintf(F, "%02x", bytes[byte_index + i]); + } + fprintf(F, "%s\n", (data_length > 8) ? "..." : ""); + + byte_index += (size_t)data_length; + } + + return byte_index; +} + +size_t picoquic_log_datagram_frame(FILE* F, const uint8_t* bytes, size_t bytes_max) +{ + uint8_t frame_id = bytes[0]; + unsigned int has_length = frame_id & 1; + size_t l_l = 0; + uint64_t length = 0; + size_t byte_index = 1; + + if (has_length) { + if (bytes_max > byte_index) { + l_l = picoquic_varint_decode(bytes + byte_index, bytes_max - byte_index, &length); + byte_index += l_l; + } + } + else { + length = bytes_max - byte_index; + } + + if ((has_length && l_l == 0) || byte_index + length > bytes_max) { + /* log format error */ + fprintf(F, " Malformed Datagram frame: "); + for (size_t i = 0; i < bytes_max && i < 8; i++) { + fprintf(F, "%02x", bytes[i]); + } + if (bytes_max > 8) { + fprintf(F, "..."); + } + fprintf(F, "\n"); + + byte_index = bytes_max; + } + else { + fprintf(F, " Datagram frame"); + fprintf(F, ", length: %d: ", (int)length); + for (size_t i = 0; i < 8 && i < length; i++) { + fprintf(F, "%02x", bytes[byte_index + i]); + } + fprintf(F, "%s\n", (length > 8) ? "..." : ""); + + byte_index += (size_t)length; + } + + return byte_index; +} + +size_t picoquic_log_ack_frequency_frame(FILE* F, const uint8_t* bytes, size_t bytes_max) +{ + uint64_t sequence = 0; + uint64_t packets = 0; + uint64_t microsecs = 0; + const uint8_t* bytes_end = bytes + bytes_max; + const uint8_t* bytes0 = bytes; + size_t byte_index = 0; + + + if ((bytes = picoquic_frames_varint_skip(bytes, bytes_end)) == NULL || + (bytes = picoquic_parse_ack_frequency_frame(bytes, bytes_end, &sequence, &packets, µsecs)) == NULL) { + fprintf(F, " Malformed ACK Frequency frame: "); + /* log format error */ + for (size_t i = 0; i < bytes_max && i < 8; i++) { + fprintf(F, "%02x", bytes0[i]); + } + if (bytes_max > 8) { + fprintf(F, "..."); + } + fprintf(F, "\n"); + byte_index = bytes_max; + } + else { + fprintf(F, " ACK Frequency: S=%" PRIu64 ", P=%" PRIu64 ", uS=%" PRIu64 "\n", + sequence, packets, microsecs); + byte_index = bytes - bytes0; + } + + return byte_index; +} + +size_t picoquic_log_time_stamp_frame(FILE* F, const uint8_t* bytes, size_t bytes_max) +{ + uint64_t time_stamp = 0; + const uint8_t* bytes_end = bytes + bytes_max; + const uint8_t* bytes0 = bytes; + size_t byte_index = 0; + + + if ((bytes = picoquic_frames_varint_skip(bytes, bytes_end)) == NULL || + (bytes = picoquic_frames_varint_decode(bytes, bytes_end, &time_stamp)) == NULL) { + fprintf(F, " Malformed Time Stamp frame: "); + /* log format error */ + for (size_t i = 0; i < bytes_max && i < 8; i++) { + fprintf(F, "%02x", bytes0[i]); + } + if (bytes_max > 8) { + fprintf(F, "..."); + } + fprintf(F, "\n"); + byte_index = bytes_max; + } + else { + fprintf(F, " Time Stamp: %" PRIu64 "\n", + time_stamp); + byte_index = bytes - bytes0; + } + + return byte_index; +} + +void picoquic_log_frames(FILE* F, uint64_t cnx_id64, const uint8_t* bytes, size_t length) +{ + size_t byte_index = 0; + + while (byte_index < length) { + uint64_t frame_id = 0; + size_t frame_id_ll = picoquic_varint_decode(bytes + byte_index, length - byte_index, &frame_id); + + picoquic_log_prefix_initial_cid64(F, cnx_id64); + + if (frame_id_ll == 0 || (frame_id < 64 && frame_id_ll != 1)) { + size_t id_length = length - byte_index; + char const* id_more = ""; + if (id_length > 8) { + id_length = 8; + id_more = "..."; + } + fprintf(F, " Incorrect frame id: "); + for (size_t x = 0; x < id_length; x++) { + fprintf(F, "%02x", bytes[byte_index++]); + } + fprintf(F, "%s\n", id_more); + byte_index = length; + continue; + } + + if (PICOQUIC_IN_RANGE(frame_id, picoquic_frame_type_stream_range_min, picoquic_frame_type_stream_range_max)) { + byte_index += picoquic_log_stream_frame(F, bytes + byte_index, length - byte_index); + continue; + } + + switch (frame_id) { + case picoquic_frame_type_ack: + byte_index += picoquic_log_ack_frame(F, cnx_id64, bytes + byte_index, length - byte_index, 0); + break; + case picoquic_frame_type_ack_ecn: + byte_index += picoquic_log_ack_frame(F, cnx_id64, bytes + byte_index, length - byte_index, 1); + break; + case picoquic_frame_type_retire_connection_id: + byte_index += picoquic_log_retire_connection_id_frame(F, bytes + byte_index, length - byte_index); + break; + case picoquic_frame_type_padding: + case picoquic_frame_type_ping: { + int nb = 0; + + while (byte_index < length && bytes[byte_index] == frame_id) { + byte_index++; + nb++; + } + + fprintf(F, " %s, %d bytes\n", picoquic_log_frame_names(frame_id), nb); + break; + } + case picoquic_frame_type_reset_stream: /* RST_STREAM */ + byte_index += picoquic_log_reset_stream_frame(F, bytes + byte_index, + length - byte_index); + break; + case picoquic_frame_type_connection_close: /* CONNECTION_CLOSE */ + byte_index += picoquic_log_connection_close_frame(F, bytes + byte_index, + length - byte_index, cnx_id64); + break; + case picoquic_frame_type_application_close: + byte_index += picoquic_log_application_close_frame(F, bytes + byte_index, + length - byte_index, cnx_id64); + break; + case picoquic_frame_type_max_data: /* MAX_DATA */ + byte_index += picoquic_log_max_data_frame(F, bytes + byte_index, + length - byte_index); + break; + case picoquic_frame_type_max_stream_data: /* MAX_STREAM_DATA */ + byte_index += picoquic_log_max_stream_data_frame(F, bytes + byte_index, + length - byte_index); + break; + case picoquic_frame_type_max_streams_bidir: /* MAX_STREAM_ID */ + case picoquic_frame_type_max_streams_unidir: /* MAX_STREAM_ID */ + byte_index += picoquic_log_max_stream_id_frame(F, bytes + byte_index, + length - byte_index, frame_id); + break; + case picoquic_frame_type_data_blocked: /* BLOCKED */ + /* No payload */ + byte_index += picoquic_log_blocked_frame(F, bytes + byte_index, + length - byte_index); + break; + case picoquic_frame_type_stream_data_blocked: /* STREAM_BLOCKED */ + byte_index += picoquic_log_stream_blocked_frame(F, bytes + byte_index, + length - byte_index); + break; + case picoquic_frame_type_streams_blocked_bidir: /* STREAM_ID_NEEDED */ + case picoquic_frame_type_streams_blocked_unidir: /* STREAM_ID_NEEDED */ + byte_index += picoquic_log_streams_blocked_frame(F, bytes + byte_index, + length - byte_index, frame_id); + break; + case picoquic_frame_type_new_connection_id: /* NEW_CONNECTION_ID */ + byte_index += picoquic_log_new_connection_id_frame(F, bytes + byte_index, length - byte_index); + break; + case picoquic_frame_type_stop_sending: /* STOP_SENDING */ + byte_index += picoquic_log_stop_sending_frame(F, bytes + byte_index, + length - byte_index); + break; + case picoquic_frame_type_path_challenge: + byte_index += picoquic_log_path_frame(F, bytes + byte_index, + length - byte_index); + break; + case picoquic_frame_type_path_response: + byte_index += picoquic_log_path_frame(F, bytes + byte_index, + length - byte_index); + break; + case picoquic_frame_type_crypto_hs: + byte_index += picoquic_log_crypto_hs_frame(F, bytes + byte_index, + length - byte_index); + break; + case picoquic_frame_type_new_token: + byte_index += picoquic_log_new_token_frame(F, bytes + byte_index, length - byte_index); + break; + case picoquic_frame_type_handshake_done: + fprintf(F, " %s\n", picoquic_log_frame_names(frame_id)); + byte_index++; + break; + case picoquic_frame_type_datagram: + case picoquic_frame_type_datagram_l: + byte_index += picoquic_log_datagram_frame(F, bytes + byte_index, length - byte_index); + break; + case picoquic_frame_type_ack_frequency: + byte_index += picoquic_log_ack_frequency_frame(F, bytes + byte_index, length - byte_index); + break; + case picoquic_frame_type_time_stamp: + byte_index += picoquic_log_time_stamp_frame(F, bytes + byte_index, length - byte_index); + break; + + default: { + /* Not implemented yet! */ + fprintf(F, " Unknown frame, type: %" PRIu64 " (0x", frame_id); + for (size_t i = 0; i < 8 && byte_index + i < length; i++) { + fprintf(F, "%02x", bytes[byte_index + i]); + } + if (byte_index + 8 < length) { + fprintf(F, "... + %zu bytes)\n", length - byte_index - 8); + } + else { + fprintf(F, ")"); + } + byte_index = length; + break; + } + } + } +} + +void picoquic_log_decrypted_segment(void* F_log, int log_cnxid, picoquic_cnx_t* cnx, + int receiving, picoquic_packet_header * ph, const uint8_t* bytes, size_t length, int ret) +{ + uint64_t log_cnxid64 = 0; + FILE * F = (FILE *)F_log; + + if (F == NULL) { + return; + } + + if (log_cnxid != 0) { + if (cnx == NULL) { + ph->pn64 = ph->pn; + if (ret == 0) { + if (ph->ptype == picoquic_packet_version_negotiation) { + log_cnxid64 = picoquic_val64_connection_id(ph->srce_cnx_id); + } + else { + log_cnxid64 = picoquic_val64_connection_id(ph->dest_cnx_id); + } + } + } + else { + log_cnxid64 = picoquic_val64_connection_id(picoquic_get_logging_cnxid(cnx)); + } + } + /* Header */ + picoquic_log_packet_header(F, log_cnxid64, ph, receiving); + + if (ret != 0) { + /* packet does parse or decrypt */ + picoquic_log_prefix_initial_cid64(F, log_cnxid64); + + if (ret == PICOQUIC_ERROR_STATELESS_RESET) { + fprintf(F, " Stateless reset.\n"); + } + else { + fprintf(F, " Header or encryption error: %x.\n", ret); + } + } + else if (ph->ptype == picoquic_packet_version_negotiation) { + /* log version negotiation */ + picoquic_log_negotiation_packet(F, log_cnxid64, bytes, length, ph); + } + else if (ph->ptype == picoquic_packet_retry) { + /* log version negotiation */ + picoquic_log_retry_packet(F, log_cnxid64, bytes, ph); + } + else if (ph->ptype != picoquic_packet_error) { + /* log frames inside packet */ + picoquic_log_prefix_initial_cid64(F, log_cnxid64); + fprintf(F, " %s %d bytes\n", (receiving)?"Decrypted": "Prepared", + (int)ph->payload_length); + picoquic_log_frames(F, log_cnxid64, bytes + ph->offset, ph->payload_length); + } + fprintf(F, "\n"); +} + +void picoquic_log_outgoing_segment(void* F_log, int log_cnxid, picoquic_cnx_t* cnx, + uint8_t * bytes, + uint64_t sequence_number, + size_t length, + uint8_t* send_buffer, size_t send_length, size_t pn_length) +{ + picoquic_cnx_t* pcnx = cnx; + picoquic_packet_header ph; + struct sockaddr_in default_addr; + size_t checksum_length = 16; + int ret; + + if (F_log == NULL) { + return; + } + memset(&default_addr, 0, sizeof(struct sockaddr_in)); + default_addr.sin_family = AF_INET; + + ret = picoquic_parse_packet_header((cnx == NULL) ? NULL : cnx->quic, send_buffer, send_length, + ((cnx == NULL || cnx->path[0] == NULL) ? (struct sockaddr *)&default_addr : + (struct sockaddr *)&cnx->path[0]->local_addr), &ph, &pcnx, 0); + + ph.pn64 = sequence_number; + ph.pn = (uint32_t)ph.pn64; + if (ph.ptype != picoquic_packet_retry) { + if (cnx != NULL) { + picoquic_epoch_enum epoch = (ph.ptype == picoquic_packet_1rtt_protected) ? picoquic_epoch_1rtt : + ((ph.ptype == picoquic_packet_0rtt_protected) ? picoquic_epoch_0rtt : + ((ph.ptype == picoquic_packet_handshake) ? picoquic_epoch_handshake : picoquic_epoch_initial)); + if (cnx->crypto_context[epoch].aead_encrypt != NULL) { + checksum_length = picoquic_get_checksum_length(cnx, epoch); + } + } + + if (ph.pn_offset != 0) { + ph.offset = ph.pn_offset + pn_length; + ph.payload_length -= pn_length; + } + } + if (ph.ptype != picoquic_packet_version_negotiation) { + if (ph.payload_length > checksum_length) { + ph.payload_length -= (uint16_t)checksum_length; + } + else { + ph.payload_length = 0; + } + } + /* log the segment. */ + picoquic_log_decrypted_segment(F_log, log_cnxid, cnx, 0, + &ph, bytes, length, ret); +} + +void picoquic_log_processing(FILE* F, picoquic_cnx_t* cnx, size_t length, int ret) +{ + fprintf(F, "Processed %d bytes, state = %d (%s), return %d\n\n", + (int)length, cnx->cnx_state, + picoquic_log_state_name(cnx->cnx_state), + ret); +} + +void picoquic_log_transport_extension_content(FILE* F, int log_cnxid, uint64_t cnx_id_64, + uint8_t* bytes, size_t bytes_max) +{ + int ret = 0; + size_t byte_index = 0; + + if (bytes_max < 256) + { + if (ret == 0) + { + size_t extensions_size = bytes_max; + size_t extensions_end; + + extensions_end = byte_index + extensions_size; + + if (log_cnxid != 0) { + picoquic_log_prefix_initial_cid64(F, cnx_id_64); + } + fprintf(F, " Extension list (%d bytes):\n", + (uint32_t)extensions_size); + while (ret == 0 && byte_index < extensions_end) { + uint64_t extension_type = 0; + uint64_t extension_length = 0; + size_t ll_type = 0; + size_t ll_length = 0; + + ll_type = picoquic_varint_decode(bytes + byte_index, extensions_end - byte_index, &extension_type); + byte_index += ll_type; + ll_length = picoquic_varint_decode(bytes + byte_index, extensions_end - byte_index, &extension_length); + byte_index += ll_length; + + if (ll_type == 0 || ll_length == 0 || byte_index + extension_length > extensions_end) { + if (log_cnxid != 0) { + picoquic_log_prefix_initial_cid64(F, cnx_id_64); + } + fprintf(F, " Malformed extension -- only %d bytes avaliable for type and length.\n", + (int)(extensions_end - byte_index)); + ret = -1; + } + else { + if (log_cnxid != 0) { + picoquic_log_prefix_initial_cid64(F, cnx_id_64); + } + fprintf(F, " Extension type: %d (%s), length %d%s", + (int)extension_type, picoquic_log_tp_name(extension_type), (int)extension_length, + (extension_length == 0) ? "" : ", "); + + if (byte_index + extension_length > extensions_end) { + if (log_cnxid != 0) { + picoquic_log_prefix_initial_cid64(F, cnx_id_64); + } + fprintf(F, "Malformed extension, only %d bytes available.\n", (int)(extensions_end - byte_index)); + ret = -1; + } + else { + for (uint16_t i = 0; i < extension_length; i++) { + fprintf(F, "%02x", bytes[byte_index++]); + } + fprintf(F, "\n"); + } + } + } + } + + if (ret == 0 && byte_index < bytes_max) { + if (log_cnxid != 0) { + picoquic_log_prefix_initial_cid64(F, cnx_id_64); + } + fprintf(F, " Remaining bytes (%d)\n", (uint32_t)(bytes_max - byte_index)); + } + } + else { + if (log_cnxid != 0) { + picoquic_log_prefix_initial_cid64(F, cnx_id_64); + } + fprintf(F, "Received transport parameter TLS extension (%d bytes):\n", (uint32_t)bytes_max); + if (log_cnxid != 0) { + picoquic_log_prefix_initial_cid64(F, cnx_id_64); + } + fprintf(F, " First bytes (%d):\n", (uint32_t)(bytes_max - byte_index)); + } + + if (ret == 0) + { + while (byte_index < bytes_max && byte_index < 128) { + if (log_cnxid != 0) { + fprintf(F, "%" PRIx64 ": ", cnx_id_64); + } + fprintf(F, " "); + for (int i = 0; i < 32 && byte_index < bytes_max && byte_index < 128; i++) { + fprintf(F, "%02x", bytes[byte_index++]); + } + fprintf(F, "\n"); + } + } +} + +void picoquic_textlog_transport_extension(FILE* F, picoquic_cnx_t* cnx, int received, int log_cnxid, uint8_t* bytes, size_t bytes_max) +{ + uint64_t cnx_id_64 = (log_cnxid) ? picoquic_val64_connection_id(picoquic_get_logging_cnxid(cnx)) : 0; + + picoquic_log_prefix_initial_cid64(F, cnx_id_64); + fprintf(F, "%s transport parameter TLS extension (%d bytes):\n", (received) ? "Received" : "Sending", (uint32_t)bytes_max); + picoquic_log_transport_extension_content(F, log_cnxid, cnx_id_64, bytes, bytes_max); +} + +void picoquic_log_transport_ids(FILE* F, picoquic_cnx_t* cnx, int log_cnxid) +{ + char const* sni = picoquic_tls_get_sni(cnx); + char const* alpn = picoquic_tls_get_negotiated_alpn(cnx); + uint64_t cnx_id64 = (log_cnxid) ? picoquic_val64_connection_id(picoquic_get_logging_cnxid(cnx)) : 0; + + picoquic_log_prefix_initial_cid64(F, cnx_id64); + if (sni == NULL) { + fprintf(F, "SNI not received.\n"); + } else { + fprintf(F, "Received SNI: %s\n", sni); + } + + picoquic_log_prefix_initial_cid64(F, cnx_id64); + if (alpn == NULL) { + fprintf(F, "ALPN not received.\n"); + } else { + fprintf(F, "Received ALPN: %s\n", alpn); + } +} + +void picoquic_textlog_negotiated_alpn(FILE* F, picoquic_cnx_t* cnx, int received, int log_cnxid, const ptls_iovec_t* list, size_t count) +{ + uint64_t cnx_id_64 = (log_cnxid) ? picoquic_val64_connection_id(picoquic_get_logging_cnxid(cnx)) : 0; + + picoquic_log_prefix_initial_cid64(F, cnx_id_64); + + fprintf(F, "%s ALPN list (%d): ", (received) ? "Received" : "Sending", (uint32_t)count); + + for (size_t i = 0; i < count; i++) { + char alpn_target[64]; + + if (list[i].len < 64) { + memcpy(alpn_target, list[i].base, list[i].len); + alpn_target[list[i].len] = 0; + } + else { + memcpy(alpn_target, list[i].base, 60); + alpn_target[60] = '.'; + alpn_target[61] = '.'; + alpn_target[62] = '.'; + alpn_target[63] = 0; + } + + fprintf(F, "%s%s", (i == 0) ? "" : ", ", alpn_target); + } + + fprintf(F, "\n"); +} + +void picoquic_log_congestion_state(FILE* F, picoquic_cnx_t* cnx, uint64_t current_time) +{ + picoquic_path_t * path_x = cnx->path[0]; + + fprintf(F, "%" PRIx64 ": ", picoquic_val64_connection_id(picoquic_get_logging_cnxid(cnx))); + picoquic_log_time(F, cnx, current_time, "T= ", ", "); + fprintf(F, "cwin: %d,", (int)path_x->cwin); + fprintf(F, "flight: %d,", (int)path_x->bytes_in_transit); + fprintf(F, "nb_ret: %d,", (int)cnx->nb_retransmission_total); + fprintf(F, "rtt_min: %d,", (int)path_x->rtt_min); + fprintf(F, "rtt: %d,", (int)path_x->smoothed_rtt); + fprintf(F, "rtt_var: %d,", (int)path_x->rtt_variant); + fprintf(F, "max_ack_delay: %d,", (int)path_x->max_ack_delay); + fprintf(F, "state: %d\n", (int)cnx->cnx_state); +} + +/* + From TLS 1.3 spec: + struct { + uint32 ticket_lifetime; + uint32 ticket_age_add; + opaque ticket_nonce<0..255>; + opaque ticket<1..2^16-1>; + Extension extensions<0..2^16-2>; + } NewSessionTicket; + + struct { + ExtensionType extension_type; + opaque extension_data<0..2^16-1>; + } Extension; +*/ +static void picoquic_textlog_tls_ticket(FILE* F, picoquic_connection_id_t cnx_id, + uint8_t* ticket, uint16_t ticket_length) +{ + uint64_t cnx_id64 = picoquic_val64_connection_id(cnx_id); + uint32_t lifetime = 0; + uint32_t age_add = 0; + uint8_t nonce_length = 0; + uint16_t ticket_val_length = 0; + uint16_t extension_length = 0; + uint8_t* extension_ptr = NULL; + uint16_t byte_index = 0; + uint16_t min_length = 4 + 4 + 1 + 2 + 2; + int ret = 0; + + if (ticket_length < min_length) { + ret = -1; + } else { + lifetime = PICOPARSE_32(ticket); + byte_index += 4; + age_add = PICOPARSE_32(ticket + byte_index); + byte_index += 4; + nonce_length = ticket[byte_index++]; + min_length += nonce_length; + if (ticket_length < min_length) { + ret = -1; + } else { + byte_index += nonce_length; + + ticket_val_length = PICOPARSE_16(ticket + byte_index); + byte_index += 2; + min_length += ticket_val_length; + if (ticket_length < min_length) { + ret = -1; + } else { + byte_index += ticket_val_length; + + extension_length = PICOPARSE_16(ticket + byte_index); + byte_index += 2; + if (extension_length > ticket_length - min_length){ + ret = -2; + } else { + extension_ptr = &ticket[byte_index]; + min_length += extension_length; + } + } + } + } + + if (ret == -1) { + picoquic_log_prefix_initial_cid64(F, cnx_id64); + fprintf(F, "Malformed ticket, length = %d, at least %d required.\n", ticket_length, min_length); + } + picoquic_log_prefix_initial_cid64(F, cnx_id64); + fprintf(F, "lifetime = %d, age_add = %x, %d nonce, %d ticket, %d extensions.\n", + lifetime, age_add, nonce_length, ticket_val_length, extension_length); + + if (extension_ptr != NULL) { + uint16_t x_index = 0; + + picoquic_log_prefix_initial_cid64(F, cnx_id64); + fprintf(F, "ticket extensions: "); + + while (x_index + 4 < extension_length) { + uint16_t x_type = PICOPARSE_16(extension_ptr + x_index); + uint16_t x_len = PICOPARSE_16(extension_ptr + x_index + 2); + x_index += 4 + x_len; + + if (x_type == 42 && x_len == 4) { + uint32_t ed_len = PICOPARSE_32(extension_ptr + x_index - 4); + fprintf(F, "%d(ED: %x),", x_type, ed_len); + } else { + fprintf(F, "%d (%d bytes),", x_type, x_len); + } + + if (x_index > extension_length) { + fprintf(F, "\n"); + picoquic_log_prefix_initial_cid64(F, cnx_id64); + fprintf(F, "malformed extensions, require %d bytes, not just %d", x_index, extension_length); + } + } + + fprintf(F, "\n"); + + if (x_index < extension_length) { + picoquic_log_prefix_initial_cid64(F, cnx_id64); + fprintf(F, "%d extra bytes at the end of the extensions\n", extension_length - x_index); + } + } + + if (ret == -2) { + picoquic_log_prefix_initial_cid64(F, cnx_id64); + fprintf(F, "Malformed TLS ticket, %d extra bytes.\n", ticket_length - min_length); + } +} + +/* + +From Picotls code: +uint64_t time; +uint16_t cipher_suite; +24 bit int = length of ticket; + +16 bit length + + + */ + +void picoquic_log_picotls_ticket(FILE* F, picoquic_connection_id_t cnx_id, + uint8_t* ticket, uint16_t ticket_length) +{ + uint64_t cnx_id64 = picoquic_val64_connection_id(cnx_id); + uint64_t ticket_time = 0; + uint16_t kx_id = 0; + uint16_t suite_id = 0; + uint32_t tls_ticket_length = 0; + uint8_t* tls_ticket_ptr = NULL; + uint16_t secret_length = 0; + /* uint8_t* secret_ptr = NULL; */ + uint16_t byte_index = 0; + uint32_t min_length = 8 + 2 + 3 + 2; + int ret = 0; + + if (ticket_length < min_length) { + ret = -1; + } else { + ticket_time = PICOPARSE_64(ticket); + byte_index += 8; + kx_id = PICOPARSE_16(ticket + byte_index); + byte_index += 2; + suite_id = PICOPARSE_16(ticket + byte_index); + byte_index += 2; + tls_ticket_length = PICOPARSE_24(ticket + byte_index); + byte_index += 3; + min_length += tls_ticket_length; + if (ticket_length < min_length) { + ret = -1; + } else { + tls_ticket_ptr = &ticket[byte_index]; + byte_index += (uint16_t) tls_ticket_length; + + secret_length = PICOPARSE_16(ticket + byte_index); + min_length += secret_length + 2; + if (ticket_length < min_length) { + ret = -1; + } else { + /* secret_ptr = &ticket[byte_index]; */ + if (ticket_length > min_length) { + ret = -2; + } + } + } + } + + picoquic_log_prefix_initial_cid64(F, cnx_id64); + fprintf(F, "ticket time = %llu, kx = %x, suite = %x, %d ticket, %d secret.\n", + (unsigned long long)ticket_time, + kx_id, suite_id, tls_ticket_length, secret_length); + + if (ret == -1) { + picoquic_log_prefix_initial_cid64(F, cnx_id64); + fprintf(F, "Malformed PTLS ticket, length = %d, at least %d required.\n", + ticket_length, min_length); + } else { + if (tls_ticket_length > 0 && tls_ticket_ptr != NULL) { + picoquic_textlog_tls_ticket(F, cnx_id, tls_ticket_ptr, (uint16_t) tls_ticket_length); + } + } + + if (ret == -2) { + picoquic_log_prefix_initial_cid64(F, cnx_id64); + fprintf(F, "Malformed PTLS ticket, %d extra bytes.\n", ticket_length - min_length); + } +} + +void picoquic_log_retry_packet_error(FILE* F, picoquic_cnx_t * cnx, char const * message) +{ + picoquic_log_prefix_initial_cid64(F, picoquic_val64_connection_id(picoquic_get_logging_cnxid(cnx))); + fprintf(F, "Retry packet rejected: %s\n", message); +} + +void picoquic_log_path_promotion(FILE* F, picoquic_cnx_t* cnx, int path_index, uint64_t current_time) +{ + uint64_t cnx_id64 = picoquic_val64_connection_id(picoquic_get_logging_cnxid(cnx)); + picoquic_log_prefix_initial_cid64(F, cnx_id64); + fprintf(F, "Path %d promoted to default at T=", path_index); + picoquic_log_time(F, cnx, current_time, "", "\n"); + picoquic_log_prefix_initial_cid64(F, cnx_id64); + fprintf(F, " Local address:"); + picoquic_log_address(F, (struct sockaddr*)& cnx->path[path_index]->local_addr); + fprintf(F, "\n"); + picoquic_log_prefix_initial_cid64(F, cnx_id64); + fprintf(F, " Peer address:"); + picoquic_log_address(F, (struct sockaddr*) & cnx->path[path_index]->peer_addr); + fprintf(F, "\n"); +} + +/* Adding here a declaration of binlog message defined in logwriter.c, + * so the call the log_app_message writes on both log file and binlog */ +void picoquic_binlog_message_v(picoquic_cnx_t* cnx, const char* fmt, va_list vargs); + +void picoquic_txtlog_message_v(picoquic_quic_t* quic, const picoquic_connection_id_t* cid, const char* fmt, va_list vargs) +{ + FILE* F = quic->F_log; + picoquic_log_prefix_initial_cid64(F, picoquic_val64_connection_id(*cid)); + +#ifdef _WINDOWS + (void)vfprintf_s(F, fmt, vargs); +#else + (void)vfprintf(F, fmt, vargs); +#endif + + fputc('\n', F); +} + +void txtlog_context_free_app_message(picoquic_quic_t* quic, const picoquic_connection_id_t * cid, const char* fmt, va_list vargs) +{ + if (quic->F_log != NULL) { + picoquic_txtlog_message_v(quic, cid, fmt, vargs); + } +} + +void textlog_app_message(picoquic_cnx_t* cnx, const char* fmt, va_list vargs) +{ + if (cnx->quic->F_log != NULL) { + picoquic_txtlog_message_v(cnx->quic, &cnx->initial_cnxid, fmt, vargs); + } +} + +void textlog_quic_pdu(picoquic_quic_t* quic, int receiving, uint64_t current_time, + uint64_t cid64, + const struct sockaddr* addr_peer, const struct sockaddr* addr_local, size_t packet_length) +{ +#ifdef _WINDOWS + UNREFERENCED_PARAMETER(addr_local); +#endif + if (quic->F_log != NULL) { + picoquic_log_packet_address(quic->F_log, cid64, + NULL, addr_peer, receiving, packet_length, current_time); + } +} + +void textlog_pdu_ex(picoquic_cnx_t* cnx, int receiving, uint64_t current_time, + const struct sockaddr* addr_peer, const struct sockaddr* addr_local, size_t packet_length) +{ +#ifdef _WINDOWS + UNREFERENCED_PARAMETER(addr_local); +#endif + if (cnx->quic->F_log != NULL && picoquic_cnx_is_still_logging(cnx)) { + picoquic_log_packet_address(cnx->quic->F_log, + picoquic_val64_connection_id(picoquic_get_logging_cnxid(cnx)), + cnx, addr_peer, receiving, packet_length, current_time); + } +} + +void textlog_packet(picoquic_cnx_t* cnx, int receiving, uint64_t current_time, + picoquic_packet_header* ph, const uint8_t* bytes, size_t bytes_max) +{ + if (cnx->quic->F_log != NULL && picoquic_cnx_is_still_logging(cnx)) { + picoquic_log_decrypted_segment(cnx->quic->F_log, 1, + cnx, receiving, ph, bytes, bytes_max, 0); + } +} + +void textlog_dropped_packet(picoquic_cnx_t* cnx, picoquic_packet_header* ph, + size_t packet_size, int ret, uint8_t* raw_data, uint64_t current_time) +{ + if (cnx->quic->F_log != NULL && picoquic_cnx_is_still_logging(cnx)) { + picoquic_log_decrypted_segment(cnx->quic->F_log, 1, cnx, 1, ph, raw_data, packet_size, ret); + } +} + +void textlog_buffered_packet(picoquic_cnx_t* cnx, + picoquic_packet_type_enum ptype, uint64_t current_time) +{ + if (cnx->quic->F_log != NULL && picoquic_cnx_is_still_logging(cnx)) { + FILE* F = cnx->quic->F_log; + + fprintf(F, "%" PRIx64 ": ", picoquic_val64_connection_id(picoquic_get_logging_cnxid(cnx))); + picoquic_log_time(F, cnx, current_time, "T= ", ", "); + fprintf(F, "Keys unavailable, buffered packet type %d.\n", ptype); + } +} + +void textlog_outgoing_packet(picoquic_cnx_t* cnx, + uint8_t* bytes, uint64_t sequence_number, size_t pn_length, size_t length, + uint8_t* send_buffer, size_t send_length, uint64_t current_time) +{ + if (cnx->quic->F_log != NULL && picoquic_cnx_is_still_logging(cnx)) { + picoquic_log_outgoing_segment(cnx->quic->F_log, 1, + cnx, bytes, sequence_number, length, send_buffer, send_length, pn_length); + } +} + +void textlog_packet_lost(picoquic_cnx_t* cnx, + picoquic_packet_type_enum ptype, uint64_t sequence_number, char const* trigger, + picoquic_connection_id_t* dcid, size_t packet_size, + uint64_t current_time) +{ + if (cnx->quic->F_log != NULL && picoquic_cnx_is_still_logging(cnx)) { + FILE* F = cnx->quic->F_log; + + fprintf(F, "%" PRIx64 ": ", picoquic_val64_connection_id(picoquic_get_logging_cnxid(cnx))); + picoquic_log_time(F, cnx, current_time, "T= ", ", "); + fprintf(F, "Lost packet type %d, number %" PRIu64 ", size %zu", ptype, sequence_number, packet_size); + if (dcid != NULL) { + fprintf(F, ", DCID "); + picoquic_log_connection_id(F, dcid); + } + fprintf(F, ", reason: %s\n", trigger); + } +} + +void textlog_negotiated_alpn(picoquic_cnx_t* cnx, int is_local, + uint8_t const* sni, size_t sni_len, uint8_t const* alpn, size_t alpn_len, + const ptls_iovec_t* alpn_list, size_t alpn_count) +{ +#ifdef _WINDOWS + UNREFERENCED_PARAMETER(sni); + UNREFERENCED_PARAMETER(sni_len); + UNREFERENCED_PARAMETER(alpn); + UNREFERENCED_PARAMETER(alpn_len); + UNREFERENCED_PARAMETER(alpn_list); + UNREFERENCED_PARAMETER(alpn_count); +#endif + if (cnx->quic->F_log != NULL && picoquic_cnx_is_still_logging(cnx)) { + /* TODO: alpn */ + picoquic_textlog_negotiated_alpn(cnx->quic->F_log, cnx, + (is_local) ? 0 : 1, 1, alpn_list, alpn_count); + } +} + + +void textlog_transport_extension(picoquic_cnx_t* cnx, int is_local, + size_t param_length, uint8_t* params) +{ + if (cnx->quic->F_log != NULL && picoquic_cnx_is_still_logging(cnx)) { + /* TODO: alpn */ + picoquic_textlog_transport_extension(cnx->quic->F_log, cnx, (is_local)?0:1, 1, params, param_length); + } +} + +void textlog_tls_ticket(picoquic_cnx_t* cnx, uint8_t* ticket, uint16_t ticket_length) +{ + if (cnx->quic->F_log != NULL && picoquic_cnx_is_still_logging(cnx)) { + picoquic_log_picotls_ticket(cnx->quic->F_log, picoquic_get_logging_cnxid(cnx), + ticket, ticket_length); + } +} + +void textlog_new_connection(picoquic_cnx_t* cnx) +{ +#ifdef _WINDOWS + UNREFERENCED_PARAMETER(cnx); +#endif +} + +void textlog_close_connection(picoquic_cnx_t* cnx) +{ +#ifdef _WINDOWS + UNREFERENCED_PARAMETER(cnx); +#endif +} + +void textlog_cc_dump(picoquic_cnx_t* cnx, uint64_t current_time) +{ + if (cnx->quic->F_log != NULL && picoquic_cnx_is_still_logging(cnx)) { + picoquic_log_congestion_state(cnx->quic->F_log, cnx, current_time); + } +} + +struct st_picoquic_unified_logging_t textlog_functions = { + /* Per context log function */ + txtlog_context_free_app_message, + textlog_quic_pdu, + /* Per connection functions */ + textlog_app_message, + textlog_pdu_ex, + textlog_packet, + textlog_dropped_packet, + textlog_buffered_packet, + textlog_outgoing_packet, + textlog_packet_lost, + textlog_negotiated_alpn, + textlog_transport_extension, + textlog_tls_ticket, + textlog_new_connection, + textlog_close_connection, + textlog_cc_dump +}; + +int picoquic_set_textlog(picoquic_quic_t* quic, char const* textlog_file) +{ + int ret = 0; + FILE* F_log; + + if (quic->F_log != NULL && quic->should_close_log) { + (void)picoquic_file_close(quic->F_log); + quic->F_log = NULL; + } + + if (textlog_file != NULL) { + F_log = picoquic_file_open(textlog_file, "w"); + if (F_log == NULL) { + DBG_PRINTF("Cannot create log file <%s>\n", textlog_file); + ret = -1; + } + else { + quic->F_log = F_log; + quic->should_close_log = 1; + } + + quic->text_log_fns = &textlog_functions; + } + + return ret; +} \ No newline at end of file diff --git a/deps/picoquic/logwriter.c b/deps/picoquic/logwriter.c new file mode 100644 index 0000000..49b17f9 --- /dev/null +++ b/deps/picoquic/logwriter.c @@ -0,0 +1,1147 @@ +/* +* Author: Christian Huitema +* Copyright (c) 2019, Private Octopus, Inc. +* All rights reserved. +* +* Permission to use, copy, modify, and distribute this software for any +* purpose with or without fee is hereby granted, provided that the above +* copyright notice and this permission notice appear in all copies. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL Private Octopus, Inc. BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/* +* Packet logging. +*/ + +#include +#include "picoquic_binlog.h" +#include "bytestream.h" +#include "tls_api.h" +#include "picotls.h" +#include "picoquic_unified_log.h" +#include "picoquic_binlog.h" + +#define VARINT_LEN(bytes) ((size_t)1 << (((bytes)[0] & 0xC0) >> 6)) + +static const uint8_t* picoquic_log_fixed_skip(const uint8_t* bytes, const uint8_t* bytes_max, size_t size) +{ + return bytes == NULL ? NULL : ((bytes += size) <= bytes_max ? bytes : NULL); +} + +static const uint8_t* picoquic_log_varint_skip(const uint8_t* bytes, const uint8_t* bytes_max) +{ + return bytes == NULL ? NULL : (bytes < bytes_max ? picoquic_log_fixed_skip(bytes, bytes_max, VARINT_LEN(bytes)) : NULL); +} + +static const uint8_t* picoquic_log_varint(const uint8_t* bytes, const uint8_t* bytes_max, uint64_t* n64) +{ + size_t len = (bytes == NULL) ? 0 : picoquic_varint_decode(bytes, bytes_max - bytes, n64); + return len == 0 ? NULL : bytes + len; +} + +static const uint8_t* picoquic_log_length(const uint8_t* bytes, const uint8_t* bytes_max, size_t* nsz) +{ + uint64_t n64 = 0; + size_t len = (bytes == NULL) ? 0 : picoquic_varint_decode(bytes, bytes_max - bytes, &n64); + *nsz = (size_t)n64; + return len == 0 || *nsz != n64 ? NULL : bytes + len; +} + +static void picoquic_binlog_frame(FILE* f, const uint8_t* bytes, const uint8_t* bytes_max) +{ + if (bytes != NULL && bytes_max != NULL) { + size_t len = bytes_max - bytes; + uint8_t varlen[8]; + size_t l_varlen = picoquic_varint_encode(varlen, 8, len); + fwrite(varlen, 1, l_varlen, f); + fwrite(bytes, 1, len, f); + } +} + +static const uint8_t* picoquic_log_stream_frame(FILE* f, const uint8_t* bytes, const uint8_t* bytes_max) +{ + const uint8_t* bytes_begin = bytes; + uint8_t ftype = bytes[0]; + size_t length = 0; + uint8_t log_buffer[256]; + int has_length = 0; + size_t extra_bytes = 8; + + bytes = picoquic_log_fixed_skip(bytes, bytes_max, 1); /* type */ + bytes = picoquic_log_varint_skip(bytes, bytes_max); /* stream */ + + if ((ftype & 4) != 0) { + bytes = picoquic_log_varint_skip(bytes, bytes_max); /* offset */ + } + + if ((ftype & 2) != 0) { + bytes = picoquic_log_length(bytes, bytes_max, &length); /* length */ + has_length = 1; + } else { + length = bytes_max - bytes; + } + + if (length < extra_bytes) { + /* Add up to 8 bytes of content that can be documented in the qlog */ + extra_bytes = length; + } + + if (has_length) { + picoquic_binlog_frame(f, bytes_begin, bytes + extra_bytes); + } + else { + uint8_t* log_next = log_buffer; + size_t l_head = bytes - bytes_begin; + + memcpy(log_buffer, bytes_begin, l_head); + log_next += l_head; + if ((log_next = picoquic_frames_varint_encode(log_next, log_buffer + 256, length)) != NULL) { + memcpy(log_next, bytes, extra_bytes); + log_next += extra_bytes; + picoquic_binlog_frame(f, log_buffer, log_next); + } + else { + picoquic_binlog_frame(f, log_buffer, log_buffer + l_head); + } + } + + bytes = picoquic_log_fixed_skip(bytes, bytes_max, length); + return bytes; +} + +static const uint8_t* picoquic_log_ack_frame(FILE* f, const uint8_t* bytes, const uint8_t* bytes_max) +{ + const uint8_t* bytes_begin = bytes; + uint8_t ftype = bytes[0]; + uint64_t nb_blocks; + + bytes = picoquic_log_fixed_skip(bytes, bytes_max, 1); + + bytes = picoquic_log_varint_skip(bytes, bytes_max); + bytes = picoquic_log_varint_skip(bytes, bytes_max); + bytes = picoquic_log_varint(bytes, bytes_max, &nb_blocks); + + for (uint64_t i = 0; bytes != NULL && i <= nb_blocks; i++) { + if (i != 0) { + bytes = picoquic_log_varint_skip(bytes, bytes_max); + } + bytes = picoquic_log_varint_skip(bytes, bytes_max); + } + + if (ftype == picoquic_frame_type_ack_ecn) { + bytes = picoquic_log_varint_skip(bytes, bytes_max); + bytes = picoquic_log_varint_skip(bytes, bytes_max); + bytes = picoquic_log_varint_skip(bytes, bytes_max); + } + + picoquic_binlog_frame(f, bytes_begin, bytes); + return bytes; +} + +static const uint8_t* picoquic_log_reset_stream_frame(FILE* f, const uint8_t* bytes, const uint8_t* bytes_max) +{ + const uint8_t * bytes_begin = bytes; + + bytes = picoquic_log_fixed_skip(bytes, bytes_max, 1); + bytes = picoquic_log_varint_skip(bytes, bytes_max); + bytes = picoquic_log_varint_skip(bytes, bytes_max); + bytes = picoquic_log_varint_skip(bytes, bytes_max); + + picoquic_binlog_frame(f, bytes_begin, bytes); + return bytes; +} + +static const uint8_t* picoquic_log_stop_sending_frame(FILE* f, const uint8_t* bytes, const uint8_t* bytes_max) +{ + const uint8_t* bytes_begin = bytes; + + bytes = picoquic_log_fixed_skip(bytes, bytes_max, 1); + bytes = picoquic_log_varint_skip(bytes, bytes_max); + bytes = picoquic_log_varint_skip(bytes, bytes_max); + + picoquic_binlog_frame(f, bytes_begin, bytes); + return bytes; +} + +static const uint8_t* picoquic_log_close_frame(FILE* f, const uint8_t* bytes, const uint8_t* bytes_max) +{ + const uint8_t* bytes_begin = bytes; + size_t length = 0; + + bytes = picoquic_log_fixed_skip(bytes, bytes_max, 1); + bytes = picoquic_log_varint_skip(bytes, bytes_max); + bytes = picoquic_log_varint_skip(bytes, bytes_max); + bytes = picoquic_log_length(bytes, bytes_max, &length); + bytes = picoquic_log_fixed_skip(bytes, bytes_max, length); + + picoquic_binlog_frame(f, bytes_begin, bytes); + return bytes; +} + +static const uint8_t* picoquic_log_app_close_frame(FILE* f, const uint8_t* bytes, const uint8_t* bytes_max) +{ + const uint8_t* bytes_begin = bytes; + size_t length = 0; + + bytes = picoquic_log_fixed_skip(bytes, bytes_max, 1); + bytes = picoquic_log_varint_skip(bytes, bytes_max); + bytes = picoquic_log_length(bytes, bytes_max, &length); + bytes = picoquic_log_fixed_skip(bytes, bytes_max, length); + + picoquic_binlog_frame(f, bytes_begin, bytes); + return bytes; +} + +static const uint8_t* picoquic_log_max_data_frame(FILE* f, const uint8_t* bytes, const uint8_t* bytes_max) +{ + const uint8_t* bytes_begin = bytes; + + bytes = picoquic_log_fixed_skip(bytes, bytes_max, 1); + bytes = picoquic_log_varint_skip(bytes, bytes_max); + + picoquic_binlog_frame(f, bytes_begin, bytes); + return bytes; +} + +static const uint8_t* picoquic_log_max_stream_data_frame(FILE* f, const uint8_t* bytes, const uint8_t* bytes_max) +{ + const uint8_t* bytes_begin = bytes; + + bytes = picoquic_log_fixed_skip(bytes, bytes_max, 1); + bytes = picoquic_log_varint_skip(bytes, bytes_max); + bytes = picoquic_log_varint_skip(bytes, bytes_max); + + picoquic_binlog_frame(f, bytes_begin, bytes); + return bytes; +} + +static const uint8_t* picoquic_log_max_stream_id_frame(FILE* f, const uint8_t* bytes, const uint8_t* bytes_max) +{ + const uint8_t* bytes_begin = bytes; + + bytes = picoquic_log_fixed_skip(bytes, bytes_max, 1); + bytes = picoquic_log_varint_skip(bytes, bytes_max); + + picoquic_binlog_frame(f, bytes_begin, bytes); + return bytes; +} + +static const uint8_t* picoquic_log_blocked_frame(FILE* f, const uint8_t* bytes, const uint8_t* bytes_max) +{ + const uint8_t* bytes_begin = bytes; + + bytes = picoquic_log_fixed_skip(bytes, bytes_max, 1); + bytes = picoquic_log_varint_skip(bytes, bytes_max); + + picoquic_binlog_frame(f, bytes_begin, bytes); + return bytes; +} + +static const uint8_t* picoquic_log_stream_blocked_frame(FILE* f, const uint8_t* bytes, const uint8_t* bytes_max) +{ + const uint8_t* bytes_begin = bytes; + + bytes = picoquic_log_fixed_skip(bytes, bytes_max, 1); + bytes = picoquic_log_varint_skip(bytes, bytes_max); + bytes = picoquic_log_varint_skip(bytes, bytes_max); + + picoquic_binlog_frame(f, bytes_begin, bytes); + return bytes; +} + +static const uint8_t* picoquic_log_streams_blocked_frame(FILE* f, const uint8_t* bytes, const uint8_t* bytes_max) +{ + const uint8_t* bytes_begin = bytes; + + bytes = picoquic_log_fixed_skip(bytes, bytes_max, 1); + bytes = picoquic_log_varint_skip(bytes, bytes_max); + + picoquic_binlog_frame(f, bytes_begin, bytes); + return bytes; +} + +static const uint8_t* picoquic_log_new_connection_id_frame(FILE* f, const uint8_t* bytes, const uint8_t* bytes_max) +{ + const uint8_t* bytes_begin = bytes; + + bytes = picoquic_log_fixed_skip(bytes, bytes_max, 1); + bytes = picoquic_log_varint_skip(bytes, bytes_max); + bytes = picoquic_log_varint_skip(bytes, bytes_max); + if (bytes != NULL) { + bytes = picoquic_log_fixed_skip(bytes, bytes_max, ((size_t)1) + bytes[0]); + } + + bytes = picoquic_log_fixed_skip(bytes, bytes_max, PICOQUIC_RESET_SECRET_SIZE); + + picoquic_binlog_frame(f, bytes_begin, bytes); + return bytes; +} + +static const uint8_t* picoquic_log_retire_connection_id_frame(FILE* f, const uint8_t* bytes, const uint8_t* bytes_max) +{ + const uint8_t* bytes_begin = bytes; + + bytes = picoquic_log_fixed_skip(bytes, bytes_max, 1); + bytes = picoquic_log_varint_skip(bytes, bytes_max); + + picoquic_binlog_frame(f, bytes_begin, bytes); + return bytes; +} + +static const uint8_t* picoquic_log_new_token_frame(FILE* f, const uint8_t* bytes, const uint8_t* bytes_max) +{ + const uint8_t* bytes_begin = bytes; + size_t length = 0; + + bytes = picoquic_log_fixed_skip(bytes, bytes_max, 1); + bytes = picoquic_log_length(bytes, bytes_max, &length); + + bytes = picoquic_log_fixed_skip(bytes, bytes_max, length); + + picoquic_binlog_frame(f, bytes_begin, bytes); + return bytes; +} + +static const uint8_t* picoquic_log_path_frame(FILE* f, const uint8_t* bytes, const uint8_t* bytes_max) +{ + const uint8_t* bytes_begin = bytes; + + bytes = picoquic_log_fixed_skip(bytes, bytes_max, 1 + 8); + + picoquic_binlog_frame(f, bytes_begin, bytes); + return bytes; +} + +static const uint8_t* picoquic_log_crypto_hs_frame(FILE* f, const uint8_t* bytes, const uint8_t* bytes_max) +{ + const uint8_t* bytes_begin = bytes; + size_t length = 0; + + bytes = picoquic_log_fixed_skip(bytes, bytes_max, 1); + bytes = picoquic_log_varint_skip(bytes, bytes_max); + bytes = picoquic_log_length(bytes, bytes_max, &length); + + picoquic_binlog_frame(f, bytes_begin, bytes); + + bytes = picoquic_log_fixed_skip(bytes, bytes_max, length); + return bytes; +} + + +static const uint8_t* picoquic_log_handshake_done_frame(FILE* f, const uint8_t* bytes, const uint8_t* bytes_max) +{ + const uint8_t* bytes_begin = bytes; + + bytes = picoquic_log_fixed_skip(bytes, bytes_max, 1); + + picoquic_binlog_frame(f, bytes_begin, bytes); + return bytes; +} + +static const uint8_t* picoquic_log_datagram_frame(FILE* f, const uint8_t* bytes, const uint8_t* bytes_max) +{ + const uint8_t* bytes_begin = bytes; + uint8_t ftype = bytes[0]; + size_t length = 0; + + bytes = picoquic_log_fixed_skip(bytes, bytes_max, 1); + + if (ftype & 1) { + bytes = picoquic_log_length(bytes, bytes_max, &length); + } else { + length = bytes_max - bytes; + } + + picoquic_binlog_frame(f, bytes_begin, bytes); + + bytes = picoquic_log_fixed_skip(bytes, bytes_max, length); + return bytes; +} + +static const uint8_t* picoquic_log_time_stamp_frame(FILE* f, const uint8_t* bytes, const uint8_t* bytes_max) +{ + const uint8_t* bytes_begin = bytes; + + bytes = picoquic_log_varint_skip(bytes, bytes_max); /* frame type as varint */ + bytes = picoquic_log_varint_skip(bytes, bytes_max); /* time stamp as varint */ + + picoquic_binlog_frame(f, bytes_begin, bytes); + + return bytes; +} + +static const uint8_t* picoquic_log_ack_frequency_frame(FILE* f, const uint8_t* bytes, const uint8_t* bytes_max) +{ + const uint8_t* bytes_begin = bytes; + + bytes = picoquic_log_varint_skip(bytes, bytes_max); /* frame type as varint */ + bytes = picoquic_log_varint_skip(bytes, bytes_max); /* Seq num */ + bytes = picoquic_log_varint_skip(bytes, bytes_max); /* Packet tolerance */ + bytes = picoquic_log_varint_skip(bytes, bytes_max); /* Max ACK delay */ + + picoquic_binlog_frame(f, bytes_begin, bytes); + + return bytes; +} + +static const uint8_t* picoquic_log_erroring_frame(FILE* f, const uint8_t* bytes, const uint8_t* bytes_max) +{ + size_t frame_size = bytes_max - bytes; + size_t copied = (frame_size > 8) ? 8 : frame_size; + + picoquic_binlog_frame(f, bytes, bytes + copied); + + return NULL; +} + +static const uint8_t* picoquic_log_padding(FILE* f, const uint8_t* bytes, const uint8_t* bytes_max) +{ + picoquic_binlog_frame(f, bytes, bytes + 1); + + uint8_t ftype = bytes[0]; + while (bytes < bytes_max && bytes[0] == ftype) { + bytes++; + } + + return bytes; +} + +void picoquic_binlog_frames(FILE * f, const uint8_t* bytes, size_t length) +{ + const uint8_t* bytes_max = bytes + length; + + while (bytes != NULL && bytes < bytes_max) { + uint64_t ftype= 0; + size_t ftype_ll = picoquic_varint_decode(bytes, length, &ftype); + + if (ftype_ll == 0) { + /* Error, incorrect frame type encoding */ + bytes = NULL; + break; + } + else if (ftype < 64 && ftype_ll != 1) { + /* Error, incorrect frame type encoding */ + bytes = NULL; + break; + } + + if (PICOQUIC_IN_RANGE(ftype, picoquic_frame_type_stream_range_min, picoquic_frame_type_stream_range_max)) { + bytes = picoquic_log_stream_frame(f, bytes, bytes_max); + continue; + } + + switch (ftype) { + case picoquic_frame_type_ack: + case picoquic_frame_type_ack_ecn: + bytes = picoquic_log_ack_frame(f, bytes, bytes_max); + break; + case picoquic_frame_type_retire_connection_id: + bytes = picoquic_log_retire_connection_id_frame(f, bytes, bytes_max); + break; + case picoquic_frame_type_padding: + case picoquic_frame_type_ping: + bytes = picoquic_log_padding(f, bytes, bytes_max); + break; + case picoquic_frame_type_reset_stream: + bytes = picoquic_log_reset_stream_frame(f, bytes, bytes_max); + break; + case picoquic_frame_type_connection_close: + bytes = picoquic_log_close_frame(f, bytes, bytes_max); + break; + case picoquic_frame_type_application_close: + bytes = picoquic_log_app_close_frame(f, bytes, bytes_max); + break; + case picoquic_frame_type_max_data: + bytes = picoquic_log_max_data_frame(f, bytes, bytes_max); + break; + case picoquic_frame_type_max_stream_data: + bytes = picoquic_log_max_stream_data_frame(f, bytes, bytes_max); + break; + case picoquic_frame_type_max_streams_bidir: + case picoquic_frame_type_max_streams_unidir: + bytes = picoquic_log_max_stream_id_frame(f, bytes, bytes_max); + break; + case picoquic_frame_type_data_blocked: + bytes = picoquic_log_blocked_frame(f, bytes, bytes_max); + break; + case picoquic_frame_type_stream_data_blocked: + bytes = picoquic_log_stream_blocked_frame(f, bytes, bytes_max); + break; + case picoquic_frame_type_streams_blocked_bidir: + case picoquic_frame_type_streams_blocked_unidir: + bytes = picoquic_log_streams_blocked_frame(f, bytes, bytes_max); + break; + case picoquic_frame_type_new_connection_id: + bytes = picoquic_log_new_connection_id_frame(f, bytes, bytes_max); + break; + case picoquic_frame_type_stop_sending: + bytes = picoquic_log_stop_sending_frame(f, bytes, bytes_max); + break; + case picoquic_frame_type_path_challenge: + case picoquic_frame_type_path_response: + bytes = picoquic_log_path_frame(f, bytes, bytes_max); + break; + case picoquic_frame_type_crypto_hs: + bytes = picoquic_log_crypto_hs_frame(f, bytes, bytes_max); + break; + case picoquic_frame_type_new_token: + bytes = picoquic_log_new_token_frame(f, bytes, bytes_max); + break; + case picoquic_frame_type_handshake_done: + bytes = picoquic_log_handshake_done_frame(f, bytes, bytes_max); + break; + case picoquic_frame_type_datagram: + case picoquic_frame_type_datagram_l: + bytes = picoquic_log_datagram_frame(f, bytes, bytes_max); + break; + case picoquic_frame_type_ack_frequency: + bytes = picoquic_log_ack_frequency_frame(f, bytes, bytes_max); + break; + case picoquic_frame_type_time_stamp: + bytes = picoquic_log_time_stamp_frame(f, bytes, bytes_max); + break; + default: + bytes = picoquic_log_erroring_frame(f, bytes, bytes_max); + break; + } + } +} + +void binlog_pdu(FILE* f, const picoquic_connection_id_t* cid, int receiving, uint64_t current_time, + const struct sockaddr* addr_peer, const struct sockaddr* addr_local, size_t packet_length) +{ + bytestream_buf stream_msg; + bytestream* msg = bytestream_buf_init(&stream_msg, BYTESTREAM_MAX_BUFFER_SIZE); + + /* Common chunk header */ + bytewrite_cid(msg, cid); + bytewrite_vint(msg, current_time); + bytewrite_vint(msg, ((uint64_t)picoquic_log_event_pdu_sent) + receiving); + + /* PDU information */ + bytewrite_addr(msg, addr_peer); + bytewrite_vint(msg, packet_length); + bytewrite_addr(msg, addr_local); + + uint8_t head[4] = { 0 }; + picoformat_32(head, (uint32_t)bytestream_length(msg)); + + (void)fwrite(head, sizeof(head), 1, f); + (void)fwrite(bytestream_data(msg), bytestream_length(msg), 1, f); +} + +static void binlog_pdu_ex(picoquic_cnx_t* cnx, int receiving, uint64_t current_time, + const struct sockaddr* addr_peer, const struct sockaddr* addr_local, size_t packet_length) +{ + if (cnx != NULL && cnx->f_binlog != NULL && picoquic_cnx_is_still_logging(cnx)) { + binlog_pdu(cnx->f_binlog, &cnx->initial_cnxid, receiving, current_time, addr_peer, addr_local, packet_length); + } +} + +void binlog_packet(FILE* f, const picoquic_connection_id_t* cid, int receiving, uint64_t current_time, + const picoquic_packet_header* ph, const uint8_t* bytes, size_t bytes_max) +{ + long fpos0 = ftell(f); + + uint8_t head[4] = { 0 }; + (void)fwrite(head, 4, 1, f); + + bytestream_buf stream_msg; + bytestream* msg = bytestream_buf_init(&stream_msg, BYTESTREAM_MAX_BUFFER_SIZE); + + /* Common chunk header */ + bytewrite_cid(msg, cid); + bytewrite_vint(msg, current_time); + bytewrite_vint(msg, ((uint64_t)picoquic_log_event_packet_sent) + receiving); + + /* packet information */ + bytewrite_vint(msg, bytes_max); + + /* packet header */ + bytewrite_int8(msg, (uint8_t)(64*ph->quic_bit_is_zero + 2 * ph->spin + ph->key_phase)); + bytewrite_vint(msg, ph->payload_length); + bytewrite_vint(msg, ph->ptype); + bytewrite_vint(msg, ph->pn64); + + bytewrite_cid(msg, &ph->dest_cnx_id); + bytewrite_cid(msg, &ph->srce_cnx_id); + + if (ph->ptype != picoquic_packet_1rtt_protected && + ph->ptype != picoquic_packet_version_negotiation) { + bytewrite_int32(msg, ph->vn); + } + + if (ph->ptype == picoquic_packet_initial) { + bytewrite_vint(msg, ph->token_length); + bytewrite_buffer(msg, ph->token_bytes, ph->token_length); + } + + (void)fwrite(bytestream_data(msg), bytestream_length(msg), 1, f); + + /* frame information */ + if (ph->ptype == picoquic_packet_version_negotiation || ph->ptype == picoquic_packet_retry) { + picoquic_binlog_frame(f, bytes + ph->offset, bytes + bytes_max); + } + else if (ph->ptype != picoquic_packet_error) { + picoquic_binlog_frames(f, bytes + ph->offset, ph->payload_length); + } + + /* re-write chunk size field */ + long fpos1 = ftell(f); + + picoformat_32(head, (uint32_t)(fpos1 - fpos0 - 4)); + + (void)fseek(f, fpos0, SEEK_SET); + (void)fwrite(head, 4, 1, f); + (void)fseek(f, 0, SEEK_END); +} + +static void binlog_packet_ex(picoquic_cnx_t* cnx, int receiving, uint64_t current_time, + picoquic_packet_header* ph, const uint8_t* bytes, size_t bytes_max) +{ + if (cnx != NULL && cnx->f_binlog != NULL && picoquic_cnx_is_still_logging(cnx)) { + binlog_packet(cnx->f_binlog, &cnx->initial_cnxid, receiving, current_time, ph, bytes, bytes_max); + } +} + +void binlog_dropped_packet(picoquic_cnx_t* cnx, + picoquic_packet_header* ph, size_t packet_size, int err, + uint8_t * raw_data, uint64_t current_time) +{ + FILE* f = cnx->f_binlog; + size_t raw_size = packet_size; + bytestream_buf stream_msg; + bytestream* msg = bytestream_buf_init(&stream_msg, BYTESTREAM_MAX_BUFFER_SIZE); + + if (err == PICOQUIC_ERROR_AEAD_CHECK) { + /* Do not log on decryption error, because the buffer was randomized by decryption */ + raw_size = 0; + } else if (raw_size > 32) { + raw_size = 32; + } + + bytewrite_int32(msg, 0); + bytewrite_cid(msg, &cnx->initial_cnxid); + bytewrite_vint(msg, current_time); + bytewrite_vint(msg, picoquic_log_event_packet_dropped); + bytewrite_vint(msg, ph->ptype); + bytewrite_vint(msg, packet_size); + bytewrite_vint(msg, err); + bytewrite_vint(msg, raw_size); + (void)bytewrite_buffer(msg, raw_data, raw_size); + + /* write the frame length at the reserved spot, and save to log file*/ + picoformat_32(msg->data, (uint32_t)(msg->ptr - 4)); + (void)fwrite(bytestream_data(msg), bytestream_length(msg), 1, f); +} + +void binlog_buffered_packet(picoquic_cnx_t* cnx, + picoquic_packet_type_enum ptype, uint64_t current_time) +{ + FILE* f = cnx->f_binlog; + bytestream_buf stream_msg; + bytestream* msg = bytestream_buf_init(&stream_msg, BYTESTREAM_MAX_BUFFER_SIZE); + + bytewrite_int32(msg, 0); + bytewrite_cid(msg, &cnx->initial_cnxid); + bytewrite_vint(msg, current_time); + bytewrite_vint(msg, picoquic_log_event_packet_buffered); + bytewrite_vint(msg, ptype); + (void)bytewrite_cstr(msg, "keys_unavailable"); + + /* write the frame length at the reserved spot, and save to log file*/ + picoformat_32(msg->data, (uint32_t)(msg->ptr - 4)); + (void)fwrite(bytestream_data(msg), bytestream_length(msg), 1, f); +} + + +void binlog_outgoing_packet(picoquic_cnx_t* cnx, + uint8_t * bytes, uint64_t sequence_number, size_t pn_length, size_t length, + uint8_t* send_buffer, size_t send_length, uint64_t current_time) +{ + FILE* f = cnx->f_binlog; + + picoquic_cnx_t* pcnx = cnx; + picoquic_packet_header ph; + size_t checksum_length = 16; + struct sockaddr_in default_addr; + + const picoquic_connection_id_t * cnxid = (cnx != NULL) ? &cnx->initial_cnxid : &picoquic_null_connection_id; + + memset(&default_addr, 0, sizeof(struct sockaddr_in)); + default_addr.sin_family = AF_INET; + + picoquic_parse_packet_header((cnx == NULL) ? NULL : cnx->quic, send_buffer, send_length, + ((cnx == NULL || cnx->path[0] == NULL) ? (struct sockaddr *)&default_addr : + (struct sockaddr *)&cnx->path[0]->local_addr), &ph, &pcnx, 0); + + if (cnx != NULL) { + picoquic_epoch_enum epoch = (ph.ptype == picoquic_packet_1rtt_protected) ? picoquic_epoch_1rtt : + ((ph.ptype == picoquic_packet_0rtt_protected) ? picoquic_epoch_0rtt : + ((ph.ptype == picoquic_packet_handshake) ? picoquic_epoch_handshake : picoquic_epoch_initial)); + if (cnx->crypto_context[epoch].aead_encrypt != NULL) { + checksum_length = picoquic_get_checksum_length(cnx, epoch); + } + } + + ph.pn64 = sequence_number; + ph.pn = (uint32_t)ph.pn64; + if (ph.ptype != picoquic_packet_retry) { + if (ph.pn_offset != 0) { + ph.offset = ph.pn_offset + pn_length; + ph.payload_length -= pn_length; + } + } + if (ph.ptype != picoquic_packet_version_negotiation) { + if (ph.payload_length > checksum_length) { + ph.payload_length -= (uint16_t)checksum_length; + } + else { + ph.payload_length = 0; + } + } + + binlog_packet(f, cnxid, 0, current_time, &ph, bytes, length); +} + +void binlog_packet_lost(picoquic_cnx_t* cnx, + picoquic_packet_type_enum ptype, uint64_t sequence_number, char const * trigger, + picoquic_connection_id_t * dcid, size_t packet_size, + uint64_t current_time) +{ + FILE* f = cnx->f_binlog; + + bytestream_buf stream_msg; + bytestream* msg = bytestream_buf_init(&stream_msg, BYTESTREAM_MAX_BUFFER_SIZE); + + bytewrite_int32(msg, 0); + bytewrite_cid(msg, &cnx->initial_cnxid); + bytewrite_vint(msg, current_time); + bytewrite_vint(msg, picoquic_log_event_packet_lost); + + bytewrite_vint(msg, ptype); + bytewrite_vint(msg, sequence_number); + bytewrite_cstr(msg, trigger); + if (dcid != NULL) { + bytewrite_cid(msg, dcid); + } + else { + bytewrite_int8(msg, 0); + } + bytewrite_vint(msg, packet_size); + + /* write the frame length at the reserved spot, and save to log file*/ + picoformat_32(msg->data, (uint32_t)(msg->ptr - 4)); + (void)fwrite(bytestream_data(msg), bytestream_length(msg), 1, f); +} + + +void binlog_negotiated_alpn(picoquic_cnx_t* cnx, int is_local, + uint8_t const * sni, size_t sni_len, uint8_t const* alpn, size_t alpn_len, + const ptls_iovec_t* alpn_list, size_t alpn_count) +{ + FILE* f = cnx->f_binlog; + + bytestream_buf stream_msg; + bytestream* msg = bytestream_buf_init(&stream_msg, BYTESTREAM_MAX_BUFFER_SIZE); + bytewrite_cid(msg, &cnx->initial_cnxid); + bytewrite_vint(msg, picoquic_get_quic_time(cnx->quic)); + bytewrite_vint(msg, picoquic_log_event_alpn_update); + + bytewrite_vint(msg, is_local); + bytewrite_vint(msg, sni_len); + if (sni_len > 0) { + bytewrite_buffer(msg, sni, sni_len); + } + + bytewrite_vint(msg, alpn_count); + if (alpn_count > 0) { + for (size_t i = 0; i < alpn_count; i++) { + bytewrite_vint(msg, alpn_list[i].len); + bytewrite_buffer(msg, alpn_list[i].base, alpn_list[i].len); + } + } + + bytewrite_vint(msg, alpn_len); + if (alpn_len > 0) { + bytewrite_buffer(msg, alpn, alpn_len); + } + + bytestream_buf stream_head; + bytestream* head = bytestream_buf_init(&stream_head, 4); + bytewrite_int32(head, (uint32_t)bytestream_length(msg)); + + (void)fwrite(bytestream_data(head), bytestream_length(head), 1, f); + (void)fwrite(bytestream_data(msg), bytestream_length(msg), 1, f); +} + +void binlog_transport_extension(picoquic_cnx_t* cnx, int is_local, + size_t param_length, uint8_t* params) +{ + FILE* f = cnx->f_binlog; + + bytestream_buf stream_msg; + bytestream* msg = bytestream_buf_init(&stream_msg, BYTESTREAM_MAX_BUFFER_SIZE); + bytewrite_cid(msg, &cnx->initial_cnxid); + bytewrite_vint(msg, picoquic_get_quic_time(cnx->quic)); + bytewrite_vint(msg, picoquic_log_event_param_update); + + bytewrite_vint(msg, is_local); + bytewrite_vint(msg, param_length); + + if (param_length > 0) { + bytewrite_buffer(msg, params, param_length); + } + + bytestream_buf stream_head; + bytestream* head = bytestream_buf_init(&stream_head, 4); + bytewrite_int32(head, (uint32_t)bytestream_length(msg)); + + (void)fwrite(bytestream_data(head), bytestream_length(head), 1, f); + (void)fwrite(bytestream_data(msg), bytestream_length(msg), 1, f); +} + +void binlog_picotls_ticket(FILE* f, picoquic_connection_id_t cnx_id, + uint8_t* ticket, uint16_t ticket_length) +{ + bytestream_buf stream_msg; + bytestream * msg = bytestream_buf_init(&stream_msg, BYTESTREAM_MAX_BUFFER_SIZE); + bytewrite_cid(msg, &cnx_id); + bytewrite_vint(msg, 0); + bytewrite_vint(msg, picoquic_log_event_tls_key_update); + + bytewrite_vint(msg, ticket_length); + bytewrite_buffer(msg, ticket, ticket_length); + + bytestream_buf stream_head; + bytestream * head = bytestream_buf_init(&stream_head, 8); + bytewrite_int32(head, (uint32_t)bytestream_length(msg)); + + (void)fwrite(bytestream_data(head), bytestream_length(head), 1, f); + (void)fwrite(bytestream_data(msg), bytestream_length(msg), 1, f); +} + +static void binlog_picotls_ticket_ex(picoquic_cnx_t* cnx, + uint8_t* ticket, uint16_t ticket_length) +{ + if (cnx != NULL && cnx->f_binlog != NULL && picoquic_cnx_is_still_logging(cnx)) { + binlog_picotls_ticket(cnx->f_binlog, cnx->initial_cnxid, ticket, ticket_length); + } +} + +FILE* create_binlog(char const* binlog_file, uint64_t creation_time); + +void binlog_new_connection(picoquic_cnx_t * cnx) +{ + char const* bin_dir = (cnx->quic->binlog_dir == NULL) ? cnx->quic->qlog_dir : cnx->quic->binlog_dir; + + if (bin_dir == NULL) { + return; + } + + if (cnx->quic->current_number_of_open_logs >= cnx->quic->max_simultaneous_logs) { + return; + } + + int ret = 0; + + cnx->f_binlog = picoquic_file_close(cnx->f_binlog); + + char cid_name[2 * PICOQUIC_CONNECTION_ID_MAX_SIZE + 1]; + if (picoquic_print_connection_id_hexa(cid_name, sizeof(cid_name), &cnx->initial_cnxid) != 0) { + ret = -1; + } + + char log_filename[512]; + if (ret == 0) { + if (picoquic_sprintf(log_filename, sizeof(log_filename), NULL, "%s%s%s.%s.log", + bin_dir, PICOQUIC_FILE_SEPARATOR, cid_name, + (cnx->client_mode)?"client":"server") != 0) { + ret = -1; + } + else { + picoquic_string_free(cnx->binlog_file_name); + cnx->binlog_file_name = picoquic_string_duplicate(log_filename); + } + } + + if (ret == 0) { + cnx->f_binlog = create_binlog(log_filename, picoquic_get_quic_time(cnx->quic)); + if (cnx->f_binlog == NULL) { + cnx->binlog_file_name = picoquic_string_free(cnx->binlog_file_name); + ret = -1; + } + else { + cnx->quic->current_number_of_open_logs++; + } + } + + if (ret == 0) { + bytestream_buf stream_msg; + bytestream * msg = bytestream_buf_init(&stream_msg, BYTESTREAM_MAX_BUFFER_SIZE); + bytewrite_cid(msg, &cnx->initial_cnxid); + bytewrite_vint(msg, cnx->start_time); + bytewrite_vint(msg, picoquic_log_event_new_connection); + + bytewrite_int8(msg, cnx->client_mode != 0); + bytewrite_int32(msg, cnx->proposed_version); + bytewrite_cid(msg, &cnx->path[0]->remote_cnxid); + + /* Algorithms used */ + bytewrite_cstr(msg, cnx->congestion_alg->congestion_algorithm_id); + bytewrite_vint(msg, cnx->spin_policy); + + bytestream_buf stream_head; + bytestream * head = bytestream_buf_init(&stream_head, 8); + bytewrite_int32(head, (uint32_t)bytestream_length(msg)); + + (void)fwrite(bytestream_data(head), bytestream_length(head), 1, cnx->f_binlog); + (void)fwrite(bytestream_data(msg), bytestream_length(msg), 1, cnx->f_binlog); + } +} + +void binlog_close_connection(picoquic_cnx_t * cnx) +{ + FILE * f = cnx->f_binlog; + if (f == NULL) { + return; + } + + bytestream_buf stream_msg; + bytestream * msg = bytestream_buf_init(&stream_msg, BYTESTREAM_MAX_BUFFER_SIZE); + bytewrite_cid(msg, &cnx->initial_cnxid); + bytewrite_vint(msg, picoquic_get_quic_time(cnx->quic)); + bytewrite_vint(msg, picoquic_log_event_connection_close); + + bytestream_buf stream_head; + bytestream * head = bytestream_buf_init(&stream_head, 8); + bytewrite_int32(head, (uint32_t)bytestream_length(msg)); + + (void)fwrite(bytestream_data(head), bytestream_length(head), 1, f); + (void)fwrite(bytestream_data(msg), bytestream_length(msg), 1, f); + + fflush(f); + + cnx->f_binlog = picoquic_file_close(cnx->f_binlog); + + if (cnx->quic->qlog_dir != NULL && cnx->quic->autoqlog_fn != NULL) { + (void)cnx->quic->autoqlog_fn(cnx); + } + cnx->binlog_file_name = picoquic_string_free(cnx->binlog_file_name); + if (cnx->quic->current_number_of_open_logs > 0) { + cnx->quic->current_number_of_open_logs--; + } +} + +FILE* create_binlog(char const* binlog_file, uint64_t creation_time) +{ + FILE* f_binlog = picoquic_file_open(binlog_file, "wb"); + if (f_binlog == NULL) { + DBG_PRINTF("Cannot open file %s for write.\n", binlog_file); + } + else { + /* Write a header text with version identifier and current date */ + bytestream_buf stream; + bytestream* ps = bytestream_buf_init(&stream, 16); + bytewrite_int32(ps, FOURCC('q', 'l', 'o', 'g')); + bytewrite_int32(ps, 0x01); + bytewrite_int64(ps, creation_time); + + if (fwrite(bytestream_data(ps), bytestream_length(ps), 1, f_binlog) <= 0) { + DBG_PRINTF("Cannot write header for file %s.\n", binlog_file); + f_binlog = picoquic_file_close(f_binlog); + } + } + + return f_binlog; +} + +/* + * Log the state of the congestion management, retransmission, etc. + * Call either just after processing a received packet, or just after + * sending a packet. + */ + +void binlog_cc_dump(picoquic_cnx_t* cnx, uint64_t current_time) +{ + if (cnx->f_binlog == NULL) { + return; + } + + bytestream_buf stream_msg; + bytestream* ps_msg = bytestream_buf_init(&stream_msg, BYTESTREAM_MAX_BUFFER_SIZE); + picoquic_packet_context_t* pkt_ctx = &cnx->pkt_ctx[picoquic_packet_context_application]; + picoquic_path_t* path = cnx->path[0]; + + bytewrite_cid(ps_msg, &cnx->initial_cnxid); + bytewrite_vint(ps_msg, current_time); + bytewrite_vint(ps_msg, picoquic_log_event_cc_update); + + bytewrite_vint(ps_msg, cnx->pkt_ctx[picoquic_packet_context_application].send_sequence); + + if (pkt_ctx->highest_acknowledged != (uint64_t)(int64_t)-1) { + bytewrite_vint(ps_msg, 1); + bytewrite_vint(ps_msg, pkt_ctx->highest_acknowledged); + bytewrite_vint(ps_msg, pkt_ctx->highest_acknowledged_time - cnx->start_time); + bytewrite_vint(ps_msg, pkt_ctx->latest_time_acknowledged - cnx->start_time); + } + else { + bytewrite_vint(ps_msg, 0); + } + + bytewrite_vint(ps_msg, path->cwin); + bytewrite_vint(ps_msg, path->one_way_delay_sample); + bytewrite_vint(ps_msg, path->rtt_sample); + bytewrite_vint(ps_msg, path->smoothed_rtt); + bytewrite_vint(ps_msg, path->rtt_min); + bytewrite_vint(ps_msg, path->bandwidth_estimate); + bytewrite_vint(ps_msg, path->receive_rate_estimate); + bytewrite_vint(ps_msg, path->send_mtu); + bytewrite_vint(ps_msg, path->pacing_packet_time_microsec); + bytewrite_vint(ps_msg, cnx->nb_retransmission_total); + bytewrite_vint(ps_msg, cnx->nb_spurious); + bytewrite_vint(ps_msg, cnx->cwin_blocked); + bytewrite_vint(ps_msg, cnx->flow_blocked); + bytewrite_vint(ps_msg, cnx->stream_blocked); + + if (cnx->congestion_alg == NULL) { + bytewrite_vint(ps_msg, 0); + bytewrite_vint(ps_msg, 0); + } + else { + uint64_t cc_state; + uint64_t cc_param; + + cnx->congestion_alg->alg_observe(cnx->path[0], &cc_state, &cc_param); + bytewrite_vint(ps_msg, cc_state); + bytewrite_vint(ps_msg, cc_param); + } + + bytewrite_vint(ps_msg, path->max_bandwidth_estimate); + bytewrite_vint(ps_msg, path->bytes_in_transit); + + bytestream_buf stream_head; + bytestream* ps_head = bytestream_buf_init(&stream_head, BYTESTREAM_MAX_BUFFER_SIZE); + + bytewrite_int32(ps_head, (uint32_t)bytestream_length(ps_msg)); + + (void)fwrite(bytestream_data(ps_head), bytestream_length(ps_head), 1, cnx->f_binlog); + (void)fwrite(bytestream_data(ps_msg), bytestream_length(ps_msg), 1, cnx->f_binlog); +} + +/* + * Write an information message frame, for free form debugging. + */ + +void picoquic_binlog_message_v(picoquic_cnx_t* cnx, const char* fmt, va_list vargs) +{ + if (cnx->f_binlog == NULL) { + return; + } + bytestream_buf stream_msg; + bytestream* ps_msg = bytestream_buf_init(&stream_msg, BYTESTREAM_MAX_BUFFER_SIZE); + size_t message_len; + char* message_text; + int written = -1; + + bytewrite_cid(ps_msg, &cnx->initial_cnxid); + bytewrite_vint(ps_msg, picoquic_get_quic_time(cnx->quic)); + bytewrite_vint(ps_msg, picoquic_log_event_info_message); + message_text = (char*)(ps_msg->data + ps_msg->ptr); +#ifdef _WINDOWS + written = vsnprintf_s(message_text, + ps_msg->size - ps_msg->ptr, _TRUNCATE, fmt, vargs); + message_len = (written < 0) ? ps_msg->size - ps_msg->ptr - 1 : written; +#else + written = vsnprintf(message_text, ps_msg->size - ps_msg->ptr, fmt, vargs); + if (written < 0 || written >= ps_msg->size - ps_msg->ptr){ + message_len = ps_msg->size - ps_msg->ptr - 1; + } else { + message_len = written; + } +#endif + ps_msg->ptr += message_len; + + bytestream_buf stream_head; + bytestream* ps_head = bytestream_buf_init(&stream_head, BYTESTREAM_MAX_BUFFER_SIZE); + + bytewrite_int32(ps_head, (uint32_t)bytestream_length(ps_msg)); + + (void)fwrite(bytestream_data(ps_head), bytestream_length(ps_head), 1, cnx->f_binlog); + (void)fwrite(bytestream_data(ps_msg), bytestream_length(ps_msg), 1, cnx->f_binlog); +} + +/* Log an event that cannot be attached to a specific connection */ +void binlog_ignore_quic_app_message(picoquic_quic_t* quic, const picoquic_connection_id_t* cid, const char* fmt, va_list vargs) +{ +#ifdef _WINDOWS + UNREFERENCED_PARAMETER(quic); + UNREFERENCED_PARAMETER(cid); + UNREFERENCED_PARAMETER(fmt); +#endif +} + +/* Log arrival or departure of an UDP datagram for an unknown connection */ +void binlog_ignore_quic_pdu(picoquic_quic_t* quic, int receiving, uint64_t current_time, uint64_t cid64, + const struct sockaddr* addr_peer, const struct sockaddr* addr_local, size_t packet_length) +{ +#ifdef _WINDOWS + UNREFERENCED_PARAMETER(quic); + UNREFERENCED_PARAMETER(receiving); + UNREFERENCED_PARAMETER(current_time); + UNREFERENCED_PARAMETER(addr_peer); + UNREFERENCED_PARAMETER(addr_local); + UNREFERENCED_PARAMETER(packet_length); +#endif +} + +/* Log an event relating to a specific connection */ +static void binlog_app_message(picoquic_cnx_t* cnx, const char* fmt, va_list vargs) +{ + if (cnx->f_binlog != NULL) { + picoquic_binlog_message_v(cnx, fmt, vargs); + } +} + +struct st_picoquic_unified_logging_t binlog_functions = { + /* Per context log function */ + binlog_ignore_quic_app_message, + binlog_ignore_quic_pdu, + /* Per connection functions */ + binlog_app_message, + binlog_pdu_ex, + binlog_packet_ex, + binlog_dropped_packet, + binlog_buffered_packet, + binlog_outgoing_packet, + binlog_packet_lost, + binlog_negotiated_alpn, + binlog_transport_extension, + binlog_picotls_ticket_ex, + binlog_new_connection, + binlog_close_connection, + binlog_cc_dump +}; + +int picoquic_set_binlog(picoquic_quic_t* quic, char const* binlog_dir) +{ + quic->binlog_dir = picoquic_string_free(quic->binlog_dir); + quic->binlog_dir = picoquic_string_duplicate(binlog_dir); + quic->bin_log_fns = &binlog_functions; + return 0; +} + +void picoquic_enable_binlog(picoquic_quic_t* quic) +{ + quic->bin_log_fns = &binlog_functions; +} diff --git a/deps/picoquic/newreno.c b/deps/picoquic/newreno.c new file mode 100644 index 0000000..9689110 --- /dev/null +++ b/deps/picoquic/newreno.c @@ -0,0 +1,298 @@ +/* +* Author: Christian Huitema +* Copyright (c) 2017, Private Octopus, Inc. +* All rights reserved. +* +* Permission to use, copy, modify, and distribute this software for any +* purpose with or without fee is hereby granted, provided that the above +* copyright notice and this permission notice appear in all copies. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL Private Octopus, Inc. BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "picoquic_internal.h" +#include +#include +#include "cc_common.h" + +#define NB_RTT_RENO 4 + +/* Many congestion control algorithms run a parallel version of new reno in order + * to provide a lower bound estimate of either the congestion window or the + * the minimal bandwidth. This implementation of new reno does not directly + * refer to the connection and path variables (e.g. cwin) but instead sets + * its entire state in memory. + */ + +void picoquic_newreno_sim_reset(picoquic_newreno_sim_state_t * nrss) +{ + /* Initialize the state of the congestion control algorithm */ + memset(nrss, 0, sizeof(picoquic_newreno_sim_state_t)); + nrss->alg_state = picoquic_newreno_alg_slow_start; + nrss->ssthresh = UINT64_MAX; + nrss->cwin = PICOQUIC_CWIN_INITIAL; +} + +/* The recovery state last 1 RTT, during which parameters will be frozen + */ +static void picoquic_newreno_sim_enter_recovery( + picoquic_newreno_sim_state_t* nr_state, + picoquic_cnx_t* cnx, + picoquic_congestion_notification_t notification, + uint64_t current_time) +{ + nr_state->ssthresh = nr_state->cwin / 2; + if (nr_state->ssthresh < PICOQUIC_CWIN_MINIMUM) { + nr_state->ssthresh = PICOQUIC_CWIN_MINIMUM; + } + + if (notification == picoquic_congestion_notification_timeout) { + nr_state->cwin = PICOQUIC_CWIN_MINIMUM; + nr_state->alg_state = picoquic_newreno_alg_slow_start; + } + else { + nr_state->cwin = nr_state->ssthresh; + nr_state->alg_state = picoquic_newreno_alg_congestion_avoidance; + } + + nr_state->recovery_start = current_time; + nr_state->recovery_sequence = picoquic_cc_get_sequence_number(cnx); + + nr_state->residual_ack = 0; +} + +/* Notification API for new Reno simulations. + */ +void picoquic_newreno_sim_notify( + picoquic_newreno_sim_state_t* nr_state, + picoquic_cnx_t* cnx, + picoquic_path_t* path_x, + picoquic_congestion_notification_t notification, + uint64_t nb_bytes_acknowledged, + uint64_t current_time) +{ + switch (notification) { + case picoquic_congestion_notification_acknowledgement: { + switch (nr_state->alg_state) { + case picoquic_newreno_alg_slow_start: + nr_state->cwin += nb_bytes_acknowledged; + /* if cnx->cwin exceeds SSTHRESH, exit and go to CA */ + if (nr_state->cwin >= nr_state->ssthresh) { + nr_state->alg_state = picoquic_newreno_alg_congestion_avoidance; + } + break; + case picoquic_newreno_alg_congestion_avoidance: + default: { + uint64_t complete_delta = nb_bytes_acknowledged * path_x->send_mtu + nr_state->residual_ack; + nr_state->residual_ack = complete_delta % nr_state->cwin; + nr_state->cwin += complete_delta / nr_state->cwin; + break; + } + } + break; + } + case picoquic_congestion_notification_ecn_ec: + case picoquic_congestion_notification_repeat: + case picoquic_congestion_notification_timeout: + /* enter recovery */ + if (current_time - nr_state->recovery_start > path_x->smoothed_rtt || + nr_state->recovery_sequence <= picoquic_cc_get_ack_number(cnx)) { + picoquic_newreno_sim_enter_recovery(nr_state, cnx, notification, current_time); + } + break; + case picoquic_congestion_notification_spurious_repeat: + if (current_time - nr_state->recovery_start < path_x->smoothed_rtt && + nr_state->recovery_sequence > picoquic_cc_get_ack_number(cnx)) { + /* If spurious repeat of initial loss detected, + * exit recovery and reset threshold to pre-entry cwin. + */ + if (nr_state->ssthresh != UINT64_MAX && + path_x->cwin < 2 * nr_state->ssthresh) { + path_x->cwin = 2 * nr_state->ssthresh; + nr_state->alg_state = picoquic_newreno_alg_congestion_avoidance; + } + } + break; + case picoquic_congestion_notification_bw_measurement: + break; + case picoquic_congestion_notification_reset: + picoquic_newreno_sim_reset(nr_state); + break; + default: + /* ignore */ + break; + } +} + + +/* Actual implementation of New Reno, when used as a stand alone algorithm + */ + +typedef struct st_picoquic_newreno_state_t { + picoquic_newreno_sim_state_t nrss; + picoquic_min_max_rtt_t rtt_filter; +} picoquic_newreno_state_t; + +static void picoquic_newreno_reset(picoquic_newreno_state_t* nr_state, picoquic_path_t* path_x) +{ + memset(nr_state, 0, sizeof(picoquic_newreno_state_t)); + picoquic_newreno_sim_reset(&nr_state->nrss); + path_x->cwin = nr_state->nrss.cwin; +} + +static void picoquic_newreno_init(picoquic_path_t* path_x, uint64_t current_time) +{ + /* Initialize the state of the congestion control algorithm */ + picoquic_newreno_state_t* nr_state = (picoquic_newreno_state_t*)malloc(sizeof(picoquic_newreno_state_t)); +#ifdef _WINDOWS + UNREFERENCED_PARAMETER(current_time); +#endif + + if (nr_state != NULL) { + picoquic_newreno_reset(nr_state, path_x); + path_x->congestion_alg_state = nr_state; + } + else { + path_x->congestion_alg_state = NULL; + } +} + +/* + * Properly implementing New Reno requires managing a number of + * signals, such as packet losses or acknowledgements. We attempt + * to condensate all that in a single API, which could be shared + * by many different congestion control algorithms. + */ +static void picoquic_newreno_notify( + picoquic_cnx_t * cnx, + picoquic_path_t* path_x, + picoquic_congestion_notification_t notification, + uint64_t rtt_measurement, + uint64_t one_way_delay, + uint64_t nb_bytes_acknowledged, + uint64_t lost_packet_number, + uint64_t current_time) +{ +#ifdef _WINDOWS + UNREFERENCED_PARAMETER(lost_packet_number); +#endif + picoquic_newreno_state_t* nr_state = (picoquic_newreno_state_t*)path_x->congestion_alg_state; + + + if (nr_state != NULL) { + switch (notification) { + case picoquic_congestion_notification_acknowledgement: + if (path_x->last_time_acked_data_frame_sent > path_x->last_sender_limited_time) { + picoquic_newreno_sim_notify(&nr_state->nrss, cnx, path_x, notification, nb_bytes_acknowledged, current_time); + path_x->cwin = nr_state->nrss.cwin; + } + break; + case picoquic_congestion_notification_ecn_ec: + case picoquic_congestion_notification_repeat: + case picoquic_congestion_notification_timeout: + picoquic_newreno_sim_notify(&nr_state->nrss, cnx, path_x, notification, nb_bytes_acknowledged, current_time); + path_x->cwin = nr_state->nrss.cwin; + break; + case picoquic_congestion_notification_spurious_repeat: + picoquic_newreno_sim_notify(&nr_state->nrss, cnx, path_x, notification, nb_bytes_acknowledged, current_time); + path_x->cwin = nr_state->nrss.cwin; + break; + case picoquic_congestion_notification_rtt_measurement: + /* Using RTT increases as signal to get out of initial slow start */ + if (nr_state->nrss.alg_state == picoquic_newreno_alg_slow_start && + nr_state->nrss.ssthresh == UINT64_MAX){ + + if (path_x->rtt_min > PICOQUIC_TARGET_RENO_RTT) { + uint64_t min_win; + + if (path_x->rtt_min > PICOQUIC_TARGET_SATELLITE_RTT) { + min_win = (uint64_t)((double)PICOQUIC_CWIN_INITIAL * (double)PICOQUIC_TARGET_SATELLITE_RTT / (double)PICOQUIC_TARGET_RENO_RTT); + } + else { + /* Increase initial CWIN for long delay links. */ + min_win = (uint64_t)((double)PICOQUIC_CWIN_INITIAL * (double)path_x->rtt_min / (double)PICOQUIC_TARGET_RENO_RTT); + } + if (min_win > nr_state->nrss.cwin) { + nr_state->nrss.cwin = min_win; + path_x->cwin = min_win; + } + } + + if (picoquic_hystart_test(&nr_state->rtt_filter, (cnx->is_time_stamp_enabled) ? one_way_delay : rtt_measurement, + cnx->path[0]->pacing_packet_time_microsec, current_time, + cnx->is_time_stamp_enabled)) { + /* RTT increased too much, get out of slow start! */ + nr_state->nrss.ssthresh = nr_state->nrss.cwin; + nr_state->nrss.alg_state = picoquic_newreno_alg_congestion_avoidance; + path_x->cwin = nr_state->nrss.cwin; + } + } + break; + case picoquic_congestion_notification_cwin_blocked: + break; + case picoquic_congestion_notification_bw_measurement: + if (nr_state->nrss.alg_state == picoquic_newreno_alg_slow_start && + nr_state->nrss.ssthresh == UINT64_MAX) { + /* RTT measurements will happen after the bandwidth is estimated */ + uint64_t max_win = path_x->max_bandwidth_estimate * path_x->smoothed_rtt / 1000000; + uint64_t min_win = max_win /= 2; + if (nr_state->nrss.cwin < min_win) { + nr_state->nrss.cwin = min_win; + path_x->cwin = min_win; + } + } + break; + case picoquic_congestion_notification_reset: + picoquic_newreno_reset(nr_state, path_x); + break; + default: + /* ignore */ + break; + } + + /* Compute pacing data */ + picoquic_update_pacing_data(cnx, path_x, nr_state->nrss.alg_state == picoquic_newreno_alg_slow_start && + nr_state->nrss.ssthresh == UINT64_MAX); + } +} + +/* Release the state of the congestion control algorithm */ +static void picoquic_newreno_delete(picoquic_path_t* path_x) +{ + if (path_x->congestion_alg_state != NULL) { + free(path_x->congestion_alg_state); + path_x->congestion_alg_state = NULL; + } +} + +/* Observe the state of congestion control */ + +void picoquic_newreno_observe(picoquic_path_t* path_x, uint64_t* cc_state, uint64_t* cc_param) +{ + picoquic_newreno_state_t* nr_state = (picoquic_newreno_state_t*)path_x->congestion_alg_state; + *cc_state = (uint64_t)nr_state->nrss.alg_state; + *cc_param = (nr_state->nrss.ssthresh == UINT64_MAX) ? 0 : nr_state->nrss.ssthresh; +} + +/* Definition record for the New Reno algorithm */ + +#define PICOQUIC_NEWRENO_ID "newreno" /* NR88 */ + +picoquic_congestion_algorithm_t picoquic_newreno_algorithm_struct = { + PICOQUIC_NEWRENO_ID, 1, + picoquic_newreno_init, + picoquic_newreno_notify, + picoquic_newreno_delete, + picoquic_newreno_observe +}; + +picoquic_congestion_algorithm_t* picoquic_newreno_algorithm = &picoquic_newreno_algorithm_struct; diff --git a/deps/picoquic/packet.c b/deps/picoquic/packet.c new file mode 100644 index 0000000..96014ca --- /dev/null +++ b/deps/picoquic/packet.c @@ -0,0 +1,2222 @@ +/* +* Author: Christian Huitema +* Copyright (c) 2017, Private Octopus, Inc. +* All rights reserved. +* +* Permission to use, copy, modify, and distribute this software for any +* purpose with or without fee is hereby granted, provided that the above +* copyright notice and this permission notice appear in all copies. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL Private Octopus, Inc. BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/* + * Processing of an incoming packet. + * - Has to find the proper context, based on either the 64 bit context ID + * or a combination of source address, source port and partial context value. + * - Has to find the sequence number, based on partial values and windows. + * - For initial packets, has to perform version checks. + */ + +#include "picoquic_internal.h" +#include "picoquic_binlog.h" +#include "picoquic_unified_log.h" +#include "tls_api.h" +#include +#include +#include + +#if 0 +uint8_t* picoquic_frames_varint_decode(uint8_t* bytes, const uint8_t* bytes_max, uint64_t* n64); +uint8_t* picoquic_frames_varlen_decode(uint8_t* bytes, const uint8_t* bytes_max, size_t* n); +uint8_t* picoquic_frames_uint8_decode(uint8_t* bytes, const uint8_t* bytes_max, uint8_t* n); +uint8_t* picoquic_frames_uint16_decode(uint8_t* bytes, const uint8_t* bytes_max, uint16_t* n); +uint8_t* picoquic_frames_uint32_decode(uint8_t* bytes, const uint8_t* bytes_max, uint32_t* n); +uint8_t* picoquic_frames_uint64_decode(uint8_t* bytes, const uint8_t* bytes_max, uint64_t* n); +uint8_t* picoquic_frames_cid_decode(uint8_t* bytes, const uint8_t* bytes_max, picoquic_connection_id_t* n); +#endif + +int picoquic_parse_long_packet_header( + picoquic_quic_t* quic, + const uint8_t* bytes, + size_t length, + const struct sockaddr* addr_from, + picoquic_packet_header* ph, + picoquic_cnx_t** pcnx) +{ + int ret = 0; + + const uint8_t* bytes_start = bytes; + const uint8_t* bytes_max = bytes + length; + uint8_t flags = 0; + + if ((bytes = picoquic_frames_uint8_decode(bytes, bytes_max, &flags)) == NULL || + (bytes = picoquic_frames_uint32_decode(bytes, bytes_max, &ph->vn)) == NULL) + { + ret = -1; + } + else if (ph->vn != 0) { + ph->version_index = picoquic_get_version_index(ph->vn); + if (ph->version_index < 0) { + DBG_PRINTF("Version is not recognized: 0x%08x\n", ph->vn); + ph->ptype = picoquic_packet_error; + ph->pc = 0; + ret = PICOQUIC_ERROR_VERSION_NOT_SUPPORTED; + } + } + + if (ret == 0 && ( + (bytes = picoquic_frames_cid_decode(bytes, bytes_max, &ph->dest_cnx_id)) == NULL || + (bytes = picoquic_frames_cid_decode(bytes, bytes_max, &ph->srce_cnx_id)) == NULL)) { + ret = -1; + } + + if (ret == 0) { + ph->offset = bytes - bytes_start; + + if (ph->vn == 0) { + /* VN = zero identifies a version negotiation packet */ + ph->ptype = picoquic_packet_version_negotiation; + ph->pc = picoquic_packet_context_initial; + ph->payload_length = (uint16_t)((length > ph->offset) ? length - ph->offset : 0); + ph->pl_val = ph->payload_length; /* saving the value found in the packet */ + + if (*pcnx == NULL && quic != NULL) { + /* The version negotiation should always include the cnx-id sent by the client */ + if (quic->local_cnxid_length == 0) { + *pcnx = picoquic_cnx_by_net(quic, addr_from); + } + else if (ph->dest_cnx_id.id_len == quic->local_cnxid_length) { + *pcnx = picoquic_cnx_by_id(quic, ph->dest_cnx_id); + } + } + } + else { + size_t payload_length = 0; + /* If the version is supported now, the format field in the version table + * describes the encoding. */ + ph->spin = 0; + ph->has_spin_bit = 0; + ph->quic_bit_is_zero = (flags & 0x40) == 0; + + switch ((flags >> 4) & 3) { + case 0: /* Initial */ + { + /* special case of the initial packets. They contain a retry token between the header + * and the encrypted payload */ + size_t tok_len = 0; + bytes = picoquic_frames_varlen_decode(bytes, bytes_max, &tok_len); + + size_t bytes_left = bytes_max - bytes; + + ph->epoch = picoquic_epoch_initial; + if (bytes == NULL || bytes_left < tok_len) { + /* packet is malformed */ + ph->ptype = picoquic_packet_error; + ph->pc = 0; + ph->offset = length; + } + else { + ph->ptype = picoquic_packet_initial; + ph->pc = picoquic_packet_context_initial; + ph->token_length = tok_len; + ph->token_bytes = bytes; + bytes += tok_len; + ph->offset = bytes - bytes_start; + } + + break; + } + case 1: /* 0-RTT Protected */ + ph->ptype = picoquic_packet_0rtt_protected; + ph->pc = picoquic_packet_context_application; + ph->epoch = picoquic_epoch_0rtt; + break; + case 2: /* Handshake */ + ph->ptype = picoquic_packet_handshake; + ph->pc = picoquic_packet_context_handshake; + ph->epoch = picoquic_epoch_handshake; + break; + case 3: /* Retry */ + ph->ptype = picoquic_packet_retry; + ph->pc = picoquic_packet_context_initial; + ph->epoch = picoquic_epoch_initial; + break; + default: /* Not a valid packet type */ + DBG_PRINTF("Packet type is not recognized: 0x%02x\n", flags); + ph->ptype = picoquic_packet_error; + ph->version_index = -1; + ph->pc = 0; + break; + } + + if (ph->ptype == picoquic_packet_retry) { + /* No segment length or sequence number in retry packets */ + if (length > ph->offset) { + payload_length = length - ph->offset; + } + else { + payload_length = 0; + ph->ptype = picoquic_packet_error; + } + } + else if (ph->ptype != picoquic_packet_error) { + bytes = picoquic_frames_varlen_decode(bytes, bytes_max, &payload_length); + + size_t bytes_left = (bytes_max > bytes) ? bytes_max - bytes : 0; + if (bytes == NULL || bytes_left < payload_length || ph->version_index < 0) { + ph->ptype = picoquic_packet_error; + ph->payload_length = (uint16_t)((length > ph->offset) ? length - ph->offset : 0); + ph->pl_val = ph->payload_length; + } + } + + if (ph->ptype != picoquic_packet_error) + { + ph->pl_val = (uint16_t)payload_length; + ph->payload_length = (uint16_t)payload_length; + ph->offset = bytes - bytes_start; + ph->pn_offset = ph->offset; + + /* Retrieve the connection context */ + if (*pcnx == NULL) { + if (quic->local_cnxid_length == 0) { + *pcnx = picoquic_cnx_by_net(quic, addr_from); + } + else + { + if (ph->dest_cnx_id.id_len == quic->local_cnxid_length) { + *pcnx = picoquic_cnx_by_id(quic, ph->dest_cnx_id); + } + + if (*pcnx == NULL && (ph->ptype == picoquic_packet_initial || ph->ptype == picoquic_packet_0rtt_protected)) { + *pcnx = picoquic_cnx_by_icid(quic, &ph->dest_cnx_id, addr_from); + } + else if (*pcnx == NULL) { + DBG_PRINTF("Dropped packet of type %d, no connection", ph->ptype); + } + } + } + + if (ph->quic_bit_is_zero && *pcnx != NULL && !(*pcnx)->local_parameters.do_grease_quic_bit) { + ph->ptype = picoquic_packet_error; + } + } + else { + /* Try to find the connection context, for logging purpose. */ + if (*pcnx == NULL) { + if (quic->local_cnxid_length == 0) { + *pcnx = picoquic_cnx_by_net(quic, addr_from); + } + else if (ph->dest_cnx_id.id_len == quic->local_cnxid_length) { + *pcnx = picoquic_cnx_by_id(quic, ph->dest_cnx_id); + } + } + } + } + } + return ret; +} + +int picoquic_parse_short_packet_header( + picoquic_quic_t* quic, + const uint8_t* bytes, + size_t length, + const struct sockaddr* addr_from, + picoquic_packet_header* ph, + picoquic_cnx_t** pcnx, + int receiving) +{ + int ret = 0; + /* If this is a short header, it should be possible to retrieve the connection + * context. This depends on whether the quic context requires cnx_id or not. + */ + uint8_t cnxid_length = (receiving == 0 && *pcnx != NULL) ? (*pcnx)->path[0]->remote_cnxid.id_len : quic->local_cnxid_length; + ph->pc = picoquic_packet_context_application; + ph->pl_val = 0; /* No actual payload length in short headers */ + + if ((int)length >= 1 + cnxid_length) { + /* We can identify the connection by its ID */ + ph->offset = (size_t)1 + picoquic_parse_connection_id(bytes + 1, cnxid_length, &ph->dest_cnx_id); + /* TODO: should consider using combination of CNX ID and ADDR_FROM */ + if (*pcnx == NULL) + { + if (quic->local_cnxid_length > 0) { + *pcnx = picoquic_cnx_by_id(quic, ph->dest_cnx_id); + } + else { + *pcnx = picoquic_cnx_by_net(quic, addr_from); + } + } + } + else { + ph->ptype = picoquic_packet_error; + ph->offset = length; + ph->payload_length = 0; + } + + if (*pcnx != NULL) { + int has_loss_bit = (receiving && (*pcnx)->is_loss_bit_enabled_incoming) || ((!receiving && (*pcnx)->is_loss_bit_enabled_outgoing)); + ph->epoch = picoquic_epoch_1rtt; + ph->version_index = (*pcnx)->version_index; + ph->quic_bit_is_zero = (bytes[0] & 0x40) == 0; + + if (!ph->quic_bit_is_zero ||(*pcnx)->local_parameters.do_grease_quic_bit) { + /* We do not check the quic bit if the local endpoint advertised greasing. */ + ph->ptype = picoquic_packet_1rtt_protected; + } else { + /* Check for QUIC bit failed! */ + ph->ptype = picoquic_packet_error; + } + + ph->has_spin_bit = 1; + ph->spin = (bytes[0] >> 5) & 1; + ph->pn_offset = ph->offset; + ph->pn = 0; + ph->pnmask = 0; + ph->key_phase = ((bytes[0] >> 2) & 1); /* Initialize here so that simple tests with unencrypted headers can work */ + + if (has_loss_bit) { + ph->has_loss_bits = 1; + ph->loss_bit_L = (bytes[0] >> 3) & 1; + ph->loss_bit_Q = (bytes[0] >> 4) & 1; + } + if (length < ph->offset || ph->ptype == picoquic_packet_error) { + ret = -1; + ph->payload_length = 0; + } + else { + ph->payload_length = (uint16_t)(length - ph->offset); + } + } + else { + /* This may be a packet to a forgotten connection */ + ph->ptype = picoquic_packet_1rtt_protected; + ph->payload_length = (uint16_t)((length > ph->offset) ? length - ph->offset : 0); + } + return ret; +} + +int picoquic_parse_packet_header( + picoquic_quic_t* quic, + const uint8_t* bytes, + size_t length, + const struct sockaddr* addr_from, + picoquic_packet_header* ph, + picoquic_cnx_t** pcnx, + int receiving) +{ + int ret = 0; + + /* Initialize the PH structure to zero, but version index to -1 (error) */ + memset(ph, 0, sizeof(picoquic_packet_header)); + ph->version_index = -1; + + /* Is this a long header or a short header? -- in any case, we need at least 17 bytes */ + if ((bytes[0] & 0x80) == 0x80) { + ret = picoquic_parse_long_packet_header(quic, bytes, length, addr_from, ph, pcnx); + } else { + ret = picoquic_parse_short_packet_header(quic, bytes, length, addr_from, ph, pcnx, receiving); + } + + return ret; +} + + +/* The packet number logic */ +uint64_t picoquic_get_packet_number64(uint64_t highest, uint64_t mask, uint32_t pn) +{ + uint64_t expected = highest + 1; + uint64_t not_mask_plus_one = (~mask) + 1; + uint64_t pn64 = (expected & mask) | pn; + + if (pn64 < expected) { + uint64_t delta1 = expected - pn64; + uint64_t delta2 = not_mask_plus_one - delta1; + if (delta2 < delta1) { + pn64 += not_mask_plus_one; + } + } else { + uint64_t delta1 = pn64 - expected; + uint64_t delta2 = not_mask_plus_one - delta1; + + if (delta2 <= delta1 && (pn64 & mask) > 0) { + /* Out of sequence packet from previous roll */ + pn64 -= not_mask_plus_one; + } + } + + return pn64; +} + +/* Debug code used to test whether the PN decryption works as expected. + */ + +void picoquic_log_pn_dec_trial(picoquic_cnx_t* cnx) +{ + if (cnx->quic->log_pn_dec && (cnx->quic->F_log != NULL || cnx->f_binlog != NULL)){ + void* pn_dec = cnx->crypto_context[picoquic_epoch_1rtt].pn_dec; + void* pn_enc = cnx->crypto_context[picoquic_epoch_1rtt].pn_enc; + uint8_t test_iv[32] = { + 0, 1, 3, 4, 4, 6, 7, 8, 9, + 0, 1, 3, 4, 4, 6, 7, 8, 9, + 0, 1, 3, 4, 4, 6, 7, 8, 9, + 0, 1 }; + size_t mask_length = 5; + uint8_t mask_bytes[5] = { 0, 0, 0, 0, 0 }; + uint8_t demask_bytes[5] = { 0, 0, 0, 0, 0 }; + + if (pn_enc != NULL) { + picoquic_pn_encrypt(pn_enc, test_iv, mask_bytes, mask_bytes, mask_length); + } + + if (pn_dec != NULL) { + picoquic_pn_encrypt(pn_dec, test_iv, demask_bytes, demask_bytes, mask_length); + } + + picoquic_log_app_message(cnx, "1RTT PN ENC/DEC, Phi: %d, signature = %02x%02x%02x%02x%02x, %02x%02x%02x%02x%02x", + cnx->key_phase_enc, + mask_bytes[0], mask_bytes[1], mask_bytes[2], mask_bytes[3], mask_bytes[4], + demask_bytes[0], demask_bytes[1], demask_bytes[2], demask_bytes[3], demask_bytes[4]); + } +} + + +/* + * Remove header protection + */ +int picoquic_remove_header_protection(picoquic_cnx_t* cnx, + uint8_t* bytes, picoquic_packet_header* ph) +{ + int ret = 0; + size_t length = ph->offset + ph->payload_length; /* this may change after decrypting the PN */ + void * pn_enc = NULL; + + pn_enc = cnx->crypto_context[ph->epoch].pn_dec; + + if (pn_enc != NULL) + { + /* The header length is not yet known, will only be known after the sequence number is decrypted */ + size_t mask_length = 5; + size_t sample_offset = ph->pn_offset + 4; + size_t sample_size = picoquic_pn_iv_size(pn_enc); + uint8_t mask_bytes[5] = { 0, 0, 0, 0, 0 }; + + if (sample_offset + sample_size > length) + { + /* return an error */ + /* Invalid packet format. Avoid crash! */ + ph->pn = 0xFFFFFFFF; + ph->pnmask = 0xFFFFFFFF00000000ull; + ph->offset = ph->pn_offset; + + DBG_PRINTF("Invalid packet length, type: %d, epoch: %d, pc: %d, pn-offset: %d, length: %d\n", + ph->ptype, ph->epoch, ph->pc, (int)ph->pn_offset, (int)length); + } + else + { /* Decode */ + uint8_t first_byte = bytes[0]; + uint8_t first_mask = ((first_byte & 0x80) == 0x80) ? 0x0F : (cnx->is_loss_bit_enabled_incoming)?0x07:0x1F; + uint8_t pn_l; + uint32_t pn_val = 0; + + picoquic_pn_encrypt(pn_enc, bytes + sample_offset, mask_bytes, mask_bytes, mask_length); + /* Decode the first byte */ + first_byte ^= (mask_bytes[0] & first_mask); + pn_l = (first_byte & 3) + 1; + ph->pnmask = (0xFFFFFFFFFFFFFFFFull); + bytes[0] = first_byte; + + /* Packet encoding is 1 to 4 bytes */ + for (uint8_t i = 1; i <= pn_l; i++) { + pn_val <<= 8; + bytes[ph->offset] ^= mask_bytes[i]; + pn_val += bytes[ph->offset++]; + ph->pnmask <<= 8; + } + + ph->pn = pn_val; + ph->payload_length -= pn_l; + /* Only set the key phase byte if short header */ + if (ph->ptype == picoquic_packet_1rtt_protected) { + ph->key_phase = ((first_byte >> 2) & 1); + } + + /* Build a packet number to 64 bits */ + ph->pn64 = picoquic_get_packet_number64( + cnx->pkt_ctx[ph->pc].first_sack_item.end_of_sack_range, ph->pnmask, ph->pn); + + /* Check the reserved bits */ + ph->has_reserved_bit_set = ((first_byte & 0x80) == 0 && !cnx->is_loss_bit_enabled_incoming && + (first_byte & 0x18) != 0); + } + } + else { + /* The pn_enc algorithm was not initialized. Avoid crash! */ + ph->pn = 0xFFFFFFFF; + ph->pnmask = 0xFFFFFFFF00000000ull; + ph->offset = ph->pn_offset; + ph->pn64 = 0xFFFFFFFFFFFFFFFFull; + + DBG_PRINTF("PN dec not ready, type: %d, epoch: %d, pc: %d, pn: %d\n", + ph->ptype, ph->epoch, ph->pc, (int)ph->pn); + + ret = PICOQUIC_ERROR_AEAD_NOT_READY; + } + + return ret; +} + +/* + * Remove packet protection + */ +size_t picoquic_remove_packet_protection(picoquic_cnx_t* cnx, + uint8_t* bytes, picoquic_packet_header* ph, + uint64_t current_time, int * already_received) +{ + size_t decoded; + int ret = 0; + + /* verify that the packet is new */ + if (already_received != NULL) { + if (picoquic_is_pn_already_received(cnx, ph->pc, ph->pn64) != 0) { + /* Set error type: already received */ + *already_received = 1; + } + else { + *already_received = 0; + } + } + + if (ph->epoch == picoquic_epoch_1rtt) { + int need_integrity_check = 1; + /* Manage key rotation */ + if (ph->key_phase == cnx->key_phase_dec) { + /* AEAD Decrypt, in place */ + decoded = picoquic_aead_decrypt_generic(bytes + ph->offset, + bytes + ph->offset, ph->payload_length, ph->pn64, bytes, ph->offset, cnx->crypto_context[picoquic_epoch_1rtt].aead_decrypt); + } + else if (ph->pn64 < cnx->crypto_rotation_sequence) { + /* This packet claims to be encoded with the old key */ + if (current_time > cnx->crypto_rotation_time_guard) { + /* Too late. Ignore the packet. Could be some kind of attack. */ + decoded = ph->payload_length + 1; + need_integrity_check = 0; + } + else if (cnx->crypto_context_old.aead_decrypt != NULL) { + decoded = picoquic_aead_decrypt_generic(bytes + ph->offset, + bytes + ph->offset, ph->payload_length, ph->pn64, bytes, ph->offset, cnx->crypto_context_old.aead_decrypt); + } + else { + /* old context is either not yet available, or already removed */ + decoded = ph->payload_length + 1; + need_integrity_check = 0; + } + } + else { + /* TODO: check that this is larger than last received with current key */ + /* These could only be a new key */ + if (cnx->crypto_context_new.aead_decrypt == NULL && + cnx->crypto_context_new.aead_encrypt == NULL) { + /* If the new context was already computed, don't do it again */ + ret = picoquic_compute_new_rotated_keys(cnx); + } + /* if decoding succeeds, the rotation should be validated */ + if (ret == 0 && cnx->crypto_context_new.aead_decrypt != NULL) { + decoded = picoquic_aead_decrypt_generic(bytes + ph->offset, + bytes + ph->offset, ph->payload_length, ph->pn64, bytes, ph->offset, cnx->crypto_context_new.aead_decrypt); + + if (decoded <= ph->payload_length) { + /* Rotation only if the packet was correctly decrypted with the new key */ + cnx->crypto_rotation_time_guard = current_time + cnx->path[0]->retransmit_timer; + cnx->crypto_rotation_sequence = ph->pn64; + picoquic_apply_rotated_keys(cnx, 0); + cnx->nb_crypto_key_rotations++; + + if (cnx->crypto_context_new.aead_encrypt != NULL) { + /* If that move was not already validated, move to the new encryption keys */ + picoquic_apply_rotated_keys(cnx, 1); + } + } + } + else { + /* new context could not be computed */ + decoded = ph->payload_length + 1; + need_integrity_check = 0; + } + } + + if (need_integrity_check && decoded > ph->payload_length) { + cnx->crypto_failure_count++; + if (cnx->crypto_failure_count > picoquic_aead_integrity_limit(cnx->crypto_context[picoquic_epoch_1rtt].aead_decrypt)) { + picoquic_log_app_message(cnx, "AEAD Integrity limit reached after 0x%" PRIx64 " failed decryptions.", cnx->crypto_failure_count); + (void)picoquic_connection_error(cnx, PICOQUIC_TRANSPORT_AEAD_LIMIT_REACHED, 0); + } + } + } + else { + /* TODO: get rid of handshake some time after handshake complete */ + /* For all the other epochs, there is a single crypto context and no key rotation */ + if (cnx->crypto_context[ph->epoch].aead_decrypt != NULL) { + decoded = picoquic_aead_decrypt_generic(bytes + ph->offset, + bytes + ph->offset, ph->payload_length, ph->pn64, bytes, ph->offset, cnx->crypto_context[ph->epoch].aead_decrypt); + } + else { + decoded = ph->payload_length + 1; + } + } + + /* Add here a check that the PN key is still valid. */ + if (decoded > ph->payload_length) { + picoquic_log_pn_dec_trial(cnx); + } + + /* by conventions, values larger than input indicate error */ + return decoded; +} + +int picoquic_parse_header_and_decrypt( + picoquic_quic_t* quic, + const uint8_t* bytes, + size_t length, + size_t packet_length, + const struct sockaddr* addr_from, + uint64_t current_time, + picoquic_packet_header* ph, + picoquic_cnx_t** pcnx, + size_t * consumed, + int * new_ctx_created) +{ + /* Parse the clear text header. Ret == 0 means an incorrect packet that could not be parsed */ + int already_received = 0; + size_t decoded_length = 0; + int ret = picoquic_parse_packet_header(quic, bytes, length, addr_from, ph, pcnx, 1); + + *new_ctx_created = 0; + + if (ret == 0 ) { + if (ph->ptype != picoquic_packet_version_negotiation && + ph->ptype != picoquic_packet_retry && ph->ptype != picoquic_packet_error) { + /* TODO: clarify length, payload length, packet length -- special case of initial packet */ + length = ph->offset + ph->payload_length; + *consumed = length; + + if (*pcnx == NULL) { + if (ph->ptype == picoquic_packet_initial) { + /* Create a connection context if the CI is acceptable */ + if (packet_length < PICOQUIC_ENFORCED_INITIAL_MTU) { + /* Unexpected packet. Reject, drop and log. */ + ret = PICOQUIC_ERROR_INITIAL_TOO_SHORT; + } + else if (ph->dest_cnx_id.id_len < PICOQUIC_ENFORCED_INITIAL_CID_LENGTH) { + /* Initial CID too short -- ignore the packet */ + ret = PICOQUIC_ERROR_INITIAL_CID_TOO_SHORT; + } + else { + /* if listening is OK, listen */ + *pcnx = picoquic_create_cnx(quic, ph->dest_cnx_id, ph->srce_cnx_id, addr_from, current_time, ph->vn, NULL, NULL, 0); + /* If an incoming connection was created, register the ICID */ + *new_ctx_created = (*pcnx == NULL) ? 0 : 1; + if (*pcnx == NULL) { + DBG_PRINTF("%s", "Cannot create connection context\n"); + } + } + } + } + else if (!(*pcnx)->client_mode && ph->ptype == picoquic_packet_initial && packet_length < PICOQUIC_ENFORCED_INITIAL_MTU) { + /* Unexpected packet. Reject, drop and log. */ + ret = PICOQUIC_ERROR_INITIAL_TOO_SHORT; + } + + if (ret == 0) { + if (*pcnx != NULL) { + /* Remove header protection at this point -- values of bytes will change */ + ret = picoquic_remove_header_protection(*pcnx, (uint8_t *)bytes, ph); + + if (ret == 0) { + decoded_length = picoquic_remove_packet_protection(*pcnx, (uint8_t *) bytes, ph, current_time, &already_received); + } + else { + decoded_length = ph->payload_length + 1; + } + + if (decoded_length > (length - ph->offset)) { + if (ph->ptype == picoquic_packet_1rtt_protected && + length >= PICOQUIC_RESET_PACKET_MIN_SIZE && + memcmp(bytes + length - PICOQUIC_RESET_SECRET_SIZE, + (*pcnx)->path[0]->reset_secret, PICOQUIC_RESET_SECRET_SIZE) == 0) { + ret = PICOQUIC_ERROR_STATELESS_RESET; + } + else { + if (ret != PICOQUIC_ERROR_AEAD_NOT_READY) { + ret = PICOQUIC_ERROR_AEAD_CHECK; + } + if (*new_ctx_created) { + picoquic_delete_cnx(*pcnx); + *pcnx = NULL; + *new_ctx_created = 0; + } + } + } + else if (already_received != 0) { + ret = PICOQUIC_ERROR_DUPLICATE; + } + else { + ph->payload_length = (uint16_t)decoded_length; + } + } + else if (ph->ptype == picoquic_packet_1rtt_protected) + { + /* This may be a stateless reset. + * We test the address + putative reset secret pair against the hash table + * of registered secrets. If there is a match, the corresponding connection is + * found and the packet is marked as Stateless Reset */ + + if (length >= PICOQUIC_RESET_PACKET_MIN_SIZE) { + *pcnx = picoquic_cnx_by_secret(quic, bytes + length - PICOQUIC_RESET_SECRET_SIZE, addr_from); + if (*pcnx != NULL) { + ret = PICOQUIC_ERROR_STATELESS_RESET; + } + } + } + } + } + else { + *consumed = length; + } + } + + return ret; +} + +/* + * Processing of a version renegotiation packet. + * + * From the specification: When the client receives a Version Negotiation packet + * from the server, it should select an acceptable protocol version. If the server + * lists an acceptable version, the client selects that version and reattempts to + * create a connection using that version. Though the contents of a packet might + * not change in response to version negotiation, a client MUST increase the packet + * number it uses on every packet it sends. Packets MUST continue to use long headers + * and MUST include the new negotiated protocol version. + */ +int picoquic_incoming_version_negotiation( + picoquic_cnx_t* cnx, + uint8_t* bytes, + size_t length, + struct sockaddr* addr_from, + picoquic_packet_header* ph, + uint64_t current_time) +{ + int ret = 0; +#ifdef _WINDOWS + UNREFERENCED_PARAMETER(addr_from); + UNREFERENCED_PARAMETER(current_time); +#endif + + /* Check the connection state */ + if (cnx->cnx_state != picoquic_state_client_init_sent) { + /* This is an unexpected packet. Log and drop.*/ + DBG_PRINTF("Unexpected VN packet (%d), state %d, type: %d, epoch: %d, pc: %d, pn: %d\n", + cnx->client_mode, cnx->cnx_state, ph->ptype, ph->epoch, ph->pc, (int)ph->pn); + } else if (picoquic_compare_connection_id(&ph->dest_cnx_id, &cnx->path[0]->p_local_cnxid->cnx_id) != 0 || ph->vn != 0) { + /* Packet that do not match the "echo" checks should be logged and ignored */ + DBG_PRINTF("VN packet (%d), does not pass echo test.\n", cnx->client_mode); + ret = PICOQUIC_ERROR_DETECTED; + } else { + /* Add DOS resilience */ + const uint8_t * v_bytes = bytes + ph->offset; + const uint8_t* bytes_max = bytes + length; + int nb_vn = 0; + while (v_bytes < bytes_max) { + uint32_t vn = 0; + if ((v_bytes = picoquic_frames_uint32_decode(v_bytes, bytes_max, &vn)) == NULL){ + DBG_PRINTF("VN packet (%d), length %zu, coding error after %d version numbers.\n", + cnx->client_mode, length, nb_vn); + ret = PICOQUIC_ERROR_DETECTED; + break; + } else if (vn == cnx->proposed_version) { + DBG_PRINTF("VN packet (%d), proposed_version[%d] = 0x%08x.\n", cnx->client_mode, nb_vn, vn); + ret = PICOQUIC_ERROR_DETECTED; + break; + } + nb_vn++; + } + if (ret == 0) { + if (nb_vn == 0) { + DBG_PRINTF("VN packet (%d), does not propose any version.\n", cnx->client_mode); + ret = PICOQUIC_ERROR_DETECTED; + } + else { + /* Signal VN to the application */ + if (cnx->callback_fn && length > ph->offset) { + (void)(cnx->callback_fn)(cnx, 0, bytes + ph->offset, length - ph->offset, + picoquic_callback_version_negotiation, cnx->callback_ctx, NULL); + } + /* TODO: consider rewriting the version negotiation code */ + DBG_PRINTF("%s", "Disconnect upon receiving version negotiation.\n"); + cnx->cnx_state = picoquic_state_disconnected; + ret = 0; + } + } + } + + return ret; +} + +/* + * Send a version negotiation packet in response to an incoming packet + * sporting the wrong version number. This assumes that the original packet + * is at least 517 bytes long. + */ + +void picoquic_prepare_version_negotiation( + picoquic_quic_t* quic, + struct sockaddr* addr_from, + struct sockaddr* addr_to, + unsigned long if_index_to, + picoquic_packet_header* ph, + uint8_t* original_bytes) +{ + picoquic_cnx_t* cnx = NULL; + uint8_t dcid_length = original_bytes[5]; + uint8_t * dcid = original_bytes + 6; + uint8_t scid_length = original_bytes[6 + dcid_length]; + uint8_t* scid = original_bytes + 6 + dcid_length + 1; + + /* Verify that this is not a spurious error by checking whether a connection context + * already exists */ + if (dcid_length <= PICOQUIC_CONNECTION_ID_MAX_SIZE) { + (void) picoquic_parse_connection_id(dcid, dcid_length, &ph->dest_cnx_id); + if (ph->dest_cnx_id.id_len == quic->local_cnxid_length) { + if (quic->local_cnxid_length == 0) { + cnx = picoquic_cnx_by_net(quic, addr_from); + } + else { + cnx = picoquic_cnx_by_id(quic, ph->dest_cnx_id); + } + } + if (cnx == NULL) { + cnx = picoquic_cnx_by_icid(quic, &ph->dest_cnx_id, addr_from); + } + } + else { + (void)picoquic_parse_connection_id(dcid, PICOQUIC_CONNECTION_ID_MAX_SIZE, &ph->dest_cnx_id); + } + + /* If no connection context exists, send back a version negotiation */ + if (cnx == NULL) { + picoquic_stateless_packet_t* sp = picoquic_create_stateless_packet(quic); + + if (sp != NULL) { + uint8_t* bytes = sp->bytes; + size_t byte_index = 0; + uint32_t rand_vn; + + /* Packet type set to random value for version negotiation */ + picoquic_public_random(bytes + byte_index, 1); + bytes[byte_index++] |= 0x80; + /* Set the version number to zero */ + picoformat_32(bytes + byte_index, 0); + byte_index += 4; + + /* Copy the connection identifiers */ + bytes[byte_index++] = scid_length; + memcpy(bytes + byte_index, scid, scid_length); + byte_index += scid_length; + bytes[byte_index++] = dcid_length; + memcpy(bytes + byte_index, dcid, dcid_length); + byte_index += dcid_length; + + /* Set the payload to the list of versions */ + for (size_t i = 0; i < picoquic_nb_supported_versions; i++) { + picoformat_32(bytes + byte_index, picoquic_supported_versions[i].version); + byte_index += 4; + } + /* Add random reserved value as grease, but be careful to not match proposed version */ + do { + rand_vn = (((uint32_t)picoquic_public_random_64()) & 0xF0F0F0F0) | 0x0A0A0A0A; + } while (rand_vn == ph->vn); + picoformat_32(bytes + byte_index, rand_vn); + byte_index += 4; + + /* Set length and addresses, and queue. */ + sp->length = byte_index; + picoquic_store_addr(&sp->addr_to, addr_from); + picoquic_store_addr(&sp->addr_local, addr_to); + sp->if_index_local = if_index_to; + sp->initial_cid = ph->dest_cnx_id; + sp->cnxid_log64 = picoquic_val64_connection_id(sp->initial_cid); + sp->ptype = picoquic_packet_version_negotiation; + + picoquic_log_quic_pdu(quic, 1, picoquic_get_quic_time(quic), 0, addr_to, addr_from, sp->length); + + picoquic_queue_stateless_packet(quic, sp); + } + } +} + +/* + * Process an unexpected connection ID. This could be an old packet from a + * previous connection. If the packet type correspond to an encrypted value, + * the server can respond with a public reset. + * + * Per draft 14, the stateless reset starts with the packet code 0K110000. + * The packet has after the first byte at least 23 random bytes, and then + * the 16 bytes reset token. + */ +void picoquic_process_unexpected_cnxid( + picoquic_quic_t* quic, + size_t length, + struct sockaddr* addr_from, + struct sockaddr* addr_to, + unsigned long if_index_to, + picoquic_packet_header* ph) +{ + if (length > PICOQUIC_RESET_PACKET_MIN_SIZE && + ph->ptype == picoquic_packet_1rtt_protected) { + picoquic_stateless_packet_t* sp = picoquic_create_stateless_packet(quic); + if (sp != NULL) { + size_t pad_size = length - PICOQUIC_RESET_SECRET_SIZE -1; + uint8_t* bytes = sp->bytes; + size_t byte_index = 0; + + if (pad_size > PICOQUIC_RESET_PACKET_PAD_SIZE) { + pad_size = (size_t)picoquic_public_uniform_random(pad_size - PICOQUIC_RESET_PACKET_PAD_SIZE) + + PICOQUIC_RESET_PACKET_PAD_SIZE; + } + else { + pad_size = PICOQUIC_RESET_PACKET_PAD_SIZE; + } + + /* Packet type set to short header, randomize the 5 lower bits */ + bytes[byte_index++] = 0x30 | (uint8_t)(picoquic_public_random_64() & 0x1F); + + /* Add the random bytes */ + picoquic_public_random(bytes + byte_index, pad_size); + byte_index += pad_size; + /* Add the public reset secret */ + (void)picoquic_create_cnxid_reset_secret(quic, &ph->dest_cnx_id, bytes + byte_index); + byte_index += PICOQUIC_RESET_SECRET_SIZE; + sp->length = byte_index; + sp->ptype = picoquic_packet_1rtt_protected; + picoquic_store_addr(&sp->addr_to, addr_from); + picoquic_store_addr(&sp->addr_local, addr_to); + sp->if_index_local = if_index_to; + sp->initial_cid = ph->dest_cnx_id; + sp->cnxid_log64 = picoquic_val64_connection_id(sp->initial_cid); + + picoquic_log_context_free_app_message(quic, &sp->initial_cid, "Unexpected connection ID, sending stateless reset.\n"); + + picoquic_queue_stateless_packet(quic, sp); + } + } +} + +/* + * Queue a stateless retry packet + */ + +void picoquic_queue_stateless_retry(picoquic_cnx_t* cnx, + picoquic_packet_header* ph, struct sockaddr* addr_from, + struct sockaddr* addr_to, + unsigned long if_index_to, + uint8_t * token, + size_t token_length) +{ + picoquic_stateless_packet_t* sp = picoquic_create_stateless_packet(cnx->quic); + void * integrity_aead = picoquic_find_retry_protection_context(cnx, 1); + size_t checksum_length = (integrity_aead == NULL) ? 0 : picoquic_aead_get_checksum_length(integrity_aead); + + if (sp != NULL) { + uint8_t* bytes = sp->bytes; + size_t byte_index = 0; + size_t header_length = 0; + size_t pn_offset; + size_t pn_length; + + cnx->path[0]->remote_cnxid = ph->srce_cnx_id; + + byte_index = header_length = picoquic_create_packet_header(cnx, picoquic_packet_retry, + 0, &cnx->path[0]->remote_cnxid, &cnx->path[0]->p_local_cnxid->cnx_id, 0, + bytes, &pn_offset, &pn_length); + + /* In the old drafts, there is no header protection and the sender copies the ODCID + * in the packet. In the recent draft, the ODCID is not sent but + * is verified as part of integrity checksum */ + if (integrity_aead == NULL) { + bytes[byte_index++] = cnx->initial_cnxid.id_len; + byte_index += picoquic_format_connection_id(bytes + byte_index, + PICOQUIC_MAX_PACKET_SIZE - byte_index - checksum_length, cnx->initial_cnxid); + } + + /* Add the token */ + memcpy(&bytes[byte_index], token, token_length); + byte_index += token_length; + + /* Encode the retry integrity protection if required. */ + byte_index = picoquic_encode_retry_protection(integrity_aead, bytes, PICOQUIC_MAX_PACKET_SIZE, byte_index, &cnx->initial_cnxid); + + sp->length = byte_index; + + sp->ptype = picoquic_packet_1rtt_protected; + + picoquic_store_addr(&sp->addr_to, addr_from); + picoquic_store_addr(&sp->addr_local, addr_to); + sp->if_index_local = if_index_to; + sp->cnxid_log64 = picoquic_val64_connection_id(picoquic_get_logging_cnxid(cnx)); + + picoquic_log_outgoing_packet(cnx, + bytes, 0, pn_length, sp->length, + bytes, sp->length, picoquic_get_quic_time(cnx->quic)); + + picoquic_queue_stateless_packet(cnx->quic, sp); + } +} + +/* + * Processing of initial or handshake messages when they are not expected + * any more. These messages could be used in a DOS attack against the + * connection, but they could also be legit messages sent by a peer + * that does not implement implicit ACK. They are processed to not + * cause any side effect, but to still generate ACK if the client + * needs them. + */ + +void picoquic_ignore_incoming_handshake( + picoquic_cnx_t* cnx, + uint8_t* bytes, + picoquic_packet_header* ph, + uint64_t current_time) +{ + /* The data starts at ph->index, and its length + * is ph->payload_length. */ + int ret = 0; + size_t byte_index = 0; + int ack_needed = 0; + picoquic_packet_context_enum pc; + + if (ph->ptype == picoquic_packet_initial) { + pc = picoquic_packet_context_initial; + } + else if (ph->ptype == picoquic_packet_handshake) { + pc = picoquic_packet_context_handshake; + } + else { + /* Not expected! */ + return; + } + + bytes += ph->offset; + + while (ret == 0 && byte_index < ph->payload_length) { + size_t frame_length = 0; + int frame_is_pure_ack = 0; + ret = picoquic_skip_frame(&bytes[byte_index], + ph->payload_length - byte_index, &frame_length, &frame_is_pure_ack); + byte_index += frame_length; + if (frame_is_pure_ack == 0) { + ack_needed = 1; + } + } + + /* If the packet contains ackable data, mark ack needed + * in the relevant packet context */ + if (ret == 0 && ack_needed) { + picoquic_set_ack_needed(cnx, current_time, pc); + } +} + +/* + * Processing of an incoming client initial packet, + * on an unknown connection context. + */ + +int picoquic_incoming_client_initial( + picoquic_cnx_t** pcnx, + uint8_t* bytes, + size_t packet_length, + struct sockaddr* addr_from, + struct sockaddr* addr_to, + unsigned long if_index_to, + picoquic_packet_header* ph, + uint64_t current_time, + int new_context_created) +{ + int ret = 0; + + /* Logic to test the retry token. + * the "check token" value may change between the time a previous client connection + * attempt triggered a "retry" and the time that retry arrive, so it is not wrong + * to receive a retry token even if not required, as long as it decrypts correctly. + */ + if ((*pcnx)->cnx_state == picoquic_state_server_init && + !(*pcnx)->quic->server_busy) { + int is_new_token = 0; + int is_wrong_token = 0; + if (ph->token_length > 0) { + if (picoquic_verify_retry_token((*pcnx)->quic, addr_from, current_time, + &is_new_token, &(*pcnx)->original_cnxid, &ph->dest_cnx_id, ph->pn, + ph->token_bytes, ph->token_length, new_context_created) != 0) { + is_wrong_token = 1; + } + else { + (*pcnx)->initial_validated = 1; + } + } + if (is_wrong_token && !is_new_token) { + (void)picoquic_connection_error(*pcnx, PICOQUIC_TRANSPORT_INVALID_TOKEN, 0); + ret = PICOQUIC_ERROR_INVALID_TOKEN; + } + else if ((*pcnx)->quic->check_token && (ph->token_length == 0 || is_wrong_token)){ + uint8_t token_buffer[256]; + size_t token_size; + + if (picoquic_prepare_retry_token((*pcnx)->quic, addr_from, + current_time + PICOQUIC_TOKEN_DELAY_SHORT, &ph->dest_cnx_id, + &(*pcnx)->path[0]->p_local_cnxid->cnx_id, ph->pn, + token_buffer, sizeof(token_buffer), &token_size) != 0) { + ret = PICOQUIC_ERROR_MEMORY; + } + else { + picoquic_queue_stateless_retry(*pcnx, ph, + addr_from, addr_to, if_index_to, token_buffer, token_size); + ret = PICOQUIC_ERROR_RETRY; + } + } + } + + if (ret == 0) { + if (picoquic_compare_connection_id(&ph->dest_cnx_id, &(*pcnx)->path[0]->p_local_cnxid->cnx_id) == 0) { + (*pcnx)->initial_validated = 1; + } + + if (!(*pcnx)->initial_validated && (*pcnx)->pkt_ctx[picoquic_packet_context_initial].retransmit_oldest != NULL + && packet_length >= PICOQUIC_ENFORCED_INITIAL_MTU) { + /* In most cases, receiving more than 1 initial packets before validation indicates that the + * client is repeating data that it believes is lost. We set the initial_repeat_needed flag + * to trigger such repetitions. There are exceptions, e.g., clients sending large client hellos + * that require multiple packets. These exceptions are detected and handled during packet + * processing. */ + (*pcnx)->initial_repeat_needed = 1; + } + + if ((*pcnx)->cnx_state == picoquic_state_server_init && + (*pcnx)->quic->server_busy) { + (*pcnx)->local_error = PICOQUIC_TRANSPORT_SERVER_BUSY; + (*pcnx)->cnx_state = picoquic_state_handshake_failure; + } + else if ((*pcnx)->cnx_state == picoquic_state_server_init && + (*pcnx)->initial_cnxid.id_len < PICOQUIC_ENFORCED_INITIAL_CID_LENGTH) { + (*pcnx)->local_error = PICOQUIC_TRANSPORT_PROTOCOL_VIOLATION; + (*pcnx)->cnx_state = picoquic_state_handshake_failure; + } + else if ((*pcnx)->cnx_state < picoquic_state_server_almost_ready) { + /* Document the incoming addresses */ + if ((*pcnx)->path[0]->local_addr.ss_family == 0 && addr_to != NULL) { + picoquic_store_addr(&(*pcnx)->path[0]->local_addr, addr_to); + } + if ((*pcnx)->path[0]->peer_addr.ss_family == 0 && addr_from != NULL) { + picoquic_store_addr(&(*pcnx)->path[0]->peer_addr, addr_from); + } + (*pcnx)->path[0]->if_index_dest = if_index_to; + + /* decode the incoming frames */ + if (ret == 0) { + ret = picoquic_decode_frames(*pcnx, (*pcnx)->path[0], + bytes + ph->offset, ph->payload_length, ph->epoch, addr_from, addr_to, current_time); + } + + /* processing of client initial packet */ + if (ret == 0) { + int data_consumed = 0; + /* initialization of context & creation of data */ + ret = picoquic_tls_stream_process(*pcnx, &data_consumed); + /* The "initial_repeat_needed" flag is set if multiple initial packets are + * received while the connection is not yet validated. In most cases, this indicates + * that the client repeated some initial packets, or sent some gratuitous initial + * packets, because it believes its own initial packet was lost. The flag forces + * immediate retransmission of initial packets. However, there are cases when the + * client sent large client hello messages that do not fit on a single packets. In + * those cases, the flag should not be set. We detect that by testing whether new + * TLS data was received in the packet. */ + if (data_consumed) { + (*pcnx)->initial_repeat_needed = 0; + } + } + } + else if ((*pcnx)->cnx_state < picoquic_state_ready) { + /* Require an acknowledgement if the packet contains ackable frames */ + picoquic_ignore_incoming_handshake(*pcnx, bytes, ph, current_time); + } + else { + /* Initial keys should have been discarded, treat packet as unexpected */ + ret = PICOQUIC_ERROR_UNEXPECTED_PACKET; + } + } + + if (ret == PICOQUIC_ERROR_INVALID_TOKEN && (*pcnx)->cnx_state == picoquic_state_handshake_failure) { + ret = 0; + } + + if (ret != 0 || (*pcnx)->cnx_state == picoquic_state_disconnected) { + /* This is bad. If this is an initial attempt, delete the connection */ + if (new_context_created) { + picoquic_delete_cnx(*pcnx); + *pcnx = NULL; + ret = PICOQUIC_ERROR_CONNECTION_DELETED; + } + } + + return ret; +} + +/* + * Processing of a server retry + * + * The packet number and connection ID fields echo the corresponding fields from the + * triggering client packet. This allows a client to verify that the server received its packet. + * + * A Server Stateless Retry packet is never explicitly acknowledged in an ACK frame by a client. + * Receiving another Client Initial packet implicitly acknowledges a Server Stateless Retry packet. + * + * After receiving a Server Stateless Retry packet, the client uses a new Client Initial packet + * containing the next token. In effect, the next cryptographic + * handshake message is sent on a new connection. + */ + +int picoquic_incoming_retry( + picoquic_cnx_t* cnx, + uint8_t* bytes, + picoquic_packet_header* ph, + uint64_t current_time) +{ + int ret = 0; + size_t token_length = 0; + uint8_t * token = NULL; + + if ((cnx->cnx_state != picoquic_state_client_init_sent && cnx->cnx_state != picoquic_state_client_init_resent) || + cnx->original_cnxid.id_len != 0) { + ret = PICOQUIC_ERROR_UNEXPECTED_PACKET; + } else { + /* Verify that the header is a proper echo of what was sent */ + if (ph->vn != picoquic_supported_versions[cnx->version_index].version) { + /* Packet that do not match the "echo" checks should be logged and ignored */ + ret = PICOQUIC_ERROR_UNEXPECTED_PACKET; + } else if (ph->pn64 != 0) { + /* after draft-12, PN is required to be 0 */ + ret = PICOQUIC_ERROR_UNEXPECTED_PACKET; + } + } + + if (ret == 0) { + /* Parse the retry frame */ + void * integrity_aead = picoquic_find_retry_protection_context(cnx, 0); + size_t byte_index = ph->offset; + size_t data_length = ph->offset + ph->payload_length; + + /* Assume that is aead context is null, this is the old format and the + * integrity shall be verifed by checking the ODCID */ + if (integrity_aead == NULL) { + uint8_t odcil = bytes[byte_index++]; + + if (odcil != cnx->initial_cnxid.id_len || (size_t)odcil + 1u > ph->payload_length || + memcmp(cnx->initial_cnxid.id, &bytes[byte_index], odcil) != 0) { + /* malformed ODCIL, or does not match initial cid; ignore */ + ret = PICOQUIC_ERROR_UNEXPECTED_PACKET; + picoquic_log_app_message(cnx, "Retry packet rejected: odcid check failed"); + } + else { + byte_index += odcil; + } + } + else { + ret = picoquic_verify_retry_protection(integrity_aead, bytes, &data_length, byte_index, &cnx->initial_cnxid); + + picoquic_log_app_message(cnx, "Retry packet rejected: integrity check failed"); + } + + if (ret == 0) { + token_length = data_length - byte_index; + + if (token_length > 0) { + token = malloc(token_length); + if (token == NULL) { + ret = PICOQUIC_ERROR_MEMORY; + } + else { + memcpy(token, &bytes[byte_index], token_length); + } + } + } + } + + if (ret == 0) { + /* Close the log, because it is keyed by initial_cnxid */ + picoquic_log_close_connection(cnx); + /* if this is the first reset, reset the original cid */ + if (cnx->original_cnxid.id_len == 0) { + cnx->original_cnxid = cnx->initial_cnxid; + } + /* reset the initial CNX_ID to the version sent by the server */ + cnx->initial_cnxid = ph->srce_cnx_id; + + /* keep a copy of the retry token */ + if (cnx->retry_token != NULL) { + free(cnx->retry_token); + } + cnx->retry_token = token; + cnx->retry_token_length = (uint16_t)token_length; + + picoquic_reset_cnx(cnx, current_time); + + /* Mark the packet as not required for ack */ + ret = PICOQUIC_ERROR_RETRY; + } + + return ret; +} + +/* + * Processing of a server clear text packet. + */ + +int picoquic_incoming_server_initial( + picoquic_cnx_t* cnx, + uint8_t* bytes, + struct sockaddr* addr_to, + unsigned long if_index_to, + picoquic_packet_header* ph, + uint64_t current_time) +{ + int ret = 0; + + if (cnx->cnx_state == picoquic_state_client_init_sent || cnx->cnx_state == picoquic_state_client_init_resent) { + cnx->cnx_state = picoquic_state_client_handshake_start; + } + + /* Check the server cnx id */ + if ((!picoquic_is_connection_id_null(&cnx->path[0]->remote_cnxid) || cnx->cnx_state > picoquic_state_client_handshake_start) && + picoquic_compare_connection_id(&cnx->path[0]->remote_cnxid, &ph->srce_cnx_id) != 0) { + ret = PICOQUIC_ERROR_CNXID_CHECK; /* protocol error */ + } + + if (ret == 0) { + if (cnx->cnx_state <= picoquic_state_client_handshake_start) { + /* Document local address if not present */ + if (cnx->path[0]->local_addr.ss_family == 0 && addr_to != NULL) { + picoquic_store_addr(&cnx->path[0]->local_addr, addr_to); + } + cnx->path[0]->if_index_dest = if_index_to; + /* Accept the incoming frames */ + if (ph->payload_length == 0) { + /* empty payload! */ + ret = picoquic_connection_error(cnx, PICOQUIC_TRANSPORT_PROTOCOL_VIOLATION, 0); + } + else { + ret = picoquic_decode_frames(cnx, cnx->path[0], + bytes + ph->offset, ph->payload_length, ph->epoch, NULL, addr_to, current_time); + } + + /* processing of initial packet */ + if (ret == 0) { + ret = picoquic_tls_stream_process(cnx, NULL); + } + } + else if (cnx->cnx_state < picoquic_state_ready) { + /* Require an acknowledgement if the packet contains ackable frames */ + picoquic_ignore_incoming_handshake(cnx, bytes, ph, current_time); + } + else { + /* Initial keys should have been discarded, treat packet as unexpected */ + ret = PICOQUIC_ERROR_UNEXPECTED_PACKET; + } + } + + return ret; +} + + +int picoquic_incoming_server_handshake( + picoquic_cnx_t* cnx, + uint8_t* bytes, + struct sockaddr* addr_to, + unsigned long if_index_to, + picoquic_packet_header* ph, + uint64_t current_time) +{ + int ret = 0; +#ifdef _WINDOWS + UNREFERENCED_PARAMETER(addr_to); + UNREFERENCED_PARAMETER(if_index_to); +#endif + int restricted = cnx->cnx_state != picoquic_state_client_handshake_start; + + if (picoquic_compare_connection_id(&cnx->path[0]->remote_cnxid, &ph->srce_cnx_id) != 0) { + ret = PICOQUIC_ERROR_CNXID_CHECK; /* protocol error */ + } + + + if (ret == 0) { + if (cnx->cnx_state < picoquic_state_ready) { + /* Accept the incoming frames */ + + if (ph->payload_length == 0) { + /* empty payload! */ + ret = picoquic_connection_error(cnx, PICOQUIC_TRANSPORT_PROTOCOL_VIOLATION, 0); + } + else { + ret = picoquic_decode_frames(cnx, cnx->path[0], + bytes + ph->offset, ph->payload_length, ph->epoch, NULL, addr_to, current_time); + } + + /* processing of initial packet */ + if (ret == 0 && restricted == 0) { + ret = picoquic_tls_stream_process(cnx, NULL); + } + } + else { + /* Initial keys should have been discarded, treat packet as unexpected */ + ret = PICOQUIC_ERROR_UNEXPECTED_PACKET; + } + } + + + return ret; +} +/* + * Processing of client handshake packet. + */ +int picoquic_incoming_client_handshake( + picoquic_cnx_t* cnx, + uint8_t* bytes, + picoquic_packet_header* ph, + uint64_t current_time) +{ + int ret = 0; + + cnx->initial_validated = 1; + cnx->initial_repeat_needed = 0; + + if (cnx->cnx_state < picoquic_state_server_almost_ready) { + if (picoquic_compare_connection_id(&ph->srce_cnx_id, &cnx->path[0]->remote_cnxid) != 0) { + ret = PICOQUIC_ERROR_CNXID_CHECK; + } else { + /* Accept the incoming frames */ + if (ph->payload_length == 0) { + /* empty payload! */ + ret = picoquic_connection_error(cnx, PICOQUIC_TRANSPORT_PROTOCOL_VIOLATION, 0); + } + else { + ret = picoquic_decode_frames(cnx, cnx->path[0], + bytes + ph->offset, ph->payload_length, ph->epoch, NULL, NULL, current_time); + } + /* processing of client clear text packet */ + if (ret == 0) { + /* Any successful handshake packet is an explicit ack of initial packets */ + picoquic_implicit_handshake_ack(cnx, picoquic_packet_context_initial, current_time); + picoquic_crypto_context_free(&cnx->crypto_context[picoquic_epoch_initial]); + + /* If TLS data present, progress the TLS state */ + ret = picoquic_tls_stream_process(cnx, NULL); + + /* If TLS FIN has been received, the server side handshake is ready */ + if (!cnx->client_mode && cnx->cnx_state < picoquic_state_ready && picoquic_is_tls_complete(cnx)) { + picoquic_ready_state_transition(cnx, current_time); + } + } + } + } + else if (cnx->cnx_state <= picoquic_state_ready) { + /* Because the client is never guaranteed to discard handshake keys, + * we need to keep it for the duration of the connection. + * Process the incoming frames, ignore them, but + * require an acknowledgement if the packet contains ackable frames */ + picoquic_ignore_incoming_handshake(cnx, bytes, ph, current_time); + } + else { + /* Not expected. Log and ignore. */ + ret = PICOQUIC_ERROR_UNEXPECTED_PACKET; + } + + return ret; +} + +/* +* Processing of stateless reset packet. +*/ +int picoquic_incoming_stateless_reset( + picoquic_cnx_t* cnx) +{ + /* Stateless reset. The connection should be abandonned */ + cnx->cnx_state = picoquic_state_disconnected; + + if (cnx->callback_fn) { + (void)(cnx->callback_fn)(cnx, 0, NULL, 0, picoquic_callback_stateless_reset, cnx->callback_ctx, NULL); + } + + return PICOQUIC_ERROR_AEAD_CHECK; +} + +/* + * Processing of 0-RTT packet + */ + +int picoquic_incoming_0rtt( + picoquic_cnx_t* cnx, + uint8_t* bytes, + picoquic_packet_header* ph, + uint64_t current_time) +{ + int ret = 0; + + if (!(picoquic_compare_connection_id(&ph->dest_cnx_id, &cnx->initial_cnxid) == 0 || + picoquic_compare_connection_id(&ph->dest_cnx_id, &cnx->path[0]->p_local_cnxid->cnx_id) == 0) || + picoquic_compare_connection_id(&ph->srce_cnx_id, &cnx->path[0]->remote_cnxid) != 0) { + ret = PICOQUIC_ERROR_CNXID_CHECK; + } else if (cnx->cnx_state == picoquic_state_server_almost_ready || + cnx->cnx_state == picoquic_state_server_false_start || + (cnx->cnx_state == picoquic_state_ready && !cnx->is_1rtt_received)) { + if (ph->vn != picoquic_supported_versions[cnx->version_index].version) { + ret = picoquic_connection_error(cnx, PICOQUIC_TRANSPORT_PROTOCOL_VIOLATION, 0); + } else { + /* Accept the incoming frames */ + if (ph->payload_length == 0) { + /* empty payload! */ + ret = picoquic_connection_error(cnx, PICOQUIC_TRANSPORT_PROTOCOL_VIOLATION, 0); + } + else { + cnx->nb_zero_rtt_received++; + ret = picoquic_decode_frames(cnx, cnx->path[0], + bytes + ph->offset, ph->payload_length, ph->epoch, NULL, NULL, current_time); + } + + if (ret == 0) { + /* Processing of TLS messages -- EOED */ + ret = picoquic_tls_stream_process(cnx, NULL); + } + } + } else { + /* Not expected. Log and ignore. */ + ret = PICOQUIC_ERROR_UNEXPECTED_PACKET; + } + + return ret; +} + +/* +Find path of incoming packet + +A path is defined by a pair of addresses. The path is created by the client +when it learns about a new local or remote address. It is created by the +server when it receives data from a not yet identified address pair. + +We associate a local CID with a path. This is the CID that the peer uses +to send packet. This is a loose association. When a packet is received, the +packet is associated with a path based on the address tuple. If this is a +new tuple, a new path should be created, unless too many paths have been +created already (some heuristics needed there). + +Different scenarios play here: + + - If the incoming CID has not yet been seen, we treat arrival as a + migration attempt and pursue the validation sequence. + + - If this is the same incoming CID as an existing path, we treat it + as an indication of NAT rebinding. We may need some heuristic to + decide whether this is legit or an attack. If this may be legit, we + create a new path and send challenges on both the new and the old path. + + - If this is the same tuple and a different incoming CID, we treat that + as an attempt by the peer to change the CID for privacy reason. On this + event, the server picks a new CID for the path if available. (May need + some safety there, e.g. only pick a new CID if the incoming CID sequence + is higher than the old one.) + +NAT rebinding should only happen if the address was changed in the +network, either by a NAT or by an attacker. NATs are: + + - rare but not unheard of in front of servers + + - rare with IPv6 + + - rare if the connection is sustained + +A small problem here is that the QUIC test suite include some pretty +unrealistic NAT rebinding simulations, so we cannot be too strict. In +order to pass the test suites, we will accept the first rebinding +attempt as genuine, and be more picky with the next ones. They may have +to wait until validation timers expire. + +Local CID are kept in a list, and are associated with paths by a reference. +If a local CID is retired, the reference is zeroed. When a new packet arrives +on path with a new CID, the reference is reset. + +If we cannot associate an existing path with a packet and also +cannot create a new path, we treat the packet as arriving on the +default path. +*/ + +int picoquic_find_incoming_path(picoquic_cnx_t* cnx, picoquic_packet_header* ph, + struct sockaddr* addr_from, + struct sockaddr* addr_to, + uint64_t current_time, + int* p_path_id) +{ + int ret = 0; + int partial_match_path = -1; + int nat_rebinding_path = -1; + int nat_rebinding_total = 0; + int path_id = picoquic_find_path_by_address(cnx, addr_to, addr_from, &partial_match_path); + + if (path_id < 0 && partial_match_path >= 0) { + /* Document the source address and promote to full match. */ + path_id = partial_match_path; + picoquic_store_addr(&cnx->path[path_id]->local_addr, addr_to); + } + + if (path_id >= 0) { + /* Packet arriving on an existing path */ + if (cnx->path[path_id]->p_local_cnxid == NULL) { + /* First packet from the peer. Remember the CNX ID. No further action */ + cnx->path[path_id]->p_local_cnxid = picoquic_find_local_cnxid(cnx, &ph->dest_cnx_id); + } else if (picoquic_compare_connection_id(&cnx->path[path_id]->p_local_cnxid->cnx_id, &ph->dest_cnx_id) != 0) { + /* The peer switched to a new CID */ + cnx->path[path_id]->p_local_cnxid = picoquic_find_local_cnxid(cnx, &ph->dest_cnx_id); + if (cnx->client_mode == 0 && cnx->cnxid_stash_first != NULL && path_id == 0) { + /* If on a server, dereference the current CID, and pick a new one */ + (void)picoquic_renew_connection_id(cnx, path_id); + } + } + } + else { + /* No valid path. Need to create one, but only if this is + * within our resource boundaries. This is the place where + * we might want to do some heuristics. + * + * Check whether this is a duplicate of an existing path. + */ + + for (int i = 0; i < cnx->nb_paths; i++) { + if (cnx->path[i]->p_local_cnxid != NULL && + picoquic_compare_connection_id(&cnx->path[i]->p_local_cnxid->cnx_id, &ph->dest_cnx_id) == 0) { + if (nat_rebinding_total == 0) { + nat_rebinding_path = i; + } + nat_rebinding_total++; + } + } + + if (cnx->nb_paths < PICOQUIC_NB_PATH_TARGET + && picoquic_create_path(cnx, current_time, addr_to, addr_from) > 0) { + /* The peer is probing for a new path, or there was a path rebinding */ + path_id = cnx->nb_paths - 1; + + if (!cnx->client_mode && cnx->local_parameters.prefered_address.is_defined) { + struct sockaddr_storage dest_addr; + + memset(&dest_addr, 0, sizeof(struct sockaddr_storage)); + + /* program a migration. */ + if (addr_to->sa_family== AF_INET) { + /* configure an IPv4 sockaddr */ + struct sockaddr_in* d4 = (struct sockaddr_in*) & dest_addr; + d4->sin_family = AF_INET; + d4->sin_port = htons(cnx->local_parameters.prefered_address.ipv4Port); + memcpy(&d4->sin_addr, cnx->local_parameters.prefered_address.ipv4Address, 4); + } else if (addr_to->sa_family == AF_INET6){ + /* configure an IPv6 sockaddr */ + struct sockaddr_in6* d6 = (struct sockaddr_in6*) & dest_addr; + d6->sin6_family = AF_INET6; + d6->sin6_port = htons(cnx->local_parameters.prefered_address.ipv6Port); + memcpy(&d6->sin6_addr, cnx->local_parameters.prefered_address.ipv6Address, 16); + } + if (picoquic_compare_addr(addr_to, (struct sockaddr*) & dest_addr) == 0) { + cnx->path[path_id]->path_is_preferred_path = 1; + } + } + + if (picoquic_assign_peer_cnxid_to_path(cnx, path_id) != 0){ + /* Copy the destination ID from an existing path */ + int alt_path = (nat_rebinding_path >= 0) ? nat_rebinding_path : 0; + cnx->path[path_id]->remote_cnxid = cnx->path[alt_path]->remote_cnxid; + cnx->path[path_id]->remote_cnxid_sequence = cnx->path[alt_path]->remote_cnxid_sequence; + memcpy(cnx->path[path_id]->reset_secret, cnx->path[alt_path]->reset_secret, + PICOQUIC_RESET_SECRET_SIZE); + } + + cnx->path[path_id]->path_is_published = 1; + cnx->path[path_id]->p_local_cnxid = picoquic_find_local_cnxid(cnx, &ph->dest_cnx_id); + picoquic_register_path(cnx, cnx->path[path_id]); + picoquic_set_path_challenge(cnx, path_id, current_time); + + /* If this is a NAT rebinding, also set a challenge on the original path */ + if (nat_rebinding_path >= 0) { + /* Treat this as a NAT rebinding. Mark the old path for validation */ + picoquic_set_path_challenge(cnx, nat_rebinding_path, current_time); + } + } + else { + DBG_PRINTF("%s", "Cannot create new path for incoming packet"); + if (nat_rebinding_path >= 0) { + path_id = nat_rebinding_path; + } + else { + path_id = 0; + } + } + } + +#if 1 + if (cnx->nb_paths > 2 && cnx->path[1] == cnx->path[2]) { + DBG_PRINTF("%s", "Bug"); + } +#endif + + *p_path_id = path_id; + + return ret; +} + +/* + * ECN Accounting. This is only called if the packet was processed successfully. + */ +void picoquic_ecn_accounting(picoquic_cnx_t* cnx, + unsigned char received_ecn, picoquic_packet_context_enum pc) +{ + switch (received_ecn & 0x03) { + case 0x00: + break; + case 0x01: /* ECN_ECT_1 */ + cnx->pkt_ctx[pc].ecn_ect1_total_local++; + cnx->pkt_ctx[pc].sending_ecn_ack |= 1; + break; + case 0x02: /* ECN_ECT_0 */ + cnx->pkt_ctx[pc].ecn_ect0_total_local++; + cnx->pkt_ctx[pc].sending_ecn_ack |= 1; + break; + case 0x03: /* ECN_CE */ + cnx->pkt_ctx[pc].ecn_ce_total_local++; + cnx->pkt_ctx[pc].sending_ecn_ack |= 1; + break; + } +} + +/* + * Processing of client encrypted packet. + */ +int picoquic_incoming_encrypted( + picoquic_cnx_t* cnx, + uint8_t* bytes, + picoquic_packet_header* ph, + struct sockaddr* addr_from, + struct sockaddr* addr_to, + int if_index_to, + unsigned char received_ecn, + uint64_t current_time) +{ + int ret = 0; + int path_id = -1; + + /* Check the packet */ + if (cnx->cnx_state < picoquic_state_client_almost_ready) { + /* handshake is not complete. Just ignore the packet */ + ret = PICOQUIC_ERROR_UNEXPECTED_PACKET; + } + else if (cnx->cnx_state == picoquic_state_disconnected) { + /* Connection is disconnected. Just ignore the packet */ + ret = PICOQUIC_ERROR_UNEXPECTED_PACKET; + } + else { + /* Packet is correct */ + + /* TODO: consider treatment of migration during closing mode */ + + /* Do not process data in closing or draining modes */ + if (cnx->cnx_state >= picoquic_state_closing_received) { + /* only look for closing frames in closing modes */ + if (cnx->cnx_state == picoquic_state_closing) { + int closing_received = 0; + + ret = picoquic_decode_closing_frames( + bytes + ph->offset, ph->payload_length, &closing_received); + + if (ret == 0) { + if (closing_received) { + if (cnx->client_mode) { + cnx->cnx_state = picoquic_state_disconnected; + } + else { + cnx->cnx_state = picoquic_state_draining; + } + } + else { + picoquic_set_ack_needed(cnx, current_time, ph->pc); + } + } + } + else { + /* Just ignore the packets in closing received or draining mode */ + ret = PICOQUIC_ERROR_UNEXPECTED_PACKET; + } + } + else { + if (ph->payload_length == 0) { + /* empty payload! */ + ret = picoquic_connection_error(cnx, PICOQUIC_TRANSPORT_PROTOCOL_VIOLATION, 0); + } + else if (ph->has_reserved_bit_set) { + /* Reserved bits were not set to zero */ + ret = picoquic_connection_error(cnx, PICOQUIC_TRANSPORT_PROTOCOL_VIOLATION, 0); + } + else { + /* Find the arrival path and update its state */ + ret = picoquic_find_incoming_path(cnx, ph, addr_from, addr_to, current_time, &path_id); + } + + if (ret == 0) { + picoquic_path_t * path_x = cnx->path[path_id]; + + path_x->if_index_dest = if_index_to; + cnx->is_1rtt_received = 1; + picoquic_spin_function_table[cnx->spin_policy].spinbit_incoming(cnx, path_x, ph); + /* Accept the incoming frames */ + ret = picoquic_decode_frames(cnx, cnx->path[path_id], + bytes + ph->offset, ph->payload_length, ph->epoch, addr_from, addr_to, current_time); + } + + if (ret == 0) { + /* Compute receive bandwidth */ + cnx->path[path_id]->received += ph->offset + ph->payload_length + + picoquic_get_checksum_length(cnx, picoquic_epoch_1rtt); + if (cnx->path[path_id]->receive_rate_epoch == 0) { + cnx->path[path_id]->received_prior = cnx->path[path_id]->received; + cnx->path[path_id]->receive_rate_epoch = current_time; + } + else { + uint64_t delta = current_time - cnx->path[path_id]->receive_rate_epoch; + if (delta > cnx->path[path_id]->smoothed_rtt && delta > PICOQUIC_BANDWIDTH_TIME_INTERVAL_MIN) { + cnx->path[path_id]->receive_rate_estimate = ((cnx->path[path_id]->received - cnx->path[path_id]->received_prior)*1000000) / delta; + cnx->path[path_id]->received_prior = cnx->path[path_id]->received; + cnx->path[path_id]->receive_rate_epoch = current_time; + if (cnx->path[path_id]->receive_rate_estimate > cnx->path[path_id]->receive_rate_max) { + cnx->path[path_id]->receive_rate_max = cnx->path[path_id]->receive_rate_estimate; + if (path_id == 0 && !cnx->is_ack_frequency_negotiated) { + cnx->ack_gap_remote = picoquic_compute_ack_gap(cnx, cnx->path[0]->receive_rate_max); + } + } + } + } + + /* Processing of TLS messages */ + ret = picoquic_tls_stream_process(cnx, NULL); + } + + if (ret == 0 && picoquic_cnx_is_still_logging(cnx)) { + picoquic_log_cc_dump(cnx, current_time); + } + } + } + + return ret; +} + +/* Processing of packets received before they could be fully decrypted + */ +int picoquic_incoming_not_decrypted( + picoquic_cnx_t* cnx, + picoquic_packet_header* ph, + uint64_t current_time, + uint8_t * bytes, + size_t length, + struct sockaddr * addr_from, + struct sockaddr* addr_to, + int if_index_to, + unsigned char received_ecn) +{ + int buffered = 0; + + if (cnx->cnx_state < picoquic_state_ready) { + if (cnx->path[0]->p_local_cnxid->cnx_id.id_len > 0 && + picoquic_compare_connection_id(&cnx->path[0]->p_local_cnxid->cnx_id, &ph->dest_cnx_id) == 0) + { + /* verifying the destination cnx id is a strong hint that the peer is responding */ + if (cnx->path[0]->smoothed_rtt == PICOQUIC_INITIAL_RTT + && cnx->path[0]->rtt_variant == 0 && + current_time - cnx->start_time < cnx->path[0]->smoothed_rtt) { + /* We received a first packet from the peer! */ + picoquic_update_path_rtt(cnx, cnx->path[0], cnx->start_time, current_time, 0); + } + + if (length <= PICOQUIC_MAX_PACKET_SIZE && + ((ph->ptype == picoquic_packet_handshake && cnx->client_mode) || ph->ptype == picoquic_packet_1rtt_protected)) { + /* stash a copy of the incoming message for processing once the keys are available */ + picoquic_stateless_packet_t* packet = picoquic_create_stateless_packet(cnx->quic); + + if (packet != NULL) { + packet->length = length; + packet->ptype = ph->ptype; + memcpy(packet->bytes, bytes, length); + packet->next_packet = cnx->first_sooner; + cnx->first_sooner = packet; + picoquic_store_addr(&packet->addr_local, addr_to); + picoquic_store_addr(&packet->addr_to, addr_from); + packet->if_index_local = if_index_to; + packet->received_ecn = received_ecn; + packet->receive_time = current_time; + buffered = 1; + } + } + } + } + + return buffered; +} + +/* +* Processing of the packet that was just received from the network. +*/ + +int picoquic_incoming_segment( + picoquic_quic_t* quic, + uint8_t* bytes, + size_t length, + size_t packet_length, + size_t* consumed, + struct sockaddr* addr_from, + struct sockaddr* addr_to, + int if_index_to, + unsigned char received_ecn, + uint64_t current_time, + uint64_t receive_time, + picoquic_connection_id_t* previous_dest_id) +{ + int ret = 0; + picoquic_cnx_t* cnx = NULL; + picoquic_packet_header ph; + int new_context_created = 0; + int is_first_segment = 0; + int is_buffered = 0; + + /* Parse the header and decrypt the segment */ + ret = picoquic_parse_header_and_decrypt(quic, bytes, length, packet_length, addr_from, + current_time, &ph, &cnx, consumed, &new_context_created); + + /* Verify that the segment coalescing is for the same destination ID */ + if (picoquic_is_connection_id_null(previous_dest_id)) { + /* This is the first segment in the incoming packet */ + *previous_dest_id = ph.dest_cnx_id; + is_first_segment = 1; + + + /* if needed, log that the packet is received */ + if (cnx != NULL) { + picoquic_log_pdu(cnx, 1, current_time, addr_from, addr_to, packet_length); + } + else { + picoquic_log_quic_pdu(quic, 1, current_time, picoquic_val64_connection_id(ph.dest_cnx_id), + addr_from, addr_to, packet_length); + } + } + else { + if (ret == 0 && picoquic_compare_connection_id(previous_dest_id, &ph.dest_cnx_id) != 0) { + ret = PICOQUIC_ERROR_CNXID_SEGMENT; + } + } + /* Store packet if received in advance of encryption keys */ + if (ret == PICOQUIC_ERROR_AEAD_NOT_READY && + cnx != NULL) { + is_buffered = picoquic_incoming_not_decrypted(cnx, &ph, current_time, bytes, length, addr_from, addr_to, if_index_to, received_ecn); + } + + /* Log the incoming packet */ + if (cnx != NULL) { + if (ret == 0) { + picoquic_log_packet(cnx, 1, current_time, &ph, bytes, *consumed); + } + else if (is_buffered) { + picoquic_log_buffered_packet(cnx, ph.ptype, current_time); + } else { + picoquic_log_dropped_packet(cnx, &ph, length, ret, bytes, current_time); + } + } + + if (ret == PICOQUIC_ERROR_VERSION_NOT_SUPPORTED) { + if (packet_length >= PICOQUIC_ENFORCED_INITIAL_MTU) { + /* use the result of parsing to consider version negotiation */ + picoquic_prepare_version_negotiation(quic, addr_from, addr_to, if_index_to, &ph, bytes); + } + } else if (ret == 0) { + if (cnx == NULL) { + /* Unexpected packet. Reject, drop and log. */ + if (!picoquic_is_connection_id_null(&ph.dest_cnx_id)) { + picoquic_process_unexpected_cnxid(quic, length, addr_from, addr_to, if_index_to, &ph); + } + ret = PICOQUIC_ERROR_DETECTED; + } + else { + cnx->quic_bit_received_0 |= ph.quic_bit_is_zero; + switch (ph.ptype) { + case picoquic_packet_version_negotiation: + ret = picoquic_incoming_version_negotiation( + cnx, bytes, length, addr_from, &ph, current_time); + break; + case picoquic_packet_initial: + /* Initial packet: either crypto handshakes or acks. */ + if ((!cnx->client_mode && picoquic_compare_connection_id(&ph.dest_cnx_id, &cnx->initial_cnxid) == 0) || + picoquic_compare_connection_id(&ph.dest_cnx_id, &cnx->path[0]->p_local_cnxid->cnx_id) == 0) { + /* Verify that the source CID matches expectation */ + if (picoquic_is_connection_id_null(&cnx->path[0]->remote_cnxid)) { + cnx->path[0]->remote_cnxid = ph.srce_cnx_id; + } else if (picoquic_compare_connection_id(&cnx->path[0]->remote_cnxid, &ph.srce_cnx_id) != 0) { + DBG_PRINTF("Error wrong srce cnxid (%d), type: %d, epoch: %d, pc: %d, pn: %d\n", + cnx->client_mode, ph.ptype, ph.epoch, ph.pc, (int)ph.pn); + ret = PICOQUIC_ERROR_UNEXPECTED_PACKET; + } + if (ret == 0) { + if (cnx->client_mode == 0) { + if (is_first_segment) { + /* Account for the data received in handshake, but only + * count the packet once. Do not count it again if it is not + * the first segment in packet */ + cnx->initial_data_received += packet_length; + } + ret = picoquic_incoming_client_initial(&cnx, bytes, packet_length, + addr_from, addr_to, if_index_to, &ph, current_time, new_context_created); + } + else { + /* TODO: this really depends on the current receive epoch */ + ret = picoquic_incoming_server_initial(cnx, bytes, addr_to, if_index_to, &ph, current_time); + } + } + } else { + DBG_PRINTF("Error detected (%d), type: %d, epoch: %d, pc: %d, pn: %d\n", + cnx->client_mode, ph.ptype, ph.epoch, ph.pc, (int)ph.pn); + ret = PICOQUIC_ERROR_DETECTED; + } + break; + case picoquic_packet_retry: + ret = picoquic_incoming_retry(cnx, bytes, &ph, current_time); + break; + case picoquic_packet_handshake: + if (cnx->client_mode) + { + ret = picoquic_incoming_server_handshake(cnx, bytes, addr_to, if_index_to, &ph, current_time); + } + else + { + ret = picoquic_incoming_client_handshake(cnx, bytes, &ph, current_time); + } + break; + case picoquic_packet_0rtt_protected: + if (is_first_segment) { + /* Account for the data received in handshake, but only + * count the packet once. Do not count it again if it is not + * the first segment in packet */ + cnx->initial_data_received += packet_length; + } + ret = picoquic_incoming_0rtt(cnx, bytes, &ph, current_time); + break; + case picoquic_packet_1rtt_protected: + ret = picoquic_incoming_encrypted(cnx, bytes, &ph, addr_from, addr_to, if_index_to, received_ecn, current_time); + break; + default: + /* Packet type error. Log and ignore */ + DBG_PRINTF("Unexpected packet type (%d), type: %d, epoch: %d, pc: %d, pn: %d\n", + cnx->client_mode, ph.ptype, ph.epoch, ph.pc, (int) ph.pn); + ret = PICOQUIC_ERROR_DETECTED; + break; + } + } + } else if (ret == PICOQUIC_ERROR_STATELESS_RESET) { + ret = picoquic_incoming_stateless_reset(cnx); + } + else if (ret == PICOQUIC_ERROR_AEAD_CHECK && + ph.ptype == picoquic_packet_handshake && + cnx != NULL && + (cnx->cnx_state == picoquic_state_client_init_sent || cnx->cnx_state == picoquic_state_client_init_resent)) + { + /* Indicates that the server probably sent initial and handshake but initial was lost */ + if (cnx->pkt_ctx[picoquic_packet_context_initial].retransmit_oldest != NULL && + cnx->pkt_ctx[picoquic_packet_context_initial].nb_retransmit == 0) { + /* Reset the retransmit timer to start retransmission immediately */ + cnx->path[0]->retransmit_timer = current_time - + cnx->pkt_ctx[picoquic_packet_context_initial].retransmit_oldest->send_time; + } + } + + if (ret == 0) { + if (cnx != NULL && cnx->cnx_state != picoquic_state_disconnected && + ph.ptype != picoquic_packet_version_negotiation) { + cnx->nb_packets_received++; + /* Mark the sequence number as received */ + ret = picoquic_record_pn_received(cnx, ph.pc, ph.pn64, receive_time); + /* Perform ECN accounting */ + picoquic_ecn_accounting(cnx, received_ecn, ph.pc); + } + if (cnx != NULL) { + picoquic_reinsert_by_wake_time(cnx->quic, cnx, current_time); + } + } else if (ret == PICOQUIC_ERROR_DUPLICATE) { + /* Bad packets are dropped silently, but duplicates should be acknowledged */ + if (cnx != NULL) { + picoquic_set_ack_needed(cnx, current_time, ph.pc); + } + ret = -1; + } else if (ret == PICOQUIC_ERROR_AEAD_CHECK || ret == PICOQUIC_ERROR_INITIAL_TOO_SHORT || + ret == PICOQUIC_ERROR_INITIAL_CID_TOO_SHORT || + ret == PICOQUIC_ERROR_UNEXPECTED_PACKET || + ret == PICOQUIC_ERROR_CNXID_CHECK || + ret == PICOQUIC_ERROR_RETRY || ret == PICOQUIC_ERROR_DETECTED || + ret == PICOQUIC_ERROR_CONNECTION_DELETED || + ret == PICOQUIC_ERROR_CNXID_SEGMENT || + ret == PICOQUIC_ERROR_VERSION_NOT_SUPPORTED || + ret == PICOQUIC_ERROR_AEAD_NOT_READY) { + /* Bad packets are dropped silently */ + if (ret == PICOQUIC_ERROR_AEAD_CHECK || + ret == PICOQUIC_ERROR_AEAD_NOT_READY || + ret == PICOQUIC_ERROR_VERSION_NOT_SUPPORTED) { + ret = 0; + } + else { + ret = -1; + } + if (cnx != NULL) { + picoquic_reinsert_by_wake_time(cnx->quic, cnx, current_time); + } + } else if (ret == 1) { + /* wonder what happened ! */ + DBG_PRINTF("Packet (%d) get ret=1, t: %d, e: %d, pc: %d, pn: %d, l: %zu\n", + (cnx == NULL) ? -1 : cnx->client_mode, ph.ptype, ph.epoch, ph.pc, (int)ph.pn, length); + ret = -1; + } + else if (ret != 0) { + DBG_PRINTF("Packet (%d) error, t: %d, e: %d, pc: %d, pn: %d, l: %zu, ret : 0x%x\n", + (cnx == NULL) ? -1 : cnx->client_mode, ph.ptype, ph.epoch, ph.pc, (int)ph.pn, length, ret); + ret = -1; + } + + return ret; +} + +int picoquic_incoming_packet( + picoquic_quic_t* quic, + uint8_t* bytes, + size_t packet_length, + struct sockaddr* addr_from, + struct sockaddr* addr_to, + int if_index_to, + unsigned char received_ecn, + uint64_t current_time) +{ + size_t consumed_index = 0; + int ret = 0; + picoquic_connection_id_t previous_destid = picoquic_null_connection_id; + + + while (consumed_index < packet_length) { + size_t consumed = 0; + + ret = picoquic_incoming_segment(quic, bytes + consumed_index, + packet_length - consumed_index, packet_length, + &consumed, addr_from, addr_to, if_index_to, received_ecn, current_time, current_time, &previous_destid); + + if (ret == 0) { + consumed_index += consumed; + if (consumed == 0) { + DBG_PRINTF("%s", "Receive bug, ret = 0 && consumed = 0\n"); + break; + } + } else { + ret = 0; + break; + } + } + + return ret; +} + +/* Processing of stashed packets after acquiring encryption context */ +void picoquic_process_sooner_packets(picoquic_cnx_t* cnx, uint64_t current_time) +{ + picoquic_stateless_packet_t* packet = cnx->first_sooner; + picoquic_stateless_packet_t* previous = NULL; + + cnx->recycle_sooner_needed = 0; + + while (packet != NULL) { + picoquic_stateless_packet_t* next_packet = packet->next_packet; + int could_try_now = 1; + picoquic_epoch_enum epoch = 0; + switch (packet->ptype) { + case picoquic_packet_handshake: + epoch = picoquic_epoch_handshake; + break; + case picoquic_packet_1rtt_protected: + epoch = picoquic_epoch_1rtt; + break; + default: + could_try_now = 0; + break; + } + + if (could_try_now && + (cnx->crypto_context[epoch].aead_decrypt != NULL || cnx->crypto_context[epoch].pn_dec != NULL)) + { +#if 0 + int ret; + + DBG_PRINTF("De-stashing packet type %d, %d bytes", (int)packet->ptype, (int)packet->length); + ret = picoquic_incoming_packet(cnx->quic, packet->bytes, packet->length, + (struct sockaddr*) & packet->addr_to, (struct sockaddr*) & packet->addr_local, packet->if_index_local, packet->received_ecn, current_time); +#else + + size_t consumed_index = 0; + int ret = 0; + picoquic_connection_id_t previous_destid = picoquic_null_connection_id; + + + while (consumed_index < packet->length) { + size_t consumed = 0; + + ret = picoquic_incoming_segment(cnx->quic, packet->bytes + consumed_index, + packet->length - consumed_index, packet->length, + &consumed, (struct sockaddr*) & packet->addr_to, (struct sockaddr*) & packet->addr_local, packet->if_index_local, + packet->received_ecn, current_time, packet->receive_time, &previous_destid); + + if (ret == 0 && consumed > 0) { + consumed_index += consumed; + } + else { + break; + } + } +#endif + if (ret != 0) { + DBG_PRINTF("Processing sooner packet type %d returns %d (0x%d)", (int)packet->ptype, ret, ret); + } + + if (previous == NULL) { + cnx->first_sooner = packet->next_packet; + } + else { + previous->next_packet = packet->next_packet; + } + picoquic_delete_stateless_packet(packet); + } + else { + previous = packet; + } + + packet = next_packet; + } +} \ No newline at end of file diff --git a/deps/picoquic/picohash.c b/deps/picoquic/picohash.c new file mode 100644 index 0000000..8181907 --- /dev/null +++ b/deps/picoquic/picohash.c @@ -0,0 +1,166 @@ +/* +* Author: Christian Huitema +* Copyright (c) 2017, Private Octopus, Inc. +* All rights reserved. +* +* Permission to use, copy, modify, and distribute this software for any +* purpose with or without fee is hereby granted, provided that the above +* copyright notice and this permission notice appear in all copies. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL Private Octopus, Inc. BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/* + * Basic hash implementation, like we have seen tons off already. + */ +#include "picohash.h" +#include +#include + +picohash_table* picohash_create(size_t nb_bin, + uint64_t (*picohash_hash)(const void*), + int (*picohash_compare)(const void*, const void*)) +{ + picohash_table* t = (picohash_table*)malloc(sizeof(picohash_table)); + if (t != NULL) { + t->hash_bin = (picohash_item**)malloc(sizeof(picohash_item*) * nb_bin); + + if (t->hash_bin == NULL) { + free(t); + t = NULL; + } else { + (void)memset(t->hash_bin, 0, sizeof(picohash_item*) * nb_bin); + t->nb_bin = nb_bin; + t->count = 0; + t->picohash_hash = picohash_hash; + t->picohash_compare = picohash_compare; + } + } + + return t; +} + +picohash_item* picohash_retrieve(picohash_table* hash_table, const void* key) +{ + uint64_t hash = hash_table->picohash_hash(key); + uint32_t bin = (uint32_t)(hash % hash_table->nb_bin); + picohash_item* item = hash_table->hash_bin[bin]; + + while (item != NULL) { + if (hash_table->picohash_compare(key, item->key) == 0) { + break; + } else { + item = item->next_in_bin; + } + } + + return item; +} + +int picohash_insert(picohash_table* hash_table, const void* key) +{ + uint64_t hash = hash_table->picohash_hash(key); + uint32_t bin = (uint32_t)(hash % hash_table->nb_bin); + int ret = 0; + picohash_item* item = (picohash_item*)malloc(sizeof(picohash_item)); + + if (item == NULL) { + ret = -1; + } else { + item->hash = hash; + item->key = key; + item->next_in_bin = hash_table->hash_bin[bin]; + hash_table->hash_bin[bin] = item; + hash_table->count++; + } + + return ret; +} + +void picohash_delete_item(picohash_table* hash_table, picohash_item* item, int delete_key_too) +{ + uint32_t bin = (uint32_t)(item->hash % hash_table->nb_bin); + picohash_item* previous = hash_table->hash_bin[bin]; + + if (previous == item) { + hash_table->hash_bin[bin] = item->next_in_bin; + hash_table->count--; + } else { + while (previous != NULL) { + if (previous->next_in_bin == item) { + previous->next_in_bin = item->next_in_bin; + hash_table->count--; + break; + } else { + previous = previous->next_in_bin; + } + } + } + + if (delete_key_too) { + free((void*)item->key); + } + + free(item); +} + +void picohash_delete_key(picohash_table* hash_table, void* key, int delete_key_too) +{ + picohash_item* item = picohash_retrieve(hash_table, key); + + if (item != NULL) { + picohash_delete_item(hash_table, item, delete_key_too); + } + else if (delete_key_too) { + free(key); + } +} + +void picohash_delete(picohash_table* hash_table, int delete_key_too) +{ + for (uint32_t i = 0; i < hash_table->nb_bin; i++) { + picohash_item* item = hash_table->hash_bin[i]; + while (item != NULL) { + picohash_item* tmp = item; + item = tmp->next_in_bin; + + if (delete_key_too) { + free((void*)tmp->key); + } + free(tmp); + } + } + + free(hash_table->hash_bin); + free(hash_table); +} + +uint64_t picohash_hash_mix(uint64_t hash, uint64_t h2) +{ + h2 ^= (hash << 17) ^ (hash >> 37); + hash ^= ((h2 << 31) ^ (h2 >> 17)); + + return hash; +} + +uint64_t picohash_bytes(const uint8_t* key, uint32_t length) +{ + uint64_t hash = 0xDEADBEEF; + + for (uint32_t i = 0; i < length; i++) { + hash ^= key[i]; + hash ^= ((hash << 31) ^ (hash >> 17)); + } + + return hash; +} + diff --git a/deps/picoquic/picohash.h b/deps/picoquic/picohash.h new file mode 100644 index 0000000..65df80c --- /dev/null +++ b/deps/picoquic/picohash.h @@ -0,0 +1,73 @@ +/* +* Author: Christian Huitema +* Copyright (c) 2017, Private Octopus, Inc. +* All rights reserved. +* +* Permission to use, copy, modify, and distribute this software for any +* purpose with or without fee is hereby granted, provided that the above +* copyright notice and this permission notice appear in all copies. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL Private Octopus, Inc. BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/* + * Context hash. + * Retrieve an object based on a hash of a context ID, or alternatively based on + * source address and port number. + */ +#ifndef PICOHASH_H +#define PICOHASH_H +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct _picohash_item { + uint64_t hash; + struct _picohash_item* next_in_bin; + const void* key; +} picohash_item; + +typedef struct picohash_table { + /* TODO: lock ! */ + picohash_item** hash_bin; + size_t nb_bin; + size_t count; + uint64_t (*picohash_hash)(const void*); + int (*picohash_compare)(const void*, const void*); +} picohash_table; + +picohash_table* picohash_create(size_t nb_bin, + uint64_t (*picohash_hash)(const void*), + int (*picohash_compute)(const void*, const void*)); + +picohash_item* picohash_retrieve(picohash_table* hash_table, const void* key); + +int picohash_insert(picohash_table* hash_table, const void* key); + +void picohash_delete_item(picohash_table* hash_table, picohash_item* item, int delete_key_too); + +void picohash_delete_key(picohash_table* hash_table, void* key, int delete_key_too); + +void picohash_delete(picohash_table* hash_table, int delete_key_too); + +uint64_t picohash_hash_mix(uint64_t hash, uint64_t h2); + +uint64_t picohash_bytes(const uint8_t* key, uint32_t length); + +#ifdef __cplusplus +} +#endif + +#endif /* PICOHASH_H */ diff --git a/deps/picoquic/picoquic.h b/deps/picoquic/picoquic.h new file mode 100644 index 0000000..689ec72 --- /dev/null +++ b/deps/picoquic/picoquic.h @@ -0,0 +1,950 @@ +/* +* Author: Christian Huitema +* Copyright (c) 2017, Private Octopus, Inc. +* All rights reserved. +* +* Permission to use, copy, modify, and distribute this software for any +* purpose with or without fee is hereby granted, provided that the above +* copyright notice and this permission notice appear in all copies. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL Private Octopus, Inc. BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef PICOQUIC_H +#define PICOQUIC_H + +#include +#ifdef _WINDOWS +#include +#include +#include +#else +#include +#include +#include +#include +#include +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#define PICOQUIC_ERROR_CLASS 0x400 +#define PICOQUIC_ERROR_DUPLICATE (PICOQUIC_ERROR_CLASS + 1) +#define PICOQUIC_ERROR_AEAD_CHECK (PICOQUIC_ERROR_CLASS + 3) +#define PICOQUIC_ERROR_UNEXPECTED_PACKET (PICOQUIC_ERROR_CLASS + 4) +#define PICOQUIC_ERROR_MEMORY (PICOQUIC_ERROR_CLASS + 5) +#if 0 +#define PICOQUIC_ERROR_SPURIOUS_REPEAT (PICOQUIC_ERROR_CLASS + 6) +#endif +#define PICOQUIC_ERROR_CNXID_CHECK (PICOQUIC_ERROR_CLASS + 7) +#define PICOQUIC_ERROR_INITIAL_TOO_SHORT (PICOQUIC_ERROR_CLASS + 8) +#define PICOQUIC_ERROR_VERSION_NEGOTIATION_SPOOFED (PICOQUIC_ERROR_CLASS + 9) +#define PICOQUIC_ERROR_MALFORMED_TRANSPORT_EXTENSION (PICOQUIC_ERROR_CLASS + 10) +#define PICOQUIC_ERROR_EXTENSION_BUFFER_TOO_SMALL (PICOQUIC_ERROR_CLASS + 11) +#define PICOQUIC_ERROR_ILLEGAL_TRANSPORT_EXTENSION (PICOQUIC_ERROR_CLASS + 12) +#define PICOQUIC_ERROR_CANNOT_RESET_STREAM_ZERO (PICOQUIC_ERROR_CLASS + 13) +#define PICOQUIC_ERROR_INVALID_STREAM_ID (PICOQUIC_ERROR_CLASS + 14) +#define PICOQUIC_ERROR_STREAM_ALREADY_CLOSED (PICOQUIC_ERROR_CLASS + 15) +#define PICOQUIC_ERROR_FRAME_BUFFER_TOO_SMALL (PICOQUIC_ERROR_CLASS + 16) +#define PICOQUIC_ERROR_INVALID_FRAME (PICOQUIC_ERROR_CLASS + 17) +#define PICOQUIC_ERROR_CANNOT_CONTROL_STREAM_ZERO (PICOQUIC_ERROR_CLASS + 18) +#define PICOQUIC_ERROR_RETRY (PICOQUIC_ERROR_CLASS + 19) +#define PICOQUIC_ERROR_DISCONNECTED (PICOQUIC_ERROR_CLASS + 20) +#define PICOQUIC_ERROR_DETECTED (PICOQUIC_ERROR_CLASS + 21) +#define PICOQUIC_ERROR_INVALID_TICKET (PICOQUIC_ERROR_CLASS + 23) +#define PICOQUIC_ERROR_INVALID_FILE (PICOQUIC_ERROR_CLASS + 24) +#define PICOQUIC_ERROR_SEND_BUFFER_TOO_SMALL (PICOQUIC_ERROR_CLASS + 25) +#define PICOQUIC_ERROR_UNEXPECTED_STATE (PICOQUIC_ERROR_CLASS + 26) +#define PICOQUIC_ERROR_UNEXPECTED_ERROR (PICOQUIC_ERROR_CLASS + 27) +#define PICOQUIC_ERROR_TLS_SERVER_CON_WITHOUT_CERT (PICOQUIC_ERROR_CLASS + 28) +#define PICOQUIC_ERROR_NO_SUCH_FILE (PICOQUIC_ERROR_CLASS + 29) +#define PICOQUIC_ERROR_STATELESS_RESET (PICOQUIC_ERROR_CLASS + 30) +#define PICOQUIC_ERROR_CONNECTION_DELETED (PICOQUIC_ERROR_CLASS + 31) +#define PICOQUIC_ERROR_CNXID_SEGMENT (PICOQUIC_ERROR_CLASS + 32) +#define PICOQUIC_ERROR_CNXID_NOT_AVAILABLE (PICOQUIC_ERROR_CLASS + 33) +#define PICOQUIC_ERROR_MIGRATION_DISABLED (PICOQUIC_ERROR_CLASS + 34) +#define PICOQUIC_ERROR_CANNOT_COMPUTE_KEY (PICOQUIC_ERROR_CLASS + 35) +#define PICOQUIC_ERROR_CANNOT_SET_ACTIVE_STREAM (PICOQUIC_ERROR_CLASS + 36) +#define PICOQUIC_ERROR_CANNOT_CHANGE_ACTIVE_CONTEXT (PICOQUIC_ERROR_CLASS + 37) +#define PICOQUIC_ERROR_INVALID_TOKEN (PICOQUIC_ERROR_CLASS + 38) +#define PICOQUIC_ERROR_INITIAL_CID_TOO_SHORT (PICOQUIC_ERROR_CLASS + 39) +#define PICOQUIC_ERROR_KEY_ROTATION_NOT_READY (PICOQUIC_ERROR_CLASS + 40) +#define PICOQUIC_ERROR_AEAD_NOT_READY (PICOQUIC_ERROR_CLASS + 41) +#define PICOQUIC_ERROR_NO_ALPN_PROVIDED (PICOQUIC_ERROR_CLASS + 42) +#define PICOQUIC_ERROR_NO_CALLBACK_PROVIDED (PICOQUIC_ERROR_CLASS + 43) +#define PICOQUIC_STREAM_RECEIVE_COMPLETE (PICOQUIC_ERROR_CLASS + 44) +#define PICOQUIC_ERROR_PACKET_HEADER_PARSING (PICOQUIC_ERROR_CLASS + 45) +#define PICOQUIC_ERROR_QUIC_BIT_MISSING (PICOQUIC_ERROR_CLASS + 46) +#define PICOQUIC_NO_ERROR_TERMINATE_PACKET_LOOP (PICOQUIC_ERROR_CLASS + 47) +#define PICOQUIC_NO_ERROR_SIMULATE_NAT (PICOQUIC_ERROR_CLASS + 48) +#define PICOQUIC_NO_ERROR_SIMULATE_MIGRATION (PICOQUIC_ERROR_CLASS + 49) +#define PICOQUIC_ERROR_VERSION_NOT_SUPPORTED (PICOQUIC_ERROR_CLASS + 50) + +/* + * Protocol errors defined in the QUIC spec + */ +#define PICOQUIC_TRANSPORT_INTERNAL_ERROR (0x1) +#define PICOQUIC_TRANSPORT_SERVER_BUSY (0x2) +#define PICOQUIC_TRANSPORT_FLOW_CONTROL_ERROR (0x3) +#define PICOQUIC_TRANSPORT_STREAM_LIMIT_ERROR (0x4) +#define PICOQUIC_TRANSPORT_STREAM_STATE_ERROR (0x5) +#define PICOQUIC_TRANSPORT_FINAL_OFFSET_ERROR (0x6) +#define PICOQUIC_TRANSPORT_FRAME_FORMAT_ERROR (0x7) +#define PICOQUIC_TRANSPORT_PARAMETER_ERROR (0x8) +#define PICOQUIC_TRANSPORT_CONNECTION_ID_LIMIT_ERROR (0x9) +#define PICOQUIC_TRANSPORT_PROTOCOL_VIOLATION (0xA) +#define PICOQUIC_TRANSPORT_INVALID_TOKEN (0xB) +#define PICOQUIC_TRANSPORT_APPLICATION_ERROR (0xC) +#define PICOQUIC_TRANSPORT_CRYPTO_BUFFER_EXCEEDED (0xD) +#define PICOQUIC_TRANSPORT_KEY_UPDATE_ERROR (0xE) +#define PICOQUIC_TRANSPORT_AEAD_LIMIT_REACHED (0xF) + +#define PICOQUIC_TRANSPORT_CRYPTO_ERROR(Alert) (((uint16_t)0x100) | ((uint16_t)((Alert)&0xFF))) +#define PICOQUIC_TLS_ALERT_WRONG_ALPN (0x178) +#define PICOQUIC_TLS_HANDSHAKE_FAILED (0x201) + +#define PICOQUIC_MAX_PACKET_SIZE 1536 +#define PICOQUIC_INITIAL_MTU_IPV4 1252 +#define PICOQUIC_INITIAL_MTU_IPV6 1232 +#define PICOQUIC_RESET_SECRET_SIZE 16 +#define PICOQUIC_RESET_PACKET_PAD_SIZE 23 +#define PICOQUIC_RESET_PACKET_MIN_SIZE (PICOQUIC_RESET_PACKET_PAD_SIZE + PICOQUIC_RESET_SECRET_SIZE) + +#define PICOQUIC_LOG_PACKET_MAX_SEQUENCE 100 + +#define FOURCC(a, b, c, d) ((((uint32_t)(d)<<24) | ((c)<<16) | ((b)<<8) | (a))) + +/* +* Connection states, useful to expose the state to the application. +*/ +typedef enum { + picoquic_state_client_init, + picoquic_state_client_init_sent, + picoquic_state_client_renegotiate, + picoquic_state_client_retry_received, + picoquic_state_client_init_resent, + picoquic_state_server_init, + picoquic_state_server_handshake, + picoquic_state_client_handshake_start, + picoquic_state_handshake_failure, + picoquic_state_handshake_failure_resend, + picoquic_state_client_almost_ready, + picoquic_state_server_false_start, + picoquic_state_server_almost_ready, + picoquic_state_client_ready_start, + picoquic_state_ready, + picoquic_state_disconnecting, + picoquic_state_closing_received, + picoquic_state_closing, + picoquic_state_draining, + picoquic_state_disconnected +} picoquic_state_enum; + + +/* +* Quic spin bit variants +*/ + +typedef enum { + picoquic_spinbit_basic = 0, /* default spin bit behavior, as specified in spin bit draft */ + picoquic_spinbit_random = 1, /* alternative spin bit behavior, randomized for each packet */ + picoquic_spinbit_null = 2, /* null behavior, randomized per path */ + picoquic_spinbit_on = 3 /* Option used in test to avoid randomizing spin bit on/off */ +} picoquic_spinbit_version_enum; + +/* + * Provisional definition of the connection ID. + */ +#define PICOQUIC_CONNECTION_ID_MIN_SIZE 0 +#define PICOQUIC_CONNECTION_ID_MAX_SIZE 20 + +typedef struct st_picoquic_connection_id_t { + uint8_t id[PICOQUIC_CONNECTION_ID_MAX_SIZE]; + uint8_t id_len; +} picoquic_connection_id_t; + + +/* forward definition to avoid full dependency on picotls.h */ +typedef struct st_ptls_iovec_t ptls_iovec_t; + +/* Detect whether error occured in TLS + */ +int picoquic_is_handshake_error(uint16_t error_code); + + +typedef struct st_picoquic_quic_t picoquic_quic_t; +typedef struct st_picoquic_cnx_t picoquic_cnx_t; +typedef struct st_picoquic_path_t picoquic_path_t; + +typedef enum { + picoquic_callback_stream_data = 0, /* Data received from peer on stream N */ + picoquic_callback_stream_fin, /* Fin received from peer on stream N; data is optional */ + picoquic_callback_stream_reset, /* Reset Stream received from peer on stream N; bytes=NULL, len = 0 */ + picoquic_callback_stop_sending, /* Stop sending received from peer on stream N; bytes=NULL, len = 0 */ + picoquic_callback_stateless_reset, /* Stateless reset received from peer. Stream=0, bytes=NULL, len=0 */ + picoquic_callback_close, /* Connection close. Stream=0, bytes=NULL, len=0 */ + picoquic_callback_application_close, /* Application closed by peer. Stream=0, bytes=NULL, len=0 */ + picoquic_callback_stream_gap, /* bytes=NULL, len = length-of-gap or 0 (if unknown) */ + picoquic_callback_prepare_to_send, /* Ask application to send data in frame, see picoquic_provide_stream_data_buffer for details */ + picoquic_callback_almost_ready, /* Data can be sent, but the connection is not fully established */ + picoquic_callback_ready, /* Data can be sent and received, connection migration can be initiated */ + picoquic_callback_datagram, /* Datagram frame has been received */ + picoquic_callback_version_negotiation, /* version negotiation requested */ + picoquic_callback_request_alpn_list, /* Provide the list of supported ALPN */ + picoquic_callback_set_alpn, /* Set ALPN to negotiated value */ + picoquic_callback_pacing_changed /* Pacing rate for the connection changed */ +} picoquic_call_back_event_t; + +typedef struct st_picoquic_tp_prefered_address_t { + int is_defined; + uint8_t ipv4Address[4]; + uint16_t ipv4Port; + uint8_t ipv6Address[16]; + uint16_t ipv6Port; + picoquic_connection_id_t connection_id; + uint8_t statelessResetToken[16]; +} picoquic_tp_prefered_address_t; + +typedef struct st_picoquic_tp_t { + uint64_t initial_max_stream_data_bidi_local; + uint64_t initial_max_stream_data_bidi_remote; + uint64_t initial_max_stream_data_uni; + uint64_t initial_max_data; + uint64_t initial_max_stream_id_bidir; + uint64_t initial_max_stream_id_unidir; + uint32_t idle_timeout; + uint32_t max_packet_size; + uint32_t max_ack_delay; /* stored in in microseconds for convenience */ + uint32_t active_connection_id_limit; + uint8_t ack_delay_exponent; + unsigned int migration_disabled; + picoquic_tp_prefered_address_t prefered_address; + uint32_t max_datagram_frame_size; + int enable_loss_bit; + int enable_time_stamp; /* (x&1) want, (x&2) can */ + uint64_t min_ack_delay; + int do_grease_quic_bit; +} picoquic_tp_t; + +/* + * Stream types + */ +#define PICOQUIC_STREAM_ID_TYPE_MASK 3 +#define PICOQUIC_STREAM_ID_CLIENT_INITIATED 0 +#define PICOQUIC_STREAM_ID_SERVER_INITIATED 1 +#define PICOQUIC_STREAM_ID_BIDIR 0 +#define PICOQUIC_STREAM_ID_UNIDIR 2 +#define PICOQUIC_STREAM_ID_CLIENT_INITIATED_BIDIR (PICOQUIC_STREAM_ID_CLIENT_INITIATED|PICOQUIC_STREAM_ID_BIDIR) +#define PICOQUIC_STREAM_ID_SERVER_INITIATED_BIDIR (PICOQUIC_STREAM_ID_SERVER_INITIATED|PICOQUIC_STREAM_ID_BIDIR) +#define PICOQUIC_STREAM_ID_CLIENT_INITIATED_UNIDIR (PICOQUIC_STREAM_ID_CLIENT_INITIATED|PICOQUIC_STREAM_ID_UNIDIR) +#define PICOQUIC_STREAM_ID_SERVER_INITIATED_UNIDIR (PICOQUIC_STREAM_ID_SERVER_INITIATED|PICOQUIC_STREAM_ID_UNIDIR) + +#define PICOQUIC_STREAM_ID_CLIENT_MAX_INITIAL_BIDIR (PICOQUIC_STREAM_ID_CLIENT_INITIATED_BIDIR + ((65535-1)*4)) +#define PICOQUIC_STREAM_ID_SERVER_MAX_INITIAL_BIDIR (PICOQUIC_STREAM_ID_SERVER_INITIATED_BIDIR + ((65535-1)*4)) +#define PICOQUIC_STREAM_ID_CLIENT_MAX_INITIAL_UNIDIR (PICOQUIC_STREAM_ID_CLIENT_INITIATED_UNIDIR + ((65535-1)*4)) +#define PICOQUIC_STREAM_ID_SERVER_MAX_INITIAL_UNIDIR (PICOQUIC_STREAM_ID_SERVER_INITIATED_UNIDIR + ((65535-1)*4)) + +/* +* Time management. Internally, picoquic works in "virtual time", updated via the "current time" parameter +* passed through picoquic_create(), picoquic_create_cnx(), picoquic_incoming_packet(), and picoquic_prepare_packet(). +* +* There are two supported modes of operation, "wall time" synchronized with the system's current time function, +* and "simulated time". Production services are expected to use wall time, tests and simulation use the +* simulated time. The simulated time is held in a 64 bit counter, the address of which is passed as +* the "p_simulated_time" parameter to picoquic_create(). +* +* The time management needs to be consistent with the functions used internally by the TLS package "picotls". +* If the argument "p_simulated_time" is NULL, picotls will use "wall time", accessed through system API. +* If the argument is set, the default time function of picotls will be overridden by a function that +* reads the value of *p_simulated_time. +* +* The function "picoquic_current_time()" reads the wall time in microseconds, using the same system calls +* as picotls. The default socket code in "picosock.[ch]" uses that time function, and returns the time +* at which messages arrived. +* +* The function "picoquic_get_quic_time()" returns the "virtual time" used by the specified quic +* context, which can be either the current wall time or the simulated time, depending on how the +* quic context was initialized. +*/ + +uint64_t picoquic_current_time(); /* wall time */ +uint64_t picoquic_get_quic_time(picoquic_quic_t* quic); /* connection time, compatible with simulations */ + +/* Callback function for providing stream data to the application, + * and generally for notifying events from stack to application. + * The type of event is specified in an enum picoquic_call_back_event_t. + * For stream related calls, stream ID provides the stream number, and the + * parameter stream_ctx provides the application supplied stream context. + */ +typedef int (*picoquic_stream_data_cb_fn)(picoquic_cnx_t* cnx, + uint64_t stream_id, uint8_t* bytes, size_t length, + picoquic_call_back_event_t fin_or_event, void* callback_ctx, void * stream_ctx); + +/* Callback from the TLS stack upon receiving a list of proposed ALPN in the Client Hello + * The stack passes a of io vectors (base, len) each containing a proposed + * ALPN. The implementation returns the index of the selected ALPN, or a value >= count + * if none of the proposed ALPN is supported. + * + * The callback is only called if no default ALPN is specified in the Quic context. + */ +typedef size_t (*picoquic_alpn_select_fn)(picoquic_quic_t* quic, ptls_iovec_t* list, size_t count); + +/* Function used during callback to provision an ALPN context. The stack + * issues a callback of type + */ +int picoquic_add_proposed_alpn(void* tls_context, const char* alpn); + +/* Callback function for producing a connection ID compatible + * with the server environment. + */ + +typedef void (*picoquic_connection_id_cb_fn)(picoquic_quic_t * quic, picoquic_connection_id_t cnx_id_local, + picoquic_connection_id_t cnx_id_remote, void* cnx_id_cb_data, picoquic_connection_id_t * cnx_id_returned); + +/* Default connection ID management functions, supporting a set of basic + * callback policies: + * - + */ + +typedef enum { + picoquic_connection_id_random = 0, + picoquic_connection_id_remote = 1, + picoquic_connection_id_encrypt_basic = 2 +} picoquic_connection_id_encrypt_enum; + + +typedef struct st_picoquic_connection_id_callback_ctx_t { + picoquic_connection_id_encrypt_enum cnx_id_select; + picoquic_connection_id_t cnx_id_mask; + picoquic_connection_id_t cnx_id_val; + void * cid_enc; +} picoquic_connection_id_callback_ctx_t; + +void picoquic_connection_id_callback(picoquic_quic_t * quic, picoquic_connection_id_t cnx_id_local, + picoquic_connection_id_t cnx_id_remote, void* cnx_id_cb_data, picoquic_connection_id_t * cnx_id_returned); + +picoquic_connection_id_callback_ctx_t * picoquic_connection_id_callback_create_ctx( + char const * select_type, char const * default_value_hex, char const * mask_hex); + +void picoquic_connection_id_callback_free_ctx(void * cnx_id_cb_data); + +/* The fuzzer function is used to inject error in packets randomly. + * It is called just prior to sending a packet, and can randomly + * change the content or length of the packet. + */ +typedef uint32_t(*picoquic_fuzz_fn)(void * fuzz_ctx, picoquic_cnx_t* cnx, uint8_t * bytes, + size_t bytes_max, size_t length, size_t header_length); +void picoquic_set_fuzz(picoquic_quic_t* quic, picoquic_fuzz_fn fuzz_fn, void * fuzz_ctx); + +/* Log application messages or other messages to the text log and binary log. + */ +void picoquic_log_app_message(picoquic_cnx_t* cnx, const char* fmt, ...); + +/* Set the log level: + * 1: log all packets + * 0: only log the first 100 packets for each connection. */ +void picoquic_set_log_level(picoquic_quic_t* quic, int log_level); + +/* Require randomization of initial PN numbers */ +void picoquic_set_random_initial(picoquic_quic_t* quic, int random_initial); + +/* Set the "packet train" mode for pacing */ +void picoquic_set_packet_train_mode(picoquic_quic_t* quic, int train_mode); + +/* set the padding policy. + * The padding policy is parameterized by two variables: + * - packets shorter than padding_min_size will be padded to that size. + * - if packets are longer than the min_size, they will be padded to the min size plus + * the nearest multiple of the "padding multiple", or to the path MTU. + * + * Padding is done before encryption, and before adding the AEAD checksum. + * + * The default value of the min size is set to 39 to enable the reset process. + * By default, the multiple is set to zero. + * + * When using "packet trains", it is a good idea to also set the padding multiple, because that + * ensures that most packets will be padded to full path MTU length. + */ +void picoquic_set_padding_policy(picoquic_quic_t* quic, uint32_t padding_min_size, uint32_t padding_multiple); + +/* Require Picoquic to log the session keys in the specified files. + * Instead of calling this API directly, consider calling the + * function picoquic_set_key_log_file_from_env() defined in + * picosocks.h */ +void picoquic_set_key_log_file(picoquic_quic_t* quic, char const* keylog_filename); + +/* Set the ESNI key. + * May be called several times to set several keys. + */ +int picoquic_esni_load_key(picoquic_quic_t * quic, char const * esni_key_file_name); + +/* Set the ESNI RR. Must be called after setting the ESNI key at least once. */ +int picoquic_esni_server_setup(picoquic_quic_t * quic, char const * esni_rr_file_name); + +/* Will be called to verify that the given data corresponds to the given signature. + * This callback and the `verify_ctx` will be set by the `verify_certificate_cb_fn`. + * If `data` and `sign` are empty buffers, an error occurred and `verify_ctx` should be freed. + * Expect `0` as return value, when the data matches the signature. + */ + +typedef int (*picoquic_verify_sign_cb_fn)(void* verify_ctx, ptls_iovec_t data, ptls_iovec_t sign); +/* Will be called to verify a certificate of a connection. + * The arguments `verify_sign` and `verify_sign_ctx` are expected to be set, when the function returns `0`. + * See `verify_sign_cb_fn` for more information about these arguments. + */ +typedef int (*picoquic_verify_certificate_cb_fn)(void* ctx, picoquic_cnx_t* cnx, ptls_iovec_t* certs, size_t num_certs, + picoquic_verify_sign_cb_fn* verify_sign, void** verify_sign_ctx); + +/* Is called to free the verify certificate ctx */ +typedef void (*picoquic_free_verify_certificate_ctx)(void* ctx); + +/* QUIC context create and dispose */ +picoquic_quic_t* picoquic_create(uint32_t nb_connections, + char const* cert_file_name, char const* key_file_name, char const * cert_root_file_name, + char const* default_alpn, + picoquic_stream_data_cb_fn default_callback_fn, + void* default_callback_ctx, + picoquic_connection_id_cb_fn cnx_id_callback, + void* cnx_id_callback_data, + uint8_t reset_seed[PICOQUIC_RESET_SECRET_SIZE], + uint64_t current_time, + uint64_t* p_simulated_time, + char const* ticket_file_name, + const uint8_t* ticket_encryption_key, + size_t ticket_encryption_key_length); + +void picoquic_free(picoquic_quic_t* quic); + +/* management of retry policy. + * The cookie mode can be used to force the following behavior: + * - if cookie_mode&1, check the token and force a retry for each incoming connection. + * - if cookie&2, provide a token to the client after completing the handshake. + * When the "force retry" is not set, the code will count the number of "half-open" + * connections. This can happen for example if the server is subject to a DDOS attack. + * If the threshold is exceeded, the code will request a token before accepting new + * connections, forcing DDOS attackers to reveal their IP address. + * By default, the threshold is set to 128 connections. + */ +void picoquic_set_cookie_mode(picoquic_quic_t* quic, int cookie_mode); + +/* Set cipher suite, for tests. + * 0: default values + * 20: chacha20poly1305sha256 + * 128: aes128gcmsha256 + * 256: aes256gcmsha384 + * returns 0 if OK, -1 if the specified ciphersuite is not supported. + */ +int picoquic_set_cipher_suite(picoquic_quic_t* quic, int cipher_suite_id); + +/* Set key exchange algorithms, for tests. + * 0: default values + * 20: x25519 + * 128: secp256r1 + * 256: secp256r1 + * returns 0 if OK, -1 if the specified ciphersuite is not supported. + */ +int picoquic_set_key_exchange(picoquic_quic_t* quic, int key_exchange_id); + +/* Init of transport parameters per quic context */ +int picoquic_set_default_tp(picoquic_quic_t* quic, picoquic_tp_t* tp); +/* Set the transport parameters per connection */ +void picoquic_set_transport_parameters(picoquic_cnx_t * cnx, picoquic_tp_t const * tp); +/* Get the transport parameters per connection */ +picoquic_tp_t const* picoquic_get_transport_parameters(picoquic_cnx_t* cnx, int get_local); + +/* Set the TLS certificate chain(DER format) for the QUIC context. The context will take ownership over the certs pointer. */ +void picoquic_set_tls_certificate_chain(picoquic_quic_t* quic, ptls_iovec_t* certs, size_t count); + +/* Set the TLS root certificates (DER format) for the QUIC context. The context will take ownership over the certs pointer. + * The root certificates will be used to verify the certificate chain of the server and client (with client authentication activated). + * Returns `0` on success, `-1` on error while loading X509 certificate or `-2` on error while adding a cert to the certificate store. + */ +int picoquic_set_tls_root_certificates(picoquic_quic_t* quic, ptls_iovec_t* certs, size_t count); + +/* Tell the TLS stack to not attempt verifying certificates */ +void picoquic_set_null_verifier(picoquic_quic_t* quic); + +/* Set the TLS private key(DER format) for the QUIC context. The caller is responsible for cleaning up the pointer. */ +int picoquic_set_tls_key(picoquic_quic_t* quic, const uint8_t* data, size_t len); + +/* Set the verify certificate callback and context. */ +int picoquic_set_verify_certificate_callback(picoquic_quic_t* quic, picoquic_verify_certificate_cb_fn cb, void* ctx, + picoquic_free_verify_certificate_ctx free_fn); + +/* Set client authentication in TLS (if enabled, client is required to send certificates). */ +void picoquic_set_client_authentication(picoquic_quic_t* quic, int client_authentication); + +/* Set default padding policy for the context */ +void picoquic_set_default_padding(picoquic_quic_t* quic, uint32_t padding_multiple, uint32_t padding_minsize); + +/* Set default spin bit policy for the context */ +void picoquic_set_default_spinbit_policy(picoquic_quic_t * quic, picoquic_spinbit_version_enum default_spinbit_policy); + +void picoquic_set_default_crypto_epoch_length(picoquic_quic_t* quic, uint64_t crypto_epoch_length_max); + +uint64_t picoquic_get_default_crypto_epoch_length(picoquic_quic_t* quic); + +/* Get the local CID length */ +uint8_t picoquic_get_local_cid_length(picoquic_quic_t* quic); + +/* Check whether a CID is locally defined */ +int picoquic_is_local_cid(picoquic_quic_t* quic, picoquic_connection_id_t* cid); + +/* Manage session tickets and retry tokens. + * There is no explicit call to load tickets, this must be done by passing + * the ticket store name as an argument to picoquic_create(). + */ +int picoquic_load_retry_tokens(picoquic_quic_t* quic, char const* token_store_filename); +int picoquic_save_session_tickets(picoquic_quic_t* quic, char const* ticket_store_filename); +int picoquic_save_retry_tokens(picoquic_quic_t* quic, char const* token_store_filename); + +/* Set default connection ID length for the context. + * All valid values are supported on the client. + * Using a null value on the server is not tested, may not work. + * Cannot be changed if there are active connections in the context. + * Value must be compatible with what the cnx_id_callback() expects on a server */ +int picoquic_set_default_connection_id_length(picoquic_quic_t* quic, uint8_t cid_length); + +void picoquic_set_mtu_max(picoquic_quic_t* quic, uint32_t mtu_max); + +void picoquic_set_alpn_select_fn(picoquic_quic_t* quic, picoquic_alpn_select_fn alpn_select_fn); + +void picoquic_set_default_callback(picoquic_quic_t * quic, picoquic_stream_data_cb_fn callback_fn, void * callback_ctx); + +/* Set and get the maximum number of simultaneously logged connections. +* If that number is too high, the maximum number of open files will be hit +* at random places in the code. A small value means that some connections may +* not be logged. Default is set to 32. */ +void picoquic_set_max_simultaneous_logs(picoquic_quic_t* quic, uint32_t max_simultaneous_logs); +uint32_t picoquic_get_max_simultaneous_logs(picoquic_quic_t* quic); + +/* Connection context creation and registration */ +picoquic_cnx_t* picoquic_create_cnx(picoquic_quic_t* quic, + picoquic_connection_id_t initial_cnx_id, picoquic_connection_id_t remote_cnx_id, + const struct sockaddr* addr_to, uint64_t start_time, uint32_t preferred_version, + char const* sni, char const* alpn, char client_mode); + +picoquic_cnx_t* picoquic_create_client_cnx(picoquic_quic_t* quic, + struct sockaddr* addr, uint64_t start_time, uint32_t preferred_version, + char const* sni, char const* alpn, + picoquic_stream_data_cb_fn callback_fn, void* callback_ctx); + +int picoquic_start_client_cnx(picoquic_cnx_t* cnx); + +void picoquic_delete_cnx(picoquic_cnx_t* cnx); + +int picoquic_esni_client_from_file(picoquic_cnx_t * cnx, char const * esni_rr_file_name); + +int picoquic_close(picoquic_cnx_t* cnx, uint16_t reason_code); + +int picoquic_probe_new_path(picoquic_cnx_t* cnx, const struct sockaddr* addr_from, + const struct sockaddr* addr_to, uint64_t current_time); + +int picoquic_renew_connection_id(picoquic_cnx_t* cnx, int path_id); + +int picoquic_start_key_rotation(picoquic_cnx_t * cnx); + +picoquic_quic_t* picoquic_get_quic_ctx(picoquic_cnx_t* cnx); +picoquic_cnx_t* picoquic_get_first_cnx(picoquic_quic_t* quic); +picoquic_cnx_t* picoquic_get_next_cnx(picoquic_cnx_t* cnx); +int64_t picoquic_get_next_wake_delay(picoquic_quic_t* quic, + uint64_t current_time, + int64_t delay_max); +picoquic_cnx_t* picoquic_get_earliest_cnx_to_wake(picoquic_quic_t* quic, uint64_t max_wake_time); + +uint64_t picoquic_get_next_wake_time(picoquic_quic_t* quic, uint64_t current_time); + +picoquic_state_enum picoquic_get_cnx_state(picoquic_cnx_t* cnx); + +void picoquic_cnx_set_padding_policy(picoquic_cnx_t * cnx, uint32_t padding_multiple, uint32_t padding_minsize); +void picoquic_cnx_get_padding_policy(picoquic_cnx_t * cnx, uint32_t * padding_multiple, uint32_t * padding_minsize); +/* Set spin bit policy for the connection */ +void picoquic_cnx_set_spinbit_policy(picoquic_cnx_t * cnx, picoquic_spinbit_version_enum spinbit_policy); + +/* Set max packet interval between key rotations */ +void picoquic_set_crypto_epoch_length(picoquic_cnx_t* cnx, uint64_t crypto_epoch_length_max); +uint64_t picoquic_get_crypto_epoch_length(picoquic_cnx_t* cnx); + +void picoquic_cnx_set_pmtud_required(picoquic_cnx_t* cnx, int is_pmtud_required); + +int picoquic_tls_is_psk_handshake(picoquic_cnx_t* cnx); + +void picoquic_get_peer_addr(picoquic_cnx_t* cnx, struct sockaddr** addr); +void picoquic_get_local_addr(picoquic_cnx_t* cnx, struct sockaddr** addr); +unsigned long picoquic_get_local_if_index(picoquic_cnx_t* cnx); + +int picoquic_set_local_addr(picoquic_cnx_t* cnx, struct sockaddr* addr); + + +picoquic_connection_id_t picoquic_get_local_cnxid(picoquic_cnx_t* cnx); +picoquic_connection_id_t picoquic_get_remote_cnxid(picoquic_cnx_t* cnx); +picoquic_connection_id_t picoquic_get_initial_cnxid(picoquic_cnx_t* cnx); +picoquic_connection_id_t picoquic_get_client_cnxid(picoquic_cnx_t* cnx); +picoquic_connection_id_t picoquic_get_server_cnxid(picoquic_cnx_t* cnx); +picoquic_connection_id_t picoquic_get_logging_cnxid(picoquic_cnx_t* cnx); + +uint64_t picoquic_get_cnx_start_time(picoquic_cnx_t* cnx); +uint64_t picoquic_is_0rtt_available(picoquic_cnx_t* cnx); + +int picoquic_is_cnx_backlog_empty(picoquic_cnx_t* cnx); + +void picoquic_set_callback(picoquic_cnx_t* cnx, + picoquic_stream_data_cb_fn callback_fn, void* callback_ctx); + +picoquic_stream_data_cb_fn picoquic_get_default_callback_function(picoquic_quic_t * quic); + +void * picoquic_get_default_callback_context(picoquic_quic_t * quic); + +picoquic_stream_data_cb_fn picoquic_get_callback_function(picoquic_cnx_t * cnx); + +void * picoquic_get_callback_context(picoquic_cnx_t* cnx); + +/* Send extra frames */ +int picoquic_queue_misc_frame(picoquic_cnx_t* cnx, const uint8_t* bytes, size_t length, int is_pure_ack); + +/* Send datagram frame */ +int picoquic_queue_datagram_frame(picoquic_cnx_t* cnx, size_t length, const uint8_t* bytes); + +/* The incoming packet API is used to pass incoming packets to a + * Quic context. The API handles the decryption of the packets + * and their processing in the context of connections. + */ + +int picoquic_incoming_packet( + picoquic_quic_t* quic, + uint8_t* bytes, + size_t length, + struct sockaddr* addr_from, + struct sockaddr* addr_to, + int if_index_to, + unsigned char received_ecn, + uint64_t current_time); + +/* Applications must regularly poll the "next packet" API to obtain the + * next packet that will be set over the network. The API for that is + * picoquic_prepare_next_packet", which operates on a "quic context". + * The API "picoquic_prepare_packet" does the same but for just one + * connection at a time. + */ + +int picoquic_prepare_next_packet_ex(picoquic_quic_t* quic, + uint64_t current_time, uint8_t* send_buffer, size_t send_buffer_max, size_t* send_length, + struct sockaddr_storage* p_addr_to, struct sockaddr_storage* p_addr_from, int* if_index, + picoquic_connection_id_t* log_cid, picoquic_cnx_t** p_last_cnx, size_t* send_msg_size); + +int picoquic_prepare_next_packet(picoquic_quic_t* quic, + uint64_t current_time, uint8_t* send_buffer, size_t send_buffer_max, size_t* send_length, + struct sockaddr_storage* p_addr_to, struct sockaddr_storage* p_addr_from, int* if_index, + picoquic_connection_id_t* p_logcid, picoquic_cnx_t** p_last_cnx); + +int picoquic_prepare_packet_ex(picoquic_cnx_t* cnx, + uint64_t current_time, uint8_t* send_buffer, size_t send_buffer_max, size_t* send_length, + struct sockaddr_storage* p_addr_to, struct sockaddr_storage* p_addr_from, int* if_index, + size_t* send_msg_size); + +int picoquic_prepare_packet(picoquic_cnx_t* cnx, + uint64_t current_time, uint8_t* send_buffer, size_t send_buffer_max, size_t* send_length, + struct sockaddr_storage* p_addr_to, struct sockaddr_storage* p_addr_from, int* if_index); + +/* Handling of out of sequence stream data delivery. + * + * For applications like video communication, it is important to process stream data + * as soon as it arrives, even if it arrives out of order. For example, it might be + * better to play the next video frame than to wait for the complete transmission + * of the previous one. Picoquic enables that with the "direct receive" marking. If + * a stream is marked as "direct receive", picoquic will hand data receive on + * that stream to the application immediately, even if it is out of sequence. + * + * The data will be delivered to a direct receive callback function, which will + * pass the pointer to the data, the stream offset and length of the data, and + * also indicates if a fin mark was received. When the fin mark is present, the + * fin offset of the stream is located at the sum of offset and length, and + * the length may be null. + * + * The function picoquic_mark_direct_receive_stream is used to mark a stream + * as `direct receive` and provide the callback function and context for that + * stream. Calling that function with a NULL callback function pointer results + * in an error. + * + * If stream data was queued at the time the picoquic_mark_direct_receive_stream + * function is called, the callback will be activated immediately. + * + * The callback function shall return: + * - 0 if the data was processed normally. + * - PICOQUIC_STREAM_RECEIVE_COMPLETE if the fin bit was received once and all + * expected bytes on the stream have been received. + * - An appropriate error code if an error was encountered. + * + * Returning an error code will cause picoquic to close the connection with the + * corresponding transport error, or PICOQUIC_TRANSPORT_INTERNAL_ERROR if the + * error code in the range of the PICOQUIC_ERROR_CLASS. + */ + +typedef int (*picoquic_stream_direct_receive_fn)(picoquic_cnx_t* cnx, + uint64_t stream_id, int fin, const uint8_t* bytes, uint64_t offset, size_t length, + void* direct_receive_ctx); + +int picoquic_mark_direct_receive_stream(picoquic_cnx_t* cnx, + uint64_t stream_id, picoquic_stream_direct_receive_fn direct_receive_fn, void* direct_receive_ctx); + +/* Associate stream with app context */ +int picoquic_set_app_stream_ctx(picoquic_cnx_t* cnx, + uint64_t stream_id, void* app_stream_ctx); + +/* Mark stream as active, or not. + * If a stream is active, it will be polled for data when the transport + * is ready to send. The polling will only start after all currently + * queued data has been sent. + */ +int picoquic_mark_active_stream(picoquic_cnx_t* cnx, + uint64_t stream_id, int is_active, void* v_stream_ctx); + +/* Mark stream as high priority. This guarantees that the data + * queued on this stream will be sent before data from any other + * stream. It is used for example in the HTTP3 implementation + * to guarantee that the "settings" frame is sent from the + * control stream before any other frame. + * Priority is immediately removed when all data from that + * stream is sent; it should be reset if new data is added + * for which priority handling is still required. + * Priority is also removed if the "is_high_priority" + * parameter is set to 0, or if another stream is set + * to high priority. + */ + +int picoquic_mark_high_priority_stream(picoquic_cnx_t* cnx, + uint64_t stream_id, int is_high_priority); + +/* If a stream is marked active, the application will receive a callback with + * event type "picoquic_callback_prepare_to_send" when the transport is ready to + * send data on a stream. The "length" argument in the call back indicates the + * largest amount of data that can be sent, and the "bytes" argument points + * to an opaque context structure. In order to prepare data, the application + * needs to call "picoquic_provide_stream_data_buffer" with that context + * pointer, with the number of bytes that it wants to write, with an indication + * of whether or not the fin of the stream was reached, and also an indication + * of whether or not the stream is still active. The function + * returns the pointer to a memory address where to write the byte -- or + * a NULL pointer in case of error. The application then copies the specified + * number of bytes at the provided address, and provide a return code 0 from + * the callback in case of success, or non zero in case of error. + */ + +uint8_t* picoquic_provide_stream_data_buffer(void* context, size_t nb_bytes, int is_fin, int is_still_active); + +/* Queue data on a stream, so the transport can send it immediately + * when ready. The data is copied in an intermediate buffer managed by + * the transport. Calling this API automatically erases the "active + * mark" that might have been set by using "picoquic_mark_active_stream". + * It also erases the "app_stream_ctx" value set in previous calls to + * picoquic_add_to_stream_with_ctx or picoquic_mark_active_stream + */ +int picoquic_add_to_stream(picoquic_cnx_t* cnx, + uint64_t stream_id, const uint8_t* data, size_t length, int set_fin); + +void picoquic_reset_stream_ctx(picoquic_cnx_t* cnx, uint64_t stream_id); + +/* Same as "picoquic_add_to_stream", but also sets the application stream context. + * The context is used in call backs, so the application can directly process responses. + */ +int picoquic_add_to_stream_with_ctx(picoquic_cnx_t * cnx, uint64_t stream_id, const uint8_t * data, size_t length, int set_fin, void * app_stream_ctx); + +/* Reset a stream, indicating that no more data will be sent on + * that stream and that any data currently queued can be abandoned. */ +int picoquic_reset_stream(picoquic_cnx_t* cnx, + uint64_t stream_id, uint16_t local_stream_error); + +/* Open the flow control for receiving the expected data on a stream */ +int picoquic_open_flow_control(picoquic_cnx_t* cnx, uint64_t stream_id, uint64_t expected_data_size); + +/* Obtain the next available stream ID in the local category */ +uint64_t picoquic_get_next_local_stream_id(picoquic_cnx_t* cnx, int is_unidir); + +/* Ask the peer to stop sending on a stream. The peer is expected + * to reset that stream when receiving the "stop sending" signal. */ +int picoquic_stop_sending(picoquic_cnx_t* cnx, + uint64_t stream_id, uint16_t local_stream_error); + + +/* + * Set the optimistic ack policy. The holes will be inserted at random locations, + * which in average will be separated by the pseudo period. By default, + * the pseudo perio is 0, which means no hole insertion. + */ + +void picoquic_set_optimistic_ack_policy(picoquic_quic_t* quic, uint32_t sequence_hole_pseudo_period); + +/* Enables keep alive for a connection. + * Keep alive interval is expressed in microseconds. + * If `interval` is `0`, it is set to `idle_timeout / 2`. + */ +void picoquic_enable_keep_alive(picoquic_cnx_t* cnx, uint64_t interval); +/* Disables keep alive for a connection. */ +void picoquic_disable_keep_alive(picoquic_cnx_t* cnx); + +/* Returns if the given connection is the client. */ +int picoquic_is_client(picoquic_cnx_t* cnx); + +/* Returns the local error of the given connection context. */ +int picoquic_get_local_error(picoquic_cnx_t* cnx); + +/* Returns the remote error of the given connection context. */ +int picoquic_get_remote_error(picoquic_cnx_t* cnx); + +/* Returns the remote error for the given stream. */ +uint64_t picoquic_get_remote_stream_error(picoquic_cnx_t* cnx, uint64_t stream_id); + + +uint64_t picoquic_get_data_sent(picoquic_cnx_t * cnx); + +uint64_t picoquic_get_data_received(picoquic_cnx_t * cnx); + +int picoquic_cnx_is_still_logging(picoquic_cnx_t* cnx); + +/* Congestion algorithm definition */ + +typedef enum { + picoquic_congestion_notification_acknowledgement, + picoquic_congestion_notification_repeat, + picoquic_congestion_notification_timeout, + picoquic_congestion_notification_spurious_repeat, + picoquic_congestion_notification_rtt_measurement, + picoquic_congestion_notification_bw_measurement, + picoquic_congestion_notification_ecn_ec, + picoquic_congestion_notification_cwin_blocked, + picoquic_congestion_notification_reset +} picoquic_congestion_notification_t; + +typedef void (*picoquic_congestion_algorithm_init)(picoquic_path_t* path_x, uint64_t current_time); +typedef void (*picoquic_congestion_algorithm_notify)( + picoquic_cnx_t* cnx, + picoquic_path_t* path_x, + picoquic_congestion_notification_t notification, + uint64_t rtt_measurement, + uint64_t one_way_delay, + uint64_t nb_bytes_acknowledged, + uint64_t lost_packet_number, + uint64_t current_time); +typedef void (*picoquic_congestion_algorithm_delete)(picoquic_path_t* cnx); +typedef void (*picoquic_congestion_algorithm_observe)( + picoquic_path_t* path_x, uint64_t * cc_state, uint64_t * cc_param); + +typedef struct st_picoquic_congestion_algorithm_t { + char const * congestion_algorithm_id; + uint8_t congestion_algorithm_number; + picoquic_congestion_algorithm_init alg_init; + picoquic_congestion_algorithm_notify alg_notify; + picoquic_congestion_algorithm_delete alg_delete; + picoquic_congestion_algorithm_observe alg_observe; +} picoquic_congestion_algorithm_t; + +extern picoquic_congestion_algorithm_t* picoquic_newreno_algorithm; +extern picoquic_congestion_algorithm_t* picoquic_cubic_algorithm; +extern picoquic_congestion_algorithm_t* picoquic_dcubic_algorithm; +extern picoquic_congestion_algorithm_t* picoquic_fastcc_algorithm; +extern picoquic_congestion_algorithm_t* picoquic_bbr_algorithm; + +#define PICOQUIC_DEFAULT_CONGESTION_ALGORITHM picoquic_newreno_algorithm; + +picoquic_congestion_algorithm_t const* picoquic_get_congestion_algorithm(char const* alg_name); + +void picoquic_set_default_congestion_algorithm(picoquic_quic_t* quic, picoquic_congestion_algorithm_t const* algo); + +void picoquic_set_default_congestion_algorithm_by_name(picoquic_quic_t* quic, char const* alg_name); + +void picoquic_set_congestion_algorithm(picoquic_cnx_t* cnx, picoquic_congestion_algorithm_t const* algo); + +/* Bandwidth update and congestion control parameters value. + * Congestion control in picoquic is characterized by three values: + * - pacing rate, expressed in bytes per second (for example, 10Mbps would be noted as 1250000) + * - congestion window, expressed in bytes + * - RTT, expressed in microseconds + * + * If an application subscribes to pacing rate updates, it will start receiving callback events + * of type "picoquic_callback_pacing_changed". The subscription to the updates specifies + * two levels: + * - decrease threshold, in bytes per second + * - increase threshold, in bytes per second + * An event will be generated each time the bandwidth increases or decreases by a value + * larger than the specified threshold. The "stream_id" parameter of the callback will + * indicate the new pacing rate, in bytes per second. + * + * By default, the threshold values are set to UINT64_MAX, and no event is generated. + * + * Applications may also use a set of accessor functions to obtain the current values + * of the key congestion control parameters, for the currently selected transmission + * path. + */ + +void picoquic_subscribe_pacing_rate_updates(picoquic_cnx_t* cnx, uint64_t decrease_threshold, uint64_t increase_threshold); +uint64_t picoquic_get_pacing_rate(picoquic_cnx_t* cnx); +uint64_t picoquic_get_cwin(picoquic_cnx_t* cnx); +uint64_t picoquic_get_rtt(picoquic_cnx_t* cnx); + +/* Load balancer support is defined in https://datatracker.ietf.org/doc/draft-ietf-quic-load-balancers/ + * The draft defines methods for encoding a server ID in a connection identifier, and optionally + * obfuscating or encrypting the CID value. The CID are generated by the individual servers, + * based on configuration options provided by the load balancer. The draft also defines + * methods for generating retry tokens either by a protection box colocated with the + * load balancer, or at the individual server, with methods for letting individual + * servers retrieve information from the tokens. + * The configuration options are encoded in the picoquic_load_balancer_config_t structure. + */ + +typedef enum { + picoquic_load_balancer_cid_clear, + picoquic_load_balancer_cid_obfuscated, + picoquic_load_balancer_cid_stream_cipher, + picoquic_load_balancer_cid_block_cipher +} picoquic_load_balancer_cid_method_enum; + + +typedef struct st_picoquic_load_balancer_config_t { + picoquic_load_balancer_cid_method_enum method; + uint8_t server_id_length; + uint8_t routing_bits_length; /* Used in divider mode */ + uint8_t nonce_length; /* used in stream cipher mode */ + uint8_t zero_pad_length; /* used in block cipher mode */ + uint8_t connection_id_length; + uint8_t first_byte; + uint64_t server_id64; + uint8_t cid_encryption_key[16]; + uint64_t divider; /* used in obfuscation methods */ +} picoquic_load_balancer_config_t; + +int picoquic_lb_compat_cid_config(picoquic_quic_t* quic, picoquic_load_balancer_config_t* lb_config); +void picoquic_lb_compat_cid_config_free(picoquic_quic_t* quic); + +typedef struct st_picoquic_load_balancer_cid_context_t { + picoquic_load_balancer_cid_method_enum method; + uint8_t server_id_length; + uint8_t routing_bits_length; + uint8_t nonce_length; /* used in stream cipher mode */ + uint8_t zero_pad_length; /* used in block cipher mode */ + uint8_t connection_id_length; + uint8_t first_byte; + uint64_t server_id64; + uint64_t divider; /* used in obfuscation methods */ + uint8_t server_id[16]; + void* cid_encryption_context; /* used in stream and cipher mode */ + void* cid_decryption_context; /* used in block cipher mode */ +} picoquic_load_balancer_cid_context_t; + +void picoquic_lb_compat_cid_generate(picoquic_quic_t* quic, picoquic_connection_id_t cnx_id_local, picoquic_connection_id_t cnx_id_remote, void* cnx_id_cb_data, picoquic_connection_id_t* cnx_id_returned); +uint64_t picoquic_lb_compat_cid_verify(picoquic_quic_t* quic, void* cnx_id_cb_data, picoquic_connection_id_t const* cnx_id); +#ifdef __cplusplus +} +#endif + +#endif /* PICOQUIC_H */ diff --git a/deps/picoquic/picoquic.vcxproj b/deps/picoquic/picoquic.vcxproj new file mode 100644 index 0000000..0a15a2b --- /dev/null +++ b/deps/picoquic/picoquic.vcxproj @@ -0,0 +1,199 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 15.0 + {63E1E6B7-DB5F-4EDC-8AC8-7E9F5990D11F} + Win32Proj + picoquic + 10.0.18362.0 + + + + StaticLibrary + true + v141 + Unicode + + + StaticLibrary + false + v141 + true + Unicode + + + StaticLibrary + true + v141 + Unicode + + + StaticLibrary + false + v141 + true + Unicode + + + + + + + + + + + + + + + + + + + + + + + + + Level4 + Disabled + WIN32;_DEBUG;_LIB;_WINDOWS;PICOQUIC_USE_CONSTANT_TIME_MEMCMP;%(PreprocessorDefinitions) + $(OPENSSLDIR)\include;..\..\picotls\include;%(AdditionalIncludeDirectories) + Guard + ProgramDatabase + true + + + Windows + + + + + + + Level4 + Disabled + _DEBUG;_LIB;_WINDOWS;_WINDOWS64;%(PreprocessorDefinitions) + $(OPENSSL64DIR)\include;..\..\picotls\include;%(AdditionalIncludeDirectories) + Guard + ProgramDatabase + true + + + Windows + + + + + Level4 + + + MaxSpeed + true + true + WIN32;NDEBUG;_LIB;_WINDOWS;PICOQUIC_USE_CONSTANT_TIME_MEMCMP;%(PreprocessorDefinitions) + $(OPENSSLDIR)\include;..\..\picotls\include;%(AdditionalIncludeDirectories) + Guard + true + + + Windows + true + true + + + + + Level4 + + + MaxSpeed + true + true + NDEBUG;_LIB;_WINDOWS;_WINDOWS64;%(PreprocessorDefinitions) + $(OPENSSL64DIR)\include;..\..\picotls\include;%(AdditionalIncludeDirectories) + Guard + true + + + Windows + true + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + false + false + false + false + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/deps/picoquic/picoquic.vcxproj.filters b/deps/picoquic/picoquic.vcxproj.filters new file mode 100644 index 0000000..6b61351 --- /dev/null +++ b/deps/picoquic/picoquic.vcxproj.filters @@ -0,0 +1,144 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + \ No newline at end of file diff --git a/deps/picoquic/picoquic_binlog.h b/deps/picoquic/picoquic_binlog.h new file mode 100644 index 0000000..e55c74e --- /dev/null +++ b/deps/picoquic/picoquic_binlog.h @@ -0,0 +1,111 @@ +/* +* Author: Christian Huitema +* Copyright (c) 2019, Private Octopus, Inc. +* All rights reserved. +* +* Permission to use, copy, modify, and distribute this software for any +* purpose with or without fee is hereby granted, provided that the above +* copyright notice and this permission notice appear in all copies. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL Private Octopus, Inc. BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef PICOQUIC_LOG_WRITER_H +#define PICOQUIC_LOG_WRITER_H + +/* +* Packet logging. +*/ +#include +#include +#include +#include "picoquic_internal.h" + +typedef enum { + picoquic_log_event_pdu_sent = 0x0002, + picoquic_log_event_pdu_recv = 0x0003, + + picoquic_log_event_packet_sent = 0x0008, + picoquic_log_event_packet_recv = 0x0009, + + picoquic_log_event_new_connection = 0x0010, + picoquic_log_event_connection_close = 0x0011, + picoquic_log_event_connection_id_update = 0x0012, + picoquic_log_event_packet_lost = 0x0013, + picoquic_log_event_packet_dropped = 0x0014, + picoquic_log_event_packet_buffered = 0x0015, + + picoquic_log_event_tls_key_update = 0x0020, + picoquic_log_event_tls_key_retired = 0x0021, + + picoquic_log_event_version_update = 0x0035, + picoquic_log_event_param_update = 0x0036, + picoquic_log_event_alpn_update = 0x0037, + picoquic_log_event_cc_update = 0x0038, + picoquic_log_event_stream_update = 0x0039, + picoquic_log_event_info_message = 0x003a, + + picoquic_log_event_frame_sent = 0x0082, + picoquic_log_event_frame_recv = 0x0083, +} picoquic_log_event_type; + +/* Log PDU arrival or departure */ +void binlog_pdu(FILE * f, const picoquic_connection_id_t* cid, int receiving, uint64_t current_time, + const struct sockaddr* addr_peer, const struct sockaddr* addr_local, size_t packet_length); + +/* binary alternative to picoquic_log_decrypted_segment() */ +void binlog_packet(FILE * f, const picoquic_connection_id_t* cid, int receiving, uint64_t current_time, + const picoquic_packet_header * ph, const uint8_t* bytes, size_t bytes_max); + +/* Report that a packet was dropped due to some error */ +void binlog_dropped_packet(picoquic_cnx_t* cnx, picoquic_packet_header* ph, size_t packet_size, int err, uint8_t* raw_data, uint64_t current_time); + +/* Report that packet was buffered waiting for decryption */ +void binlog_buffered_packet(picoquic_cnx_t* cnx, picoquic_packet_type_enum ptype, uint64_t current_time); + +/* binary alternative to picoquic_log_outgoing_segment() */ +void binlog_outgoing_packet(picoquic_cnx_t* cnx, + uint8_t * bytes, uint64_t sequence_number, size_t pn_length, size_t length, + uint8_t* send_buffer, size_t send_length, uint64_t current_time); + +/* Logging packet lost events */ +void binlog_packet_lost(picoquic_cnx_t* cnx, + picoquic_packet_type_enum ptype, uint64_t sequence_number, char const* trigger, + picoquic_connection_id_t* dcid, size_t packet_size, + uint64_t current_time); + +/* Logging of SNI and ALPN negotiation */ +void binlog_negotiated_alpn(picoquic_cnx_t* cnx, int is_local, + uint8_t const* sni, size_t sni_len, uint8_t const* alpn, size_t alpn_len, + const ptls_iovec_t* alpn_list, size_t alpn_count); + +/* binary alternative to picoquic_log_transport_extension() */ +void binlog_transport_extension(picoquic_cnx_t * cnx, int is_local, + size_t param_length, uint8_t* params); + +/* binary alternative to picoquic_log_tls_ticket() */ +void binlog_picotls_ticket(FILE* f, picoquic_connection_id_t cnx_id, + uint8_t* ticket, uint16_t ticket_length); + +void binlog_new_connection(picoquic_cnx_t * cnx); +void binlog_close_connection(picoquic_cnx_t * cnx); + +void binlog_cc_dump(picoquic_cnx_t * cnx, uint64_t current_time); + +/* Set the binary log folder and start generating per connection traces into it. + * Set to NULL value to stop binary tracing. + */ +int picoquic_set_binlog(picoquic_quic_t* quic, char const* binlog_dir); + +/* Enable binary logs, e.g. if autoqlog is requests */ +void picoquic_enable_binlog(picoquic_quic_t* quic); + +#endif /* PICOQUIC_LOG_WRITER_H */ diff --git a/deps/picoquic/picoquic_internal.h b/deps/picoquic/picoquic_internal.h new file mode 100644 index 0000000..0413de4 --- /dev/null +++ b/deps/picoquic/picoquic_internal.h @@ -0,0 +1,1434 @@ +/* +* Author: Christian Huitema +* Copyright (c) 2017, Private Octopus, Inc. +* All rights reserved. +* +* Permission to use, copy, modify, and distribute this software for any +* purpose with or without fee is hereby granted, provided that the above +* copyright notice and this permission notice appear in all copies. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL Private Octopus, Inc. BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef PICOQUIC_INTERNAL_H +#define PICOQUIC_INTERNAL_H + +#ifdef _MSC_VER +#pragma warning(disable: 4100) // unreferenced formal parameter +#endif + +#include "picohash.h" +#include "picosplay.h" +#include "picoquic.h" +#include "picoquic_utils.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define PICOQUIC_VERSION "0.32" + +#ifndef PICOQUIC_MAX_PACKET_SIZE +#define PICOQUIC_MAX_PACKET_SIZE 1536 +#endif +#define PICOQUIC_MIN_SEGMENT_SIZE 256 +#define PICOQUIC_ENFORCED_INITIAL_MTU 1200 +#define PICOQUIC_ENFORCED_INITIAL_CID_LENGTH 8 +#define PICOQUIC_PRACTICAL_MAX_MTU 1440 +#define PICOQUIC_RETRY_SECRET_SIZE 64 +#define PICOQUIC_RETRY_TOKEN_PAD_SIZE 26 +#define PICOQUIC_DEFAULT_0RTT_WINDOW (10*PICOQUIC_ENFORCED_INITIAL_MTU) +#define PICOQUIC_NB_PATH_TARGET 8 +#define PICOQUIC_NB_PATH_DEFAULT 2 +#define PICOQUIC_MAX_PACKETS_IN_POOL 0x8000 + +#define PICOQUIC_INITIAL_RTT 250000ull /* 250 ms */ +#define PICOQUIC_TARGET_RENO_RTT 100000ull /* 100 ms */ +#define PICOQUIC_TARGET_SATELLITE_RTT 610000ull /* 610 ms, practical maximum for non-pathological RTT */ +#define PICOQUIC_INITIAL_RETRANSMIT_TIMER 250000ull /* 250 ms */ +#define PICOQUIC_INITIAL_MAX_RETRANSMIT_TIMER 1000000ull /* one second */ +#define PICOQUIC_LARGE_RETRANSMIT_TIMER 2000000ull /* two seconds */ +#define PICOQUIC_MIN_RETRANSMIT_TIMER 50000ull /* 50 ms */ +#define PICOQUIC_ACK_DELAY_MAX 10000ull /* 10 ms */ +#define PICOQUIC_ACK_DELAY_MAX_DEFAULT 25000ull /* 25 ms, per protocol spec */ +#define PICOQUIC_ACK_DELAY_MIN 1000ull /* 1 ms */ +#define PICOQUIC_ACK_DELAY_MIN_MAX_VALUE 0xFFFFFFull /* max value that can be negotiated by peers */ +#define PICOQUIC_RACK_DELAY 10000ull /* 10 ms */ +#define PICOQUIC_MAX_ACK_DELAY_MAX_MS 0x4000ull /* 2<14 ms */ +#define PICOQUIC_TOKEN_DELAY_LONG (24*60*60*1000000ull) /* 24 hours */ +#define PICOQUIC_TOKEN_DELAY_SHORT (2*60*1000000ull) /* 2 minutes */ +#define PICOQUIC_CID_REFRESH_DELAY (5*1000000ull) /* if idle for 5 seconds, refresh the CID */ +#define PICOQUIC_MTU_LOSS_THRESHOLD 10 /* if threshold of full MTU packetlost, reset MTU */ + +#define PICOQUIC_BANDWIDTH_ESTIMATE_MAX 10000000000ull /* 10 GB per second */ +#define PICOQUIC_BANDWIDTH_TIME_INTERVAL_MIN 1000 +#define PICOQUIC_BANDWIDTH_MEDIUM 2000000 /* 16 Mbps, threshold for coalescing 10 packets per ACK */ +#define PICOQUIC_MAX_BANDWIDTH_TIME_INTERVAL_MIN 1000 +#define PICOQUIC_MAX_BANDWIDTH_TIME_INTERVAL_MAX 15000 + +#define PICOQUIC_SPURIOUS_RETRANSMIT_DELAY_MAX 1000000ull /* one second */ + +#define PICOQUIC_MICROSEC_SILENCE_MAX 120000000ull /* 120 seconds for now */ +#define PICOQUIC_MICROSEC_HANDSHAKE_MAX 30000000ull /* 30 seconds for now */ +#define PICOQUIC_MICROSEC_WAIT_MAX 10000000ull /* 10 seconds for now */ + +#define PICOQUIC_CWIN_INITIAL (10 * PICOQUIC_MAX_PACKET_SIZE) +#define PICOQUIC_CWIN_MINIMUM (2 * PICOQUIC_MAX_PACKET_SIZE) + +#define PICOQUIC_DEFAULT_CRYPTO_EPOCH_LENGTH (1<<22) + +#define PICOQUIC_DEFAULT_SIMULTANEOUS_LOGS 32 +#define PICOQUIC_DEFAULT_HALF_OPEN_RETRY_THRESHOLD 64 + +#define PICOQUIC_PN_RANDOM_MIN 0xffff +#define PICOQUIC_PN_RANDOM_RANGE 0x10000 + +#define PICOQUIC_SPIN_RESERVE_MOD_256 17 + +#define PICOQUIC_CHALLENGE_REPEAT_MAX 3 + +#define PICOQUIC_ALPN_NUMBER_MAX 8 + +/* + * Types of frames + */ +typedef enum { + picoquic_frame_type_padding = 0, + picoquic_frame_type_ping = 1, + picoquic_frame_type_ack = 0x02, + picoquic_frame_type_ack_ecn = 0x03, + picoquic_frame_type_reset_stream = 0x04, + picoquic_frame_type_stop_sending = 0x05, + picoquic_frame_type_crypto_hs = 0x06, + picoquic_frame_type_new_token = 0x07, + picoquic_frame_type_stream_range_min = 0x08, + picoquic_frame_type_stream_range_max = 0x0f, + picoquic_frame_type_max_data = 0x10, + picoquic_frame_type_max_stream_data = 0x11, + picoquic_frame_type_max_streams_bidir = 0x12, + picoquic_frame_type_max_streams_unidir = 0x13, + picoquic_frame_type_data_blocked = 0x14, + picoquic_frame_type_stream_data_blocked = 0x15, + picoquic_frame_type_streams_blocked_bidir = 0x16, + picoquic_frame_type_streams_blocked_unidir = 0x17, + picoquic_frame_type_new_connection_id = 0x18, + picoquic_frame_type_retire_connection_id = 0x19, + picoquic_frame_type_path_challenge = 0x1a, + picoquic_frame_type_path_response = 0x1b, + picoquic_frame_type_connection_close = 0x1c, + picoquic_frame_type_application_close = 0x1d, + picoquic_frame_type_handshake_done = 0x1e, + picoquic_frame_type_datagram = 0x30, + picoquic_frame_type_datagram_l = 0x31, + picoquic_frame_type_ack_frequency = 0xAF, + picoquic_frame_type_time_stamp = 757 +} picoquic_frame_type_enum_t; + +/* PMTU discovery requirement status */ + +typedef enum { + picoquic_pmtu_discovery_not_needed = 0, + picoquic_pmtu_discovery_optional, + picoquic_pmtu_discovery_required +} picoquic_pmtu_discovery_status_enum; + +/* + * Efficient range operations that assume range containing bitfields. + * Namely, it assumes max&min==min, min&bits==0, max&bits==bits. + */ +#define PICOQUIC_IN_RANGE(v, min, max) (((v) & ~((min)^(max))) == (min)) + // Is v between min and max and has all given bits set/clear? +#define PICOQUIC_BITS_SET_IN_RANGE( v, min, max, bits) (((v) & ~((min)^(max)^(bits))) == ((min)^(bits))) +#define PICOQUIC_BITS_CLEAR_IN_RANGE(v, min, max, bits) (((v) & ~((min)^(max)^(bits))) == (min)) + + +/* + * Supported versions + */ +#if 0 +#define PICOQUIC_FIRST_INTEROP_VERSION 0xFF000005 +#define PICOQUIC_SECOND_INTEROP_VERSION 0xFF000007 +#define PICOQUIC_THIRD_INTEROP_VERSION 0xFF000008 +#define PICOQUIC_FOURTH_INTEROP_VERSION 0xFF000009 +#define PICOQUIC_FIFTH_INTEROP_VERSION 0xFF00000B +#define PICOQUIC_SIXTH_INTEROP_VERSION 0xFF00000C +#define PICOQUIC_SEVENTH_INTEROP_VERSION 0xFF00000D +#define PICOQUIC_EIGHT_INTEROP_VERSION 0xFF00000E +#define PICOQUIC_NINTH_INTEROP_VERSION 0xFF00000F +#define PICOQUIC_NINTH_BIS_INTEROP_VERSION 0xFF000010 +#define PICOQUIC_TENTH_INTEROP_VERSION 0xFF000011 +#define PICOQUIC_ELEVENTH_INTEROP_VERSION 0xFF000012 +#define PICOQUIC_TWELFTH_INTEROP_DRAFT19 0xFF000013 +#define PICOQUIC_TWELFTH_INTEROP_VERSION 0xFF000014 +#define PICOQUIC_THIRTEENTH_INTEROP_VERSION 0xFF000016 +#define PICOQUIC_FOURTEENTH_INTEROP_VERSION 0xFF000017 +#define PICOQUIC_FIFTEENTH_INTEROP_VERSION 0xFF000018 +#define PICOQUIC_SIXTEENTH_INTEROP_VERSION 0xFF000019 +#endif +#define PICOQUIC_SEVENTEENTH_INTEROP_VERSION 0xFF00001B +#define PICOQUIC_EIGHTEENTH_INTEROP_VERSION 0xFF00001C +#define PICOQUIC_NINETEENTH_INTEROP_VERSION 0xFF00001D +#define PICOQUIC_NINETEENTH_BIS_INTEROP_VERSION 0xFF00001E +#define PICOQUIC_TWENTIETH_PRE_INTEROP_VERSION 0xFF00001F +#define PICOQUIC_TWENTIETH_INTEROP_VERSION 0xFF000020 +#define PICOQUIC_INTERNAL_TEST_VERSION_1 0x50435130 +#define PICOQUIC_INTERNAL_TEST_VERSION_2 0x50435131 + +#define PICOQUIC_INTEROP_VERSION_INDEX 0 + +#define PICOQUIC_INTEROP_VERSION_LATEST PICOQUIC_NINETEENTH_INTEROP_VERSION + + +typedef struct st_picoquic_version_parameters_t { + uint32_t version; + size_t version_aead_key_length; + uint8_t* version_aead_key; + size_t version_retry_key_length; + uint8_t* version_retry_key; +} picoquic_version_parameters_t; + +extern const picoquic_version_parameters_t picoquic_supported_versions[]; +extern const size_t picoquic_nb_supported_versions; + +int picoquic_get_version_index(uint32_t proposed_version); + +/* Quic defines 4 epochs, which are used for managing the + * crypto contexts + */ +#define PICOQUIC_NUMBER_OF_EPOCHS 4 +#define PICOQUIC_NUMBER_OF_EPOCH_OFFSETS (PICOQUIC_NUMBER_OF_EPOCHS+1) + +typedef enum { + picoquic_epoch_initial = 0, + picoquic_epoch_0rtt = 1, + picoquic_epoch_handshake = 2, + picoquic_epoch_1rtt = 3 +} picoquic_epoch_enum; + +/* +* Nominal packet types. These are the packet types used internally by the +* implementation. The wire encoding depends on the version. +*/ +typedef enum { + picoquic_packet_error = 0, + picoquic_packet_version_negotiation, + picoquic_packet_initial, + picoquic_packet_retry, + picoquic_packet_handshake, + picoquic_packet_0rtt_protected, + picoquic_packet_1rtt_protected, + picoquic_packet_type_max +} picoquic_packet_type_enum; + +typedef enum { + picoquic_packet_context_application = 0, + picoquic_packet_context_handshake = 1, + picoquic_packet_context_initial = 2, + picoquic_nb_packet_context = 3 +} picoquic_packet_context_enum; + +/* Packet header structure. + * This structure is used internally when parsing or + * formatting the header of a Quic packet. + */ + +typedef struct st_picoquic_packet_header_t { + picoquic_connection_id_t dest_cnx_id; + picoquic_connection_id_t srce_cnx_id; + uint32_t pn; + uint32_t vn; + size_t offset; /* offset to the first byte of the payload.*/ + size_t pn_offset; /* offset to the first byte of the packet number */ + picoquic_packet_type_enum ptype; + uint64_t pnmask; + uint64_t pn64; + size_t payload_length; + int version_index; + picoquic_epoch_enum epoch; + picoquic_packet_context_enum pc; + + unsigned int key_phase : 1; + unsigned int spin : 1; + unsigned int has_spin_bit : 1; + unsigned int has_reserved_bit_set : 1; + unsigned int has_loss_bits : 1; + unsigned int loss_bit_Q : 1; + unsigned int loss_bit_L : 1; + unsigned int quic_bit_is_zero : 1; + + size_t token_length; + const uint8_t* token_bytes; + size_t pl_val; +} picoquic_packet_header; + +/* There are two loss bits in the packet header. On is used + * to report errors, the other to build an observable square + * wave, of half period Q defined below. + */ +#define PICOQUIC_LOSS_BIT_Q_HALF_PERIOD 64 + +/* + * Management of the spin bit in the packet header. + * We envisage different spin bit policies, and implement + * each policy by 2 function pointers for processing incoming and + * outgoing packets. + */ +typedef void(*picoquic_spinbit_incoming_fn)(picoquic_cnx_t* cnx, picoquic_path_t* path_x, picoquic_packet_header* ph); +typedef uint8_t(*picoquic_spinbit_outgoing_fn)(picoquic_cnx_t* cnx); + +typedef struct st_picoquic_spinbit_def_t { + picoquic_spinbit_incoming_fn spinbit_incoming; + picoquic_spinbit_outgoing_fn spinbit_outgoing; +} picoquic_spinbit_def_t; + +extern picoquic_spinbit_def_t picoquic_spin_function_table[]; + +/* +* The stateless packet structure is used to temporarily store +* stateless packets before they can be sent by servers. +*/ + +typedef struct st_picoquic_stateless_packet_t { + struct st_picoquic_stateless_packet_t* next_packet; + struct sockaddr_storage addr_to; + struct sockaddr_storage addr_local; + int if_index_local; + unsigned char received_ecn; + size_t length; + uint64_t receive_time; + uint64_t cnxid_log64; + picoquic_connection_id_t initial_cid; + picoquic_packet_type_enum ptype; + + uint8_t bytes[PICOQUIC_MAX_PACKET_SIZE]; +} picoquic_stateless_packet_t; + +/* Handling of stateless packets */ +picoquic_stateless_packet_t* picoquic_create_stateless_packet(picoquic_quic_t* quic); +void picoquic_queue_stateless_packet(picoquic_quic_t* quic, picoquic_stateless_packet_t* sp); +picoquic_stateless_packet_t* picoquic_dequeue_stateless_packet(picoquic_quic_t* quic); +void picoquic_delete_stateless_packet(picoquic_stateless_packet_t* sp); + + +/* + * The simple packet structure is used to store packets that + * have been sent but are not yet acknowledged. + * Packets are stored in unencrypted format. + * The checksum length is the difference between encrypted and unencrypted. + */ + +typedef struct st_picoquic_packet_t { + struct st_picoquic_packet_t* previous_packet; + struct st_picoquic_packet_t* next_packet; + struct st_picoquic_path_t* send_path; + uint64_t sequence_number; + uint64_t send_time; + uint64_t delivered_prior; + uint64_t delivered_time_prior; + uint64_t delivered_sent_prior; + size_t length; + size_t checksum_overhead; + size_t offset; + picoquic_packet_type_enum ptype; + picoquic_packet_context_enum pc; + unsigned int is_evaluated : 1; + unsigned int is_pure_ack : 1; + unsigned int contains_crypto : 1; + unsigned int is_mtu_probe : 1; + unsigned int is_ack_trap : 1; + unsigned int delivered_app_limited : 1; + + uint8_t bytes[PICOQUIC_MAX_PACKET_SIZE]; +} picoquic_packet_t; + +picoquic_packet_t* picoquic_create_packet(picoquic_quic_t* quic); +void picoquic_recycle_packet(picoquic_quic_t* quic, picoquic_packet_t* packet); + +/* Definition of the token register used to prevent repeated usage of + * the same new token, retry token, or session ticket. + */ + +typedef struct st_picoquic_registered_token_t { + picosplay_node_t registered_token_node; + uint64_t token_time; + uint64_t token_hash; /* The last 8 bytes of the token, normally taken from AEAD checksum */ + int count; +} picoquic_registered_token_t; + +/* + * Definition of the session ticket store and connection token + * store that can be associated with a + * client context. + */ + +typedef enum { + picoquic_tp_0rtt_max_data = 0, + picoquic_tp_0rtt_max_stream_data_bidi_local = 1, + picoquic_tp_0rtt_max_stream_data_bidi_remote = 2, + picoquic_tp_0rtt_max_stream_data_uni = 3, + picoquic_tp_0rtt_max_streams_id_bidir = 4, + picoquic_tp_0rtt_max_streams_id_unidir = 5 +} picoquic_tp_0rtt_enum; +#define PICOQUIC_NB_TP_0RTT 6 + +typedef struct st_picoquic_stored_ticket_t { + struct st_picoquic_stored_ticket_t* next_ticket; + char* sni; + char* alpn; + uint64_t tp_0rtt[PICOQUIC_NB_TP_0RTT]; + uint8_t* ticket; + uint64_t time_valid_until; + uint16_t sni_length; + uint16_t alpn_length; + uint16_t ticket_length; + unsigned int was_used : 1; +} picoquic_stored_ticket_t; + +int picoquic_store_ticket(picoquic_stored_ticket_t** p_first_ticket, + uint64_t current_time, + char const* sni, uint16_t sni_length, char const* alpn, uint16_t alpn_length, + uint8_t* ticket, uint16_t ticket_length, picoquic_tp_t const * tp); +int picoquic_get_ticket(picoquic_stored_ticket_t* p_first_ticket, + uint64_t current_time, + char const* sni, uint16_t sni_length, char const* alpn, uint16_t alpn_length, + uint8_t** ticket, uint16_t* ticket_length, picoquic_tp_t * tp, int mark_used); + +int picoquic_save_tickets(const picoquic_stored_ticket_t* first_ticket, + uint64_t current_time, char const* ticket_file_name); +int picoquic_load_tickets(picoquic_stored_ticket_t** pp_first_ticket, + uint64_t current_time, char const* ticket_file_name); +void picoquic_free_tickets(picoquic_stored_ticket_t** pp_first_ticket); + +typedef struct st_picoquic_stored_token_t { + struct st_picoquic_stored_token_t* next_token; + char const* sni; + uint8_t const* token; + uint8_t const* ip_addr; + uint64_t time_valid_until; + uint16_t sni_length; + uint16_t token_length; + uint8_t ip_addr_length; + unsigned int was_used : 1; +} picoquic_stored_token_t; + +int picoquic_store_token(picoquic_stored_token_t** p_first_token, + uint64_t current_time, + char const* sni, uint16_t sni_length, + uint8_t const* ip_addr, uint8_t ip_addr_length, + uint8_t const* token, uint16_t token_length); +int picoquic_get_token(picoquic_stored_token_t* p_first_token, + uint64_t current_time, + char const* sni, uint16_t sni_length, + uint8_t const* ip_addr, uint8_t ip_addr_length, + uint8_t** token, uint16_t* token_length, int mark_used); + +int picoquic_save_tokens(const picoquic_stored_token_t* first_token, + uint64_t current_time, char const* token_file_name); +int picoquic_load_tokens(picoquic_stored_token_t** pp_first_token, + uint64_t current_time, char const* token_file_name); +void picoquic_free_tokens(picoquic_stored_token_t** pp_first_token); + +/* + * Transport parameters, as defined by the QUIC transport specification + */ + +typedef enum { + picoquic_tp_original_connection_id = 0, + picoquic_tp_idle_timeout = 1, + picoquic_tp_stateless_reset_token = 2, + picoquic_tp_max_packet_size = 3, + picoquic_tp_initial_max_data = 4, + picoquic_tp_initial_max_stream_data_bidi_local = 5, + picoquic_tp_initial_max_stream_data_bidi_remote = 6, + picoquic_tp_initial_max_stream_data_uni = 7, + picoquic_tp_initial_max_streams_bidi = 8, + picoquic_tp_initial_max_streams_uni = 9, + picoquic_tp_ack_delay_exponent = 10, + picoquic_tp_max_ack_delay = 11, + picoquic_tp_disable_migration = 12, + picoquic_tp_server_preferred_address = 13, + picoquic_tp_active_connection_id_limit = 14, + picoquic_tp_handshake_connection_id = 15, + picoquic_tp_retry_connection_id = 16, + picoquic_tp_max_datagram_frame_size = 32 /* per draft-pauly-quic-datagram-05 */, + picoquic_tp_test_large_chello = 3127, + picoquic_tp_enable_loss_bit_old = 0x1055, + picoquic_tp_enable_loss_bit = 0x1057, + picoquic_tp_min_ack_delay = 0xDE1A, + picoquic_tp_enable_time_stamp = 0x7158, /* x&1 = */ + picoquic_tp_grease_quic_bit = 0x2ab2 +} picoquic_tp_enum; + +/* Callback for converting binary log to quic log at the end of a connection. + * This is kept private for now, and will only be set through the "set quic log" + * API. + */ +typedef int (*picoquic_autoqlog_fn)(picoquic_cnx_t * cnx); + +/* QUIC context, defining the tables of connections, + * open sockets, etc. + */ +typedef struct st_picoquic_quic_t { + void* tls_master_ctx; + struct st_ptls_key_exchange_context_t * esni_key_exchange[16]; + picoquic_stream_data_cb_fn default_callback_fn; + void* default_callback_ctx; + char const* default_alpn; + picoquic_alpn_select_fn alpn_select_fn; + uint8_t reset_seed[PICOQUIC_RESET_SECRET_SIZE]; + uint8_t retry_seed[PICOQUIC_RETRY_SECRET_SIZE]; + uint64_t* p_simulated_time; + char const* ticket_file_name; + char const* token_file_name; + picoquic_stored_ticket_t * p_first_ticket; + picoquic_stored_token_t * p_first_token; + picosplay_tree_t token_reuse_tree; /* detection of token reuse */ + uint32_t mtu_max; + uint32_t padding_multiple_default; + uint32_t padding_minsize_default; + uint32_t sequence_hole_pseudo_period; /* Optimistic ack defense */ + picoquic_spinbit_version_enum default_spin_policy; + uint64_t crypto_epoch_length_max; /* Default packet interval between key rotations */ + uint32_t max_simultaneous_logs; + uint32_t current_number_of_open_logs; + uint32_t max_half_open_before_retry; + uint32_t current_number_half_open; + + /* Flags */ + unsigned int check_token : 1; + unsigned int force_check_token : 1; + unsigned int provide_token : 1; + unsigned int unconditional_cnx_id : 1; + unsigned int client_zero_share : 1; + unsigned int server_busy : 1; + unsigned int is_cert_store_not_empty : 1; + unsigned int use_long_log : 1; + unsigned int should_close_log : 1; + unsigned int dont_coalesce_init : 1; /* test option to turn of packet coalescing on server */ + unsigned int one_way_grease_quic_bit : 1; /* Grease of QUIC bit, but do not announce support */ + unsigned int log_pn_dec : 1; /* Log key hashes on key changes to debug crypto */ + unsigned int random_initial : 1; /* Randomize the initial PN number */ + unsigned int packet_train_mode : 1; /* Tune pacing for sending packet trains */ + + picoquic_stateless_packet_t* pending_stateless_packet; + + picoquic_congestion_algorithm_t const* default_congestion_alg; + + struct st_picoquic_cnx_t* cnx_list; + struct st_picoquic_cnx_t* cnx_last; + picosplay_tree_t cnx_wake_tree; + + struct st_picoquic_cnx_t* cnx_in_progress; + + picohash_table* table_cnx_by_id; + picohash_table* table_cnx_by_net; + picohash_table* table_cnx_by_icid; + picohash_table* table_cnx_by_secret; + + picoquic_packet_t * p_first_packet; + size_t nb_packets_in_pool; + + picoquic_connection_id_cb_fn cnx_id_callback_fn; + void* cnx_id_callback_ctx; + + void* aead_encrypt_ticket_ctx; + void* aead_decrypt_ticket_ctx; + void ** retry_integrity_sign_ctx; + void ** retry_integrity_verify_ctx; + + picoquic_verify_certificate_cb_fn verify_certificate_callback_fn; + picoquic_free_verify_certificate_ctx free_verify_certificate_callback_fn; + void* verify_certificate_ctx; + uint8_t local_cnxid_length; + + picoquic_tp_t * default_tp; + + picoquic_fuzz_fn fuzz_fn; + void* fuzz_ctx; + int wake_file; + int wake_line; + + /* Logging APIS */ + void* F_log; + char* binlog_dir; + char* qlog_dir; + picoquic_autoqlog_fn autoqlog_fn; + struct st_picoquic_unified_logging_t* text_log_fns; + struct st_picoquic_unified_logging_t* bin_log_fns; + struct st_picoquic_unified_logging_t* qlog_fns; +} picoquic_quic_t; + +picoquic_packet_context_enum picoquic_context_from_epoch(int epoch); + +int picoquic_registered_token_check_reuse(picoquic_quic_t* quic, const uint8_t* token, size_t token_length, uint64_t expiry_time); + +void picoquic_registered_token_clear(picoquic_quic_t* quic, uint64_t expiry_time_max); + +/* + * SACK dashboard item, part of connection context. Each item + * holds a range of packet numbers that have been received. + * The same structured is reused in stream management to hold + * a range of bytes that have been received. + */ + +typedef struct st_picoquic_sack_item_t { + struct st_picoquic_sack_item_t* next_sack; + uint64_t start_of_sack_range; + uint64_t end_of_sack_range; +} picoquic_sack_item_t; + +/* + * Stream head. + * Stream contains bytes of data, which are not always delivered in order. + * When in order data is available, the application can read it, + * or a callback can be set. + * + * Streams are maintained in the context of connections, which includes: + * + * - a list of open streams, managed as a "splay" + * - a subset of "output" streams, managed as a double linked list + * + * For each stream, the code maintains a list of received stream segments, managed as + * a "splay" of "stream data nodes". + * + * Two input modes are supported. If streams are marked active, the application receives + * a callback and provides data "just in time". Other streams can just push data using + * "picoquic_add_to_stream", and the data segments will be listed in the "send_queue". + * Segments in the send queue will be sent in order, and the "active" poll for data + * will only happen when all segments are sent. + * + * The stream structure holds a variety of parameters about the state of the stream. + */ + +typedef struct st_picoquic_stream_data_node_t { + picosplay_node_t stream_data_node; + struct st_picoquic_stream_data_node_t* next_stream_data; + uint64_t offset; /* Stream offset of the first octet in "bytes" */ + size_t length; /* Number of octets in "bytes" */ + uint8_t* bytes; +} picoquic_stream_data_node_t; + +typedef struct st_picoquic_stream_head_t { + picosplay_node_t stream_node; /* splay of streams in connection context */ + struct st_picoquic_stream_head_t * next_output_stream; /* link in the list of output streams */ + struct st_picoquic_stream_head_t * previous_output_stream; + uint64_t stream_id; + uint64_t consumed_offset; /* amount of data consumed by the application */ + uint64_t fin_offset; /* If the fin mark is received, index of the byte after last */ + uint64_t maxdata_local; /* flow control limit of how much the peer is authorized to send */ + uint64_t maxdata_remote; /* flow control limit of how much we authorize the peer to send */ + uint64_t local_error; + uint64_t remote_error; + uint64_t local_stop_error; + uint64_t remote_stop_error; + picosplay_tree_t stream_data_tree; /* splay of received stream segments */ + uint64_t sent_offset; /* Amount of data sent in the stream */ + picoquic_stream_data_node_t* send_queue; /* if the stream is not "active", list of data segments ready to send */ + void * app_stream_ctx; + picoquic_stream_direct_receive_fn direct_receive_fn; /* direct receive function, if not NULL */ + void* direct_receive_ctx; /* direct receive context */ + picoquic_sack_item_t first_sack_item; /* Track which parts of the stream were acknowledged by the peer */ + /* Flags describing the state of the stream */ + unsigned int is_active : 1; /* The application is actively managing data sending through callbacks */ + unsigned int fin_requested : 1; /* Application has requested Fin of sending stream */ + unsigned int fin_sent : 1; /* Fin sent to peer */ + unsigned int fin_received : 1; /* Fin received from peer */ + unsigned int fin_signalled : 1; /* After Fin was received from peer, Fin was signalled to the application */ + unsigned int reset_requested : 1; /* Application has requested to reset the stream */ + unsigned int reset_sent : 1; /* Reset stream sent to peer */ + unsigned int reset_received : 1; /* Reset stream received from peer */ + unsigned int reset_signalled : 1; /* After Reset stream received from peer, application was notified */ + unsigned int stop_sending_requested : 1; /* Application has requested to stop sending */ + unsigned int stop_sending_sent : 1; /* Stop sending was sent to peer */ + unsigned int stop_sending_received : 1; /* Stop sending received from peer */ + unsigned int stop_sending_signalled : 1; /* After stop sending received from peer, application was notified */ + unsigned int max_stream_updated : 1; /* After stream was closed in both directions, the max stream id number was updated */ + unsigned int stream_data_blocked_sent : 1; /* If stream_data_blocked has been sent to peer, and no data sent on stream since */ + unsigned int is_output_stream : 1; /* If stream is listed in the output list */ + unsigned int is_closed : 1; /* Stream is closed, closure is accouted for */ +} picoquic_stream_head_t; + +#define IS_CLIENT_STREAM_ID(id) (unsigned int)(((id) & 1) == 0) +#define IS_BIDIR_STREAM_ID(id) (unsigned int)(((id) & 2) == 0) +#define IS_LOCAL_STREAM_ID(id, client_mode) (unsigned int)(((id)^(client_mode)) & 1) +#define STREAM_ID_FROM_RANK(rank, is_server_stream, is_unidir) ((((uint64_t)(rank)-(uint64_t)1)<<2)|(((uint64_t)is_unidir)<<1)|((uint64_t)(is_server_stream))) +#define STREAM_RANK_FROM_ID(id) ((id + 4)>>2) +#define STREAM_TYPE_FROM_ID(id) ((id)&3) +#define NEXT_STREAM_ID_FOR_TYPE(id) ((id)+4) + +/* + * Frame queue. This is used for miscellaneous packets. It is also used for + * various tests, allowing for fault injection. + * + * Misc frames are sent at the next opportunity. + * TODO: consider flagging MISC frames with expected packet type or epoch, + * to avoid creating unexpected protocol errors. + * + * The misc frame are allocated in meory as blobs, starting with the + * misc_frame_header, followed by the misc frame content. + */ + +typedef struct st_picoquic_misc_frame_header_t { + struct st_picoquic_misc_frame_header_t* next_misc_frame; + struct st_picoquic_misc_frame_header_t* previous_misc_frame; + size_t length; + int is_pure_ack; +} picoquic_misc_frame_header_t; + +/* Local CID. + * Local CID are created on demand, and stashed in the CID list. + * When the CID is created, it is registered in the QUIC context as + * pointing to the local connection. We manage collisions, so two + * connections do not use the same context. + * When a CID is associated with a path, we set a pointer from the + * path to the entry in the CID list. If a CID is retired, these pointers + * are nullified. +*/ +typedef struct st_picoquic_local_cnxid_t { + struct st_picoquic_local_cnxid_t* next; + struct st_picoquic_cnx_id_key_t* first_cnx_id; + uint64_t sequence; + picoquic_connection_id_t cnx_id; +} picoquic_local_cnxid_t; + +/* +* Per path context. +* Path contexts are created: +* - At the beginning of the connection for path[0] +* - When sending or receiving packets to a or from new addresses and ports. +* When a path is created, it is assigned a local connection idand a remote connection ID. +* After that, the path has to be validated by a successful challenge/response. +* +* As soon as a path is validated, it moves to position 0. The old path[0] moves to the +* last position, and is marked as deprecated. After about 1 RTT, the path resource +* are freed. (TODO: once we actually support multipath, change that behavior.) +* (TODO: servers should only validate the path after receiving non-probing frames from +* the client.) +* +* Congestion control and spin bit management are path specific. +* Packet numbering is global, see packet context. +*/ + +typedef struct st_picoquic_path_t { + picoquic_local_cnxid_t* p_local_cnxid; + picoquic_connection_id_t remote_cnxid; + + struct st_picoquic_net_id_key_t* first_net_id; + + uint64_t path_sequence; + uint64_t remote_cnxid_sequence; + + /* Peer address. */ + struct sockaddr_storage peer_addr; + struct sockaddr_storage local_addr; + unsigned long if_index_dest; + /* Public reset secret, provisioned by the peer */ + uint8_t reset_secret[PICOQUIC_RESET_SECRET_SIZE]; + /* Challenge used for this path */ + uint64_t challenge_response; + uint64_t challenge[PICOQUIC_CHALLENGE_REPEAT_MAX]; + uint64_t challenge_time; + uint64_t demotion_time; + uint8_t challenge_repeat_count; + + /* flags */ + unsigned int mtu_probe_sent : 1; + unsigned int path_is_published : 1; + unsigned int path_is_activated : 1; + unsigned int challenge_required : 1; + unsigned int challenge_verified : 1; + unsigned int challenge_failed : 1; + unsigned int response_required : 1; + unsigned int path_is_demoted : 1; + unsigned int current_spin : 1; + unsigned int path_is_registered : 1; + unsigned int last_bw_estimate_path_limited : 1; + unsigned int path_cid_rotated : 1; + unsigned int path_is_preferred_path : 1; + + /* number of retransmissions observed on path */ + uint64_t retrans_count; + + /* Time measurement */ + int64_t phase_delay; + uint64_t max_ack_delay; + uint64_t rtt_sample; + uint64_t one_way_delay_sample; + uint64_t smoothed_rtt; + uint64_t rtt_variant; + uint64_t retransmit_timer; + uint64_t rtt_min; + uint64_t max_spurious_rtt; + uint64_t max_reorder_delay; + uint64_t max_reorder_gap; + uint64_t latest_sent_time; + + /* MTU */ + size_t send_mtu; + size_t send_mtu_max_tried; + + /* Bandwidth measurement */ + uint64_t delivered; /* The total amount of data delivered so far on the path */ + uint64_t delivered_last; /* Amount delivered by last bandwidth estimation */ + uint64_t delivered_time_last; /* time last delivered packet was delivered */ + uint64_t delivered_sent_last; /* time last delivered packet was sent */ + uint64_t delivered_limited_index; + uint64_t delivered_last_packet; + uint64_t bandwidth_estimate; /* In bytes per second */ + uint64_t max_sample_acked_time; /* Time max sample was delivered */ + uint64_t max_sample_sent_time; /* Time max sample was sent */ + uint64_t max_sample_delivered; /* Delivered value at time of max sample */ + uint64_t max_bandwidth_estimate; /* In bytes per second */ + + uint64_t received; /* Total amount of bytes received from the path */ + uint64_t receive_rate_epoch; /* Time of last receive rate measurement */ + uint64_t received_prior; /* Total amount received at start of epoch */ + uint64_t receive_rate_estimate; /* In bytes per second */ + uint64_t receive_rate_max; /* In bytes per second */ + + /* Congestion control state */ + uint64_t cwin; + uint64_t bytes_in_transit; + uint64_t last_sender_limited_time; + uint64_t last_time_acked_data_frame_sent; + void* congestion_alg_state; + + /* + * Pacing uses a set of per path variables: + * - pacing_rate: bytes per second. + * - pacing_evaluation_time: last time the path was evaluated. + * - pacing_bucket_nanosec: number of nanoseconds of transmission time that are allowed. + * - pacing_bucket_max: maximum value (capacity) of the leaky bucket. + * - pacing_packet_time_nanosec: number of nanoseconds required to send a full size packet. + * - pacing_packet_time_microsec: max of (packet_time_nano_sec/1024, 1) microsec. + */ + + uint64_t pacing_rate; + uint64_t pacing_evaluation_time; + int64_t pacing_bucket_nanosec; + int64_t pacing_bucket_max; + int64_t pacing_packet_time_nanosec; + uint64_t pacing_packet_time_microsec; + + /* MTU safety tracking */ + uint64_t nb_mtu_losses; + + /* Loss bit data */ + uint64_t total_bytes_lost; /* Sum of length of packet lost on this path */ + uint64_t nb_losses_found; + uint64_t nb_losses_reported; + uint64_t q_square; + +} picoquic_path_t; + +/* Crypto context. There are four such contexts: +* 0: Initial context, with encryption based on a version dependent key, +* 1: 0-RTT context +* 2: Handshake context +* 3: Application data +*/ +typedef struct st_picoquic_crypto_context_t { + void* aead_encrypt; + void* aead_decrypt; + void* pn_enc; /* Used for PN encryption */ + void* pn_dec; /* Used for PN decryption */ +} picoquic_crypto_context_t; + +/* Per epoch sequence/packet context. +* There are three such contexts: +* 0: Application (0-RTT and 1-RTT) +* 1: Handshake +* 2: Initial +* The context holds all the data required to manage acknowledgments and +* retransmissions. +*/ + +typedef struct st_picoquic_packet_context_t { + uint64_t send_sequence; + + picoquic_sack_item_t first_sack_item; + uint64_t next_sequence_hole; + uint64_t time_stamp_largest_received; + uint64_t highest_ack_sent; + uint64_t highest_ack_sent_time; + + uint64_t nb_retransmit; + uint64_t latest_retransmit_time; + uint64_t retransmit_sequence; + uint64_t highest_acknowledged; + uint64_t latest_time_acknowledged; /* time at which the highest acknowledged was sent */ + uint64_t highest_acknowledged_time; /* time at which the highest ack was received */ + uint64_t time_oldest_unack_packet_received; /* first packet that has not been acked yet */ + picoquic_packet_t* retransmit_newest; + picoquic_packet_t* retransmit_oldest; + picoquic_packet_t* retransmitted_newest; + picoquic_packet_t* retransmitted_oldest; + /* ECN Counters */ + uint64_t ecn_ect0_total_local; + uint64_t ecn_ect1_total_local; + uint64_t ecn_ce_total_local; + uint64_t ecn_ect0_total_remote; + uint64_t ecn_ect1_total_remote; + uint64_t ecn_ce_total_remote; + /* Flags */ + unsigned int ack_needed : 1; + unsigned int ack_of_ack_requested : 1; + unsigned int ack_after_fin : 1; + unsigned int sending_ecn_ack : 1; +} picoquic_packet_context_t; + +/* +* Item in the list of the CNX-ID received from the peer, and not +* yet used in a path or a probe. +*/ +typedef struct st_picoquic_cnxid_stash_t { + struct st_picoquic_cnxid_stash_t * next_in_stash; + uint64_t sequence; + picoquic_connection_id_t cnx_id; + uint8_t reset_secret[PICOQUIC_RESET_SECRET_SIZE]; +} picoquic_cnxid_stash_t; + +/* +* Per connection context. +*/ +typedef struct st_picoquic_cnx_t { + picoquic_quic_t* quic; + + /* Management of context retrieval tables */ + + struct st_picoquic_cnx_t* next_in_table; + struct st_picoquic_cnx_t* previous_in_table; + + /* Proposed and negotiated version. Feature flags denote version dependent features */ + uint32_t proposed_version; + int version_index; + + /* Series of flags showing the state or choices of the connection */ + unsigned int is_0RTT_accepted : 1; /* whether 0-RTT is accepted */ + unsigned int remote_parameters_received : 1; /* whether remote parameters where received */ + unsigned int client_mode : 1; /* Is this connection the client side? */ + unsigned int key_phase_enc : 1; /* Key phase used in outgoing packets */ + unsigned int key_phase_dec : 1; /* Key phase expected in incoming packets */ + unsigned int zero_rtt_data_accepted : 1; /* Peer confirmed acceptance of zero rtt data */ + unsigned int sending_ecn_ack : 1; /* ECN data has been received, should be copied in acks */ + unsigned int sent_blocked_frame : 1; /* Blocked frame has been sent */ + unsigned int stream_blocked_bidir_sent : 1; /* If stream_blocked has been sent to peer and no stream limit update since */ + unsigned int stream_blocked_unidir_sent : 1; /* If stream_blocked has been sent to peer and no stream limit update since */ + unsigned int max_stream_data_needed : 1; /* If at least one stream needs more data */ + unsigned int path_demotion_needed : 1; /* If at least one path was recently demoted */ + unsigned int alt_path_challenge_needed : 1; /* If at least one alt path challenge is needed or in progress */ + unsigned int is_handshake_finished : 1; /* If there are no more packets to ack or retransmit in initial or handshake contexts */ + unsigned int is_1rtt_received : 1; /* If at least one 1RTT packet has been received */ + unsigned int is_1rtt_acked : 1; /* If at least one 1RTT packet has been acked by the peer */ + unsigned int has_successful_probe : 1; /* At least one probe was successful */ + unsigned int grease_transport_parameters : 1; /* Exercise greasing of transport parameters */ + unsigned int test_large_chello : 1; /* Add a greasing parameter to test sending CHello on multiple packets */ + unsigned int initial_validated : 1; /* Path has been validated, DOS amplification protection is lifted */ + unsigned int initial_repeat_needed : 1; /* Path has not been validated, repeated initial was received */ + unsigned int is_loss_bit_enabled_incoming : 1; /* Read the loss bits in incoming packets */ + unsigned int is_loss_bit_enabled_outgoing : 1; /* Insert the loss bits in outgoing packets */ + unsigned int is_pmtud_required : 1; /* Force PMTU discovery */ + unsigned int is_ack_frequency_negotiated : 1; /* Ack Frequency extension negotiated */ + unsigned int is_ack_frequency_updated : 1; /* Should send an ack frequency frame asap. */ + unsigned int recycle_sooner_needed : 1; /* There may be a need to recycle "sooner" packets */ + unsigned int is_time_stamp_enabled : 1; /* Read time stamp on on incoming */ + unsigned int is_time_stamp_sent : 1; /* Send time stamp with ACKS */ + unsigned int is_pacing_update_requested : 1; /* Whether the application subscribed to pacing updates */ + unsigned int is_flow_control_limited : 1; /* Flow control window limited to initial value, mostly for tests */ + unsigned int is_hcid_verified : 1; /* Whether the HCID was received from the peer */ + unsigned int do_grease_quic_bit : 1; /* Negotiated grease of QUIC bit */ + unsigned int quic_bit_greased : 1; /* Indicate whether the quic bit was greased at least once */ + unsigned int quic_bit_received_0 : 1; /* Indicate whether the quic bit was received as zero at least once */ + unsigned int is_half_open : 1; /* for server side connections, created but not yet complete */ + + /* Spin bit policy */ + picoquic_spinbit_version_enum spin_policy; + /* Idle timeout in microseconds */ + uint64_t idle_timeout; + /* Local and remote parameters */ + picoquic_tp_t local_parameters; + picoquic_tp_t remote_parameters; + /* Padding policy */ + uint32_t padding_multiple; + uint32_t padding_minsize; + + /* On clients, document the SNI and ALPN expected from the server */ + /* TODO: there may be a need to propose multiple ALPN */ + char const* sni; + char const* alpn; + /* On clients, receives the maximum 0RTT size accepted by server */ + size_t max_early_data_size; + /* Call back function and context */ + picoquic_stream_data_cb_fn callback_fn; + void* callback_ctx; + + /* connection state, ID, etc. Todo: allow for multiple cnxid */ + picoquic_state_enum cnx_state; + picoquic_connection_id_t initial_cnxid; + picoquic_connection_id_t original_cnxid; + struct st_picoquic_net_icid_key_t* net_icid_key; + struct st_picoquic_net_secret_key_t* reset_secret_key; + uint64_t start_time; + uint16_t application_error; + uint16_t local_error; + uint16_t remote_application_error; + uint16_t remote_error; + uint64_t offending_frame_type; + uint16_t retry_token_length; + uint8_t * retry_token; + + /* Next time sending data is expected */ + uint64_t next_wake_time; + picosplay_node_t cnx_wake_node; + + /* TLS context, TLS Send Buffer, streams, epochs */ + void* tls_ctx; + uint64_t crypto_epoch_length_max; + uint64_t crypto_epoch_sequence; + uint64_t crypto_rotation_sequence; + uint64_t crypto_rotation_time_guard; + struct st_ptls_buffer_t* tls_sendbuf; + uint16_t psk_cipher_suite_id; + + picoquic_stream_head_t tls_stream[PICOQUIC_NUMBER_OF_EPOCHS]; /* Separate input/output from each epoch */ + picoquic_crypto_context_t crypto_context[PICOQUIC_NUMBER_OF_EPOCHS]; /* Encryption and decryption objects */ + picoquic_crypto_context_t crypto_context_old; /* Old encryption and decryption context after key rotation */ + picoquic_crypto_context_t crypto_context_new; /* New encryption and decryption context just before key rotation */ + uint64_t crypto_failure_count; + /* Liveness detection */ + uint64_t latest_progress_time; /* last local time at which the connection progressed */ + + /* Sequence and retransmission state */ + picoquic_packet_context_t pkt_ctx[picoquic_nb_packet_context]; + + /* Statistics */ + uint64_t nb_bytes_queued; + uint32_t nb_zero_rtt_sent; + uint32_t nb_zero_rtt_acked; + uint32_t nb_zero_rtt_received; + uint64_t nb_packets_received; + uint64_t nb_trains_sent; + uint64_t nb_trains_short; + uint64_t nb_trains_blocked_cwin; + uint64_t nb_trains_blocked_pacing; + uint64_t nb_trains_blocked_others; + uint64_t nb_packets_sent; + uint64_t nb_packets_logged; + uint64_t nb_retransmission_total; + uint64_t nb_spurious; + uint64_t nb_crypto_key_rotations; + unsigned int cwin_blocked : 1; + unsigned int flow_blocked : 1; + unsigned int stream_blocked : 1; + /* Congestion algorithm */ + picoquic_congestion_algorithm_t const* congestion_alg; + uint64_t pacing_rate_signalled; + uint64_t pacing_increase_threshold; + uint64_t pacing_decrease_threshold; + + /* Data accounting for limiting amplification attacks */ + uint64_t initial_data_received; + uint64_t initial_data_sent; + + /* Flow control information */ + uint64_t data_sent; + uint64_t data_received; + uint64_t maxdata_local; + uint64_t maxdata_remote; + uint64_t max_stream_id_bidir_local; + uint64_t max_stream_id_bidir_local_computed; + uint64_t max_stream_id_unidir_local; + uint64_t max_stream_id_unidir_local_computed; + uint64_t max_stream_id_bidir_remote; + uint64_t max_stream_id_unidir_remote; + + /* Queue for frames waiting to be sent */ + picoquic_misc_frame_header_t* first_misc_frame; + picoquic_misc_frame_header_t* last_misc_frame; + + /* Management of streams */ + picosplay_tree_t stream_tree; + picoquic_stream_head_t * first_output_stream; + picoquic_stream_head_t * last_output_stream; + uint64_t high_priority_stream_id; + uint64_t next_stream_id[4]; + + /* Retransmit queue contains congestion controlled frames that should + * be sent in priority when the congestion window opens. */ + struct st_picoquic_misc_frame_header_t* stream_frame_retransmit_queue; + struct st_picoquic_misc_frame_header_t* stream_frame_retransmit_queue_last; + + /* Management of datagrams */ + picoquic_misc_frame_header_t* first_datagram; + picoquic_misc_frame_header_t* last_datagram; + + /* If not `0`, the connection will send keep alive messages in the given interval. */ + uint64_t keep_alive_interval; + + /* Management of paths */ + picoquic_path_t ** path; + int nb_paths; + int nb_path_alloc; + uint64_t path_sequence_next; + + /* Management of the CNX-ID stash */ + uint64_t retire_cnxid_before; + picoquic_cnxid_stash_t * cnxid_stash_first; + + /* management of local CID stash */ + uint64_t local_cnxid_sequence_next; + int nb_local_cnxid; + picoquic_local_cnxid_t* local_cnxid_first; + + /* Management of ACK frequency */ + uint64_t ack_frequency_sequence_local; + uint64_t ack_gap_local; + uint64_t ack_frequency_delay_local; + uint64_t ack_frequency_sequence_remote; + uint64_t ack_gap_remote; + uint64_t ack_delay_remote; + + /* Copies of packets received too soon */ + picoquic_stateless_packet_t* first_sooner; + picoquic_stateless_packet_t* last_sooner; + + FILE* f_binlog; + char* binlog_file_name; + +} picoquic_cnx_t; + +typedef struct st_picoquic_packet_data_t { + picoquic_path_t* acked_path; /* path for which ACK was received */ + uint64_t last_ack_delay; /* ACK Delay in ACK frame */ + uint64_t last_time_stamp_received; + uint64_t largest_sent_time; /* Send time of ACKed packet (largest number acked) */ + uint64_t delivered_prior; /* Amount delivered prior to that packet */ + uint64_t delivered_time_prior; /* Time last delivery before acked packet sent */ + uint64_t delivered_sent_prior; /* Time this last delivery packet was sent */ + int rs_is_path_limited; /* Whether the path was app limited when packet was sent */ +} picoquic_packet_data_t; + +/* Load the stash of retry tokens. */ +int picoquic_load_token_file(picoquic_quic_t* quic, char const * token_file_name); + +/* Init of transport parameters */ +void picoquic_init_transport_parameters(picoquic_tp_t* tp, int client_mode); + +/* Registration of per path connection ID in server context */ +int picoquic_register_cnx_id(picoquic_quic_t* quic, picoquic_cnx_t* cnx, picoquic_local_cnxid_t* l_cid); + +/* Register or update default address and reset secret */ +int picoquic_register_net_secret(picoquic_cnx_t* cnx); + +/* Management of path */ +int picoquic_create_path(picoquic_cnx_t* cnx, uint64_t start_time, + const struct sockaddr* local_addr, const struct sockaddr* peer_addr); +void picoquic_register_path(picoquic_cnx_t* cnx, picoquic_path_t * path_x); +void picoquic_delete_path(picoquic_cnx_t* cnx, int path_index); +void picoquic_demote_path(picoquic_cnx_t* cnx, int path_index, uint64_t current_time); +void picoquic_promote_path_to_default(picoquic_cnx_t* cnx, int path_index, uint64_t current_time); +void picoquic_delete_abandoned_paths(picoquic_cnx_t* cnx, uint64_t current_time, uint64_t * next_wake_time); +void picoquic_set_path_challenge(picoquic_cnx_t* cnx, int path_id, uint64_t current_time); +int picoquic_find_path_by_address(picoquic_cnx_t* cnx, const struct sockaddr* addr_to, const struct sockaddr* addr_from, int* partial_match); +int picoquic_assign_peer_cnxid_to_path(picoquic_cnx_t* cnx, int path_id); +void picoquic_reset_path_mtu(picoquic_path_t* path_x); +int picoquic_probe_new_path_ex(picoquic_cnx_t* cnx, const struct sockaddr* addr_from, + const struct sockaddr* addr_to, uint64_t current_time, int to_preferred_address); + +/* Management of the CNX-ID stash */ +picoquic_cnxid_stash_t * picoquic_dequeue_cnxid_stash(picoquic_cnx_t* cnx); + +int picoquic_enqueue_cnxid_stash(picoquic_cnx_t * cnx, + const uint64_t sequence, const uint8_t cid_length, const uint8_t * cnxid_bytes, + const uint8_t * secret_bytes, picoquic_cnxid_stash_t ** pstashed); + +int picoquic_remove_not_before_cid(picoquic_cnx_t* cnx, uint64_t not_before, uint64_t current_time); +int picoquic_renew_path_connection_id(picoquic_cnx_t* cnx, picoquic_path_t* path_x); + +/* handling of retransmission queue */ +picoquic_packet_t* picoquic_dequeue_retransmit_packet(picoquic_cnx_t* cnx, picoquic_packet_t* p, int should_free); +void picoquic_dequeue_retransmitted_packet(picoquic_cnx_t* cnx, picoquic_packet_t* p); + +#if 0 +/* Reset connection after receiving version negotiation */ +int picoquic_reset_cnx_version(picoquic_cnx_t* cnx, uint8_t* bytes, size_t length, uint64_t current_time); +#endif + +/* Reset the connection context, e.g. after retry */ +int picoquic_reset_cnx(picoquic_cnx_t* cnx, uint64_t current_time); + +/* Reset packet context */ +void picoquic_reset_packet_context(picoquic_cnx_t* cnx, + picoquic_packet_context_enum pc); + +/* Notify error on connection */ +int picoquic_connection_error(picoquic_cnx_t* cnx, uint16_t local_error, uint64_t frame_type); + +/* Connection context retrieval functions */ +picoquic_cnx_t* picoquic_cnx_by_id(picoquic_quic_t* quic, picoquic_connection_id_t cnx_id); +picoquic_cnx_t* picoquic_cnx_by_net(picoquic_quic_t* quic, const struct sockaddr* addr); +picoquic_cnx_t* picoquic_cnx_by_icid(picoquic_quic_t* quic, picoquic_connection_id_t* icid, + const struct sockaddr* addr); +picoquic_cnx_t* picoquic_cnx_by_secret(picoquic_quic_t* quic, const uint8_t* reset_secret, const struct sockaddr* addr); + +/* Reset the pacing data after CWIN is updated */ +void picoquic_update_pacing_data(picoquic_cnx_t* cnx, picoquic_path_t * path_x, int slow_start); +void picoquic_update_pacing_after_send(picoquic_path_t* path_x, uint64_t current_time); +int picoquic_is_sending_authorized_by_pacing(picoquic_cnx_t* cnx, picoquic_path_t* path_x, uint64_t current_time, uint64_t* next_time); +/* Reset pacing data if congestion algorithm computes it directly */ +void picoquic_update_pacing_rate(picoquic_cnx_t* cnx, picoquic_path_t* path_x, double pacing_rate, uint64_t quantum); + +/* Next time is used to order the list of available connections, + * so ready connections are polled first */ +void picoquic_reinsert_by_wake_time(picoquic_quic_t* quic, picoquic_cnx_t* cnx, uint64_t next_time); + +/* Integer parsing macros */ +#define PICOPARSE_16(b) ((((uint16_t)(b)[0]) << 8) | (uint16_t)((b)[1])) +#define PICOPARSE_24(b) ((((uint32_t)PICOPARSE_16(b)) << 8) | (uint32_t)((b)[2])) +#define PICOPARSE_32(b) ((((uint32_t)PICOPARSE_16(b)) << 16) | (uint32_t)PICOPARSE_16((b) + 2)) +#define PICOPARSE_64(b) ((((uint64_t)PICOPARSE_32(b)) << 32) | (uint64_t)PICOPARSE_32((b) + 4)) + +/* Integer formatting functions */ +void picoformat_16(uint8_t* bytes, uint16_t n16); +void picoformat_24(uint8_t* bytes, uint32_t n24); +void picoformat_32(uint8_t* bytes, uint32_t n32); +void picoformat_64(uint8_t* bytes, uint64_t n64); + +size_t picoquic_varint_encode(uint8_t* bytes, size_t max_bytes, uint64_t n64); +void picoquic_varint_encode_16(uint8_t* bytes, uint16_t n16); +size_t picoquic_varint_decode(const uint8_t* bytes, size_t max_bytes, uint64_t* n64); +const uint8_t* picoquic_frames_varint_decode(const uint8_t* bytes, const uint8_t* bytes_max, uint64_t* n64); +const uint8_t* picoquic_frames_varint_skip(const uint8_t* bytes, const uint8_t* bytes_max); +size_t picoquic_varint_skip(const uint8_t* bytes); + +size_t picoquic_encode_varint_length(uint64_t n64); +size_t picoquic_decode_varint_length(uint8_t byte); + +/* Packet parsing */ + +int picoquic_parse_packet_header( + picoquic_quic_t* quic, + const uint8_t* bytes, + size_t length, + const struct sockaddr* addr_from, + picoquic_packet_header* ph, + picoquic_cnx_t** pcnx, + int receiving); + +size_t picoquic_create_packet_header( + picoquic_cnx_t* cnx, + picoquic_packet_type_enum packet_type, + uint64_t sequence_number, + picoquic_connection_id_t * dest_cnxid, + picoquic_connection_id_t * srce_cnxid, + size_t header_length, + uint8_t* bytes, + size_t* pn_offset, + size_t* pn_length); + +size_t picoquic_predict_packet_header_length( + picoquic_cnx_t* cnx, + picoquic_packet_type_enum packet_type); + +void picoquic_update_payload_length( + uint8_t* bytes, size_t pnum_index, size_t header_length, size_t packet_length); + +size_t picoquic_get_checksum_length(picoquic_cnx_t* cnx, picoquic_epoch_enum is_cleartext_mode); + +uint64_t picoquic_get_packet_number64(uint64_t highest, uint64_t mask, uint32_t pn); + +void picoquic_log_pn_dec_trial(picoquic_cnx_t* cnx); /* For debugging potential PN_ENC corruption */ + +void picoquic_finalize_and_protect_packet(picoquic_cnx_t *cnx, picoquic_packet_t * packet, int ret, + size_t length, size_t header_length, size_t checksum_overhead, + size_t * send_length, uint8_t * send_buffer, size_t send_buffer_max, + picoquic_connection_id_t * remote_cnxid, + picoquic_connection_id_t * local_cnxid, + picoquic_path_t * path_x, uint64_t current_time); + +void picoquic_implicit_handshake_ack(picoquic_cnx_t* cnx, picoquic_packet_context_enum pc, uint64_t current_time); +void picoquic_ready_state_transition(picoquic_cnx_t* cnx, uint64_t current_time); + +int picoquic_parse_header_and_decrypt( + picoquic_quic_t* quic, + const uint8_t* bytes, + size_t length, + size_t packet_length, + const struct sockaddr* addr_from, + uint64_t current_time, + picoquic_packet_header* ph, + picoquic_cnx_t** pcnx, + size_t * consumed, + int * new_context_created); + +/* handling of ACK logic */ +int picoquic_is_ack_needed(picoquic_cnx_t* cnx, uint64_t current_time, uint64_t * next_wake_time, picoquic_packet_context_enum pc); + +int picoquic_is_pn_already_received(picoquic_cnx_t* cnx, + picoquic_packet_context_enum pc, uint64_t pn64); +int picoquic_record_pn_received(picoquic_cnx_t* cnx, + picoquic_packet_context_enum pc, uint64_t pn64, uint64_t current_microsec); + +int picoquic_update_sack_list(picoquic_sack_item_t* sack, + uint64_t pn64_min, uint64_t pn64_max); +/* Check whether the data fills a hole. returns 0 if it does, -1 otherwise. */ +int picoquic_check_sack_list(picoquic_sack_item_t* sack, + uint64_t pn64_min, uint64_t pn64_max); + +/* + * Process ack of ack + */ +int picoquic_process_ack_of_ack_frame( + picoquic_sack_item_t* first_sack, + uint8_t* bytes, size_t bytes_max, size_t* consumed, int is_ecn); + +/* Computation of ack delay max and ack gap, based on RTT and Data Rate. + * If ACK Frequency extension is used, these functions will compute the values + * that will be sent to the peer. Otherwise, they computes the values used locally. + */ + +uint64_t picoquic_compute_ack_gap(picoquic_cnx_t* cnx, uint64_t data_rate); + +uint64_t picoquic_compute_ack_delay_max(uint64_t rtt, uint64_t remote_min_ack_delay); + +/* Update the path RTT upon receiving an explict or implicit acknowledgement */ +void picoquic_update_path_rtt(picoquic_cnx_t* cnx, picoquic_path_t * old_path, uint64_t send_time, + uint64_t current_time, uint64_t ack_delay); + +/* stream management */ +picoquic_stream_head_t* picoquic_create_stream(picoquic_cnx_t* cnx, uint64_t stream_id); +picoquic_stream_head_t* picoquic_create_missing_streams(picoquic_cnx_t* cnx, uint64_t stream_id, int is_remote); +int picoquic_is_stream_closed(picoquic_stream_head_t* stream, int client_mode); +int picoquic_delete_stream_if_closed(picoquic_cnx_t* cnx, picoquic_stream_head_t* stream); + +void picoquic_update_stream_initial_remote(picoquic_cnx_t* cnx); + +picoquic_stream_head_t * picoquic_stream_from_node(picosplay_node_t * node); +void picoquic_insert_output_stream(picoquic_cnx_t* cnx, picoquic_stream_head_t * stream); +void picoquic_remove_output_stream(picoquic_cnx_t* cnx, picoquic_stream_head_t * stream, picoquic_stream_head_t * previous_stream); +picoquic_stream_head_t * picoquic_first_stream(picoquic_cnx_t * cnx); +picoquic_stream_head_t * picoquic_last_stream(picoquic_cnx_t * cnx); +picoquic_stream_head_t * picoquic_next_stream(picoquic_stream_head_t * stream); +picoquic_stream_head_t* picoquic_find_stream(picoquic_cnx_t* cnx, uint64_t stream_id); +void picoquic_add_output_streams(picoquic_cnx_t * cnx, uint64_t old_limit, uint64_t new_limit, unsigned int is_bidir); +picoquic_stream_head_t* picoquic_find_ready_stream(picoquic_cnx_t* cnx); +int picoquic_is_tls_stream_ready(picoquic_cnx_t* cnx); +const uint8_t* picoquic_decode_stream_frame(picoquic_cnx_t* cnx, const uint8_t* bytes, + const uint8_t* bytes_max, uint64_t current_time); + +uint8_t* picoquic_format_stream_frame(picoquic_cnx_t* cnx, picoquic_stream_head_t* stream, + uint8_t* bytes, uint8_t* bytes_max, int* more_data, int* is_pure_ack, int* is_still_active, int* ret); + +void picoquic_update_max_stream_ID_local(picoquic_cnx_t* cnx, picoquic_stream_head_t* stream); + +/* Handling of retransmission of frames. + * When a packet is deemed lost, the code looks at the frames that it contained and + * calls "picoquic_check_frame_needs_repeat" to see whether a given frame needs to + * be retransmitted. This is different from checking whether a frame needs to be acked. + * For example, a "MAX DATA" frame needs to be acked, but it will only be retransmitted + * if it was not superceded by a similar frame carrying a larger max value. + * + * May have to split a retransmitted stream frame if it does not fit in the new packet size */ +int picoquic_check_frame_needs_repeat(picoquic_cnx_t* cnx, const uint8_t* bytes, + size_t bytes_max, int* no_need_to_repeat); + +uint8_t* picoquic_format_available_stream_frames(picoquic_cnx_t* cnx, uint8_t* bytes_next, uint8_t* bytes_max, + int* more_data, int* is_pure_ack, int* stream_tried_and_failed, int* ret); +uint8_t* picoquic_format_stream_frame_for_retransmit(picoquic_cnx_t* cnx, + uint8_t* bytes_next, uint8_t* bytes_max, int* is_pure_ack); +uint8_t* picoquic_format_stream_frames_queued_for_retransmit(picoquic_cnx_t* cnx, uint8_t* bytes_next, uint8_t* bytes_max, int* more_data, int* is_pure_ack); +int picoquic_copy_before_retransmit(picoquic_packet_t * old_p, + picoquic_cnx_t * cnx, + uint8_t * new_bytes, + size_t send_buffer_max_minus_checksum, + int * packet_is_pure_ack, + int * do_not_detect_spurious, + size_t * length); + +void picoquic_set_ack_needed(picoquic_cnx_t* cnx, uint64_t current_time, picoquic_packet_context_enum pc); + +/* Coding and decoding of frames */ + +int picoquic_is_stream_frame_unlimited(const uint8_t* bytes); +int picoquic_parse_stream_header( + const uint8_t* bytes, size_t bytes_max, + uint64_t* stream_id, uint64_t* offset, size_t* data_length, int* fin, + size_t* consumed); + +int picoquic_parse_ack_header( + uint8_t const* bytes, size_t bytes_max, + uint64_t* num_block, uint64_t* largest, + uint64_t* ack_delay, size_t* consumed, + uint8_t ack_delay_exponent); +const uint8_t* picoquic_decode_crypto_hs_frame(picoquic_cnx_t* cnx, const uint8_t* bytes, + const uint8_t* bytes_max, int epoch); +uint8_t* picoquic_format_crypto_hs_frame(picoquic_stream_head_t* stream, uint8_t* bytes, uint8_t* bytes_max, int* more_data, int* is_pure_ack); +uint8_t* picoquic_format_ack_frame(picoquic_cnx_t* cnx, uint8_t* bytes, uint8_t* bytes_max, int* more_data, uint64_t current_time, picoquic_packet_context_enum pc); +uint8_t* picoquic_format_connection_close_frame(picoquic_cnx_t* cnx, uint8_t* bytes, uint8_t* bytes_max, int* more_data, int* is_pure_ack); +uint8_t* picoquic_format_application_close_frame(picoquic_cnx_t* cnx, uint8_t* bytes, uint8_t* bytes_max, int* more_data, int* is_pure_ack); +uint8_t* picoquic_format_required_max_stream_data_frames(picoquic_cnx_t* cnx, uint8_t* bytes, uint8_t* bytes_max, int* more_data, int* is_pure_ack); +uint8_t* picoquic_format_max_data_frame(picoquic_cnx_t* cnx, uint8_t* bytes, uint8_t* bytes_max, int* more_data, int* is_pure_ack, uint64_t maxdata_increase); +uint8_t* picoquic_format_max_stream_data_frame(picoquic_stream_head_t* stream, uint8_t* bytes, uint8_t* bytes_max, int* more_data, int* is_pure_ack, uint64_t new_max_data); +uint64_t picoquic_cc_increased_window(picoquic_cnx_t* cnx, uint64_t previous_window); /* Trigger sending more data if window increases */ +uint8_t* picoquic_format_max_streams_frame_if_needed(picoquic_cnx_t* cnx, uint8_t* bytes, uint8_t* bytes_max, int* more_data, int* is_pure_ack); +void picoquic_clear_stream(picoquic_stream_head_t* stream); +void picoquic_delete_stream(picoquic_cnx_t * cnx, picoquic_stream_head_t * stream); +picoquic_local_cnxid_t* picoquic_create_local_cnxid(picoquic_cnx_t* cnx, picoquic_connection_id_t* suggested_value); +void picoquic_delete_local_cnxid(picoquic_cnx_t* cnx, picoquic_local_cnxid_t* l_cid); +void picoquic_retire_local_cnxid(picoquic_cnx_t* cnx, uint64_t sequence); +picoquic_local_cnxid_t* picoquic_find_local_cnxid(picoquic_cnx_t* cnx, picoquic_connection_id_t* cnxid); +uint8_t* picoquic_format_path_challenge_frame(uint8_t* bytes, uint8_t* bytes_max, int* more_data, int* is_pure_ack, uint64_t challenge); +uint8_t* picoquic_format_path_response_frame(uint8_t* bytes, uint8_t* bytes_max, int* more_data, int* is_pure_ack, uint64_t challenge); +uint8_t* picoquic_format_new_connection_id_frame(picoquic_cnx_t* cnx, uint8_t* bytes, uint8_t* bytes_max, int* more_data, int* is_pure_ack, picoquic_local_cnxid_t* l_cid); +uint8_t* picoquic_format_blocked_frames(picoquic_cnx_t* cnx, uint8_t* bytes, uint8_t* bytes_max, int* more_data, int* is_pure_ack); +int picoquic_queue_retire_connection_id_frame(picoquic_cnx_t * cnx, uint64_t sequence); +int picoquic_queue_new_token_frame(picoquic_cnx_t * cnx, uint8_t * token, size_t token_length); +uint8_t* picoquic_format_one_blocked_frame(picoquic_cnx_t* cnx, uint8_t* bytes, uint8_t* bytes_max, int* more_data, int* is_pure_ack, picoquic_stream_head_t* stream); +uint8_t* picoquic_format_first_misc_or_dg_frame(uint8_t* bytes, uint8_t* bytes_max, int* more_data, int* is_pure_ack, picoquic_misc_frame_header_t** first, picoquic_misc_frame_header_t** last); +uint8_t* picoquic_format_first_misc_frame(picoquic_cnx_t* cnx, uint8_t* bytes, uint8_t* bytes_max, int* more_data, int* is_pure_ack); +int picoquic_queue_misc_or_dg_frame(picoquic_cnx_t* cnx, picoquic_misc_frame_header_t** first, picoquic_misc_frame_header_t** last, const uint8_t* bytes, size_t length, int is_pure_ack); +void picoquic_delete_misc_or_dg(picoquic_misc_frame_header_t** first, picoquic_misc_frame_header_t** last, picoquic_misc_frame_header_t* frame); +int picoquic_queue_handshake_done_frame(picoquic_cnx_t* cnx); +uint8_t* picoquic_format_first_datagram_frame(picoquic_cnx_t* cnx, uint8_t* bytes, uint8_t* bytes_max, int* more_data, int* is_pure_ack); +const uint8_t* picoquic_parse_ack_frequency_frame(const uint8_t* bytes, const uint8_t* bytes_max, uint64_t* seq, uint64_t* packets, uint64_t* microsec); +uint8_t* picoquic_format_ack_frequency_frame(picoquic_cnx_t* cnx, uint8_t* bytes, uint8_t* bytes_max, int* more_data); +uint8_t* picoquic_format_time_stamp_frame(picoquic_cnx_t* cnx, uint8_t* bytes, uint8_t* bytes_max, int* more_data, uint64_t current_time); +size_t picoquic_encode_time_stamp_length(picoquic_cnx_t* cnx, uint64_t current_time); + +int picoquic_decode_frames(picoquic_cnx_t* cnx, picoquic_path_t * path_x, const uint8_t* bytes, size_t bytes_max, + int epoch, struct sockaddr* addr_from, struct sockaddr* addr_to, uint64_t current_time); + +int picoquic_skip_frame(const uint8_t* bytes, size_t bytes_max, size_t* consumed, int* pure_ack); + +int picoquic_decode_closing_frames(uint8_t* bytes, size_t bytes_max, int* closing_received); + +uint64_t picoquic_decode_transport_param_stream_id(uint64_t rank, int extension_mode, int stream_type); +uint64_t picoquic_prepare_transport_param_stream_id(uint64_t stream_id); + +void picoquic_process_sooner_packets(picoquic_cnx_t* cnx, uint64_t current_time); +void picoquic_delete_sooner_packets(picoquic_cnx_t* cnx); + +/* handling of transport extensions. + */ + +int picoquic_prepare_transport_extensions(picoquic_cnx_t* cnx, int extension_mode, + uint8_t* bytes, size_t bytes_max, size_t* consumed); + +int picoquic_receive_transport_extensions(picoquic_cnx_t* cnx, int extension_mode, + uint8_t* bytes, size_t bytes_max, size_t* consumed); + +picoquic_misc_frame_header_t* picoquic_create_misc_frame(const uint8_t* bytes, size_t length, int is_pure_ack); + +#ifdef __cplusplus +} +#endif +#endif /* PICOQUIC_INTERNAL_H */ diff --git a/deps/picoquic/picoquic_logger.h b/deps/picoquic/picoquic_logger.h new file mode 100644 index 0000000..fd0175b --- /dev/null +++ b/deps/picoquic/picoquic_logger.h @@ -0,0 +1,43 @@ +/* +* Author: Christian Huitema +* Copyright (c) 2018, Private Octopus, Inc. +* All rights reserved. +* +* Permission to use, copy, modify, and distribute this software for any +* purpose with or without fee is hereby granted, provided that the above +* copyright notice and this permission notice appear in all copies. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL Private Octopus, Inc. BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef PICOQUIC_LOGGER_H +#define PICOQUIC_LOGGER_H +#include +#include "picoquic.h" +#include "picoquic_unified_log.h" + + +/* Set the text log file and start tracing into it. + * Set to NULL value to stop text log. + */ +int picoquic_set_textlog(picoquic_quic_t* quic, char const* textlog_file); + +/* Handling of packet logging */ + +void picoquic_log_prefix_initial_cid64(FILE* F, uint64_t log_cnxid64); + +void picoquic_log_picotls_ticket(FILE* F, picoquic_connection_id_t cnx_id, + uint8_t* ticket, uint16_t ticket_length); + +const char* picoquic_log_fin_or_event_name(picoquic_call_back_event_t ev); + +#endif /* PICOQUIC_LOGGER_H */ \ No newline at end of file diff --git a/deps/picoquic/picoquic_packet_loop.h b/deps/picoquic/picoquic_packet_loop.h new file mode 100644 index 0000000..d94bfb2 --- /dev/null +++ b/deps/picoquic/picoquic_packet_loop.h @@ -0,0 +1,62 @@ +/* +* Author: Christian Huitema +* Copyright (c) 2020, Private Octopus, Inc. +* All rights reserved. +* +* Permission to use, copy, modify, and distribute this software for any +* purpose with or without fee is hereby granted, provided that the above +* copyright notice and this permission notice appear in all copies. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL Private Octopus, Inc. BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef PICOQUIC_PACKET_LOOP_H +#define PICOQUIC_PACKET_LOOP_H + +#include "picosocks.h" +#include "picoquic.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define PICOQUIC_PACKET_LOOP_SOCKETS_MAX 2 +#define PICOQUIC_PACKET_LOOP_SEND_MAX 10 + +typedef enum { + picoquic_packet_loop_ready = 0, + picoquic_packet_loop_after_receive, + picoquic_packet_loop_after_send +} picoquic_packet_loop_cb_enum; + +typedef int (*picoquic_packet_loop_cb_fn)(picoquic_quic_t * quic, picoquic_packet_loop_cb_enum cb_mode, void * callback_ctx); + +int picoquic_packet_loop(picoquic_quic_t* quic, + int local_port, + int local_af, + int dest_if, + picoquic_packet_loop_cb_fn loop_callback, + void * loop_callback_ctx); + +#ifdef _WINDOWS +int picoquic_packet_loop_win(picoquic_quic_t* quic, + int local_port, + int local_af, + int dest_if, + picoquic_packet_loop_cb_fn loop_callback, + void* loop_callback_ctx); +#endif + +#ifdef __cplusplus +} +#endif +#endif /* PICOQUIC_PACKET_LOOP_H */ \ No newline at end of file diff --git a/deps/picoquic/picoquic_unified_log.h b/deps/picoquic/picoquic_unified_log.h new file mode 100644 index 0000000..8be4e55 --- /dev/null +++ b/deps/picoquic/picoquic_unified_log.h @@ -0,0 +1,189 @@ +/* +* Author: Christian Huitema +* Copyright (c) 2020, Private Octopus, Inc. +* All rights reserved. +* +* Permission to use, copy, modify, and distribute this software for any +* purpose with or without fee is hereby granted, provided that the above +* copyright notice and this permission notice appear in all copies. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL Private Octopus, Inc. BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef PICOQUIC_UNIFIED_LOG_H +#define PICOQUIC_UNIFIED_LOG_H + +/* +* Unified logging API. +* +* The logging code may be a significant part of the total application code. +* For a variety of reasons, the code supports three modes of logging: +* - logging to a text file +* - structured logging to a binary file +* - structured logging to a qlog file +* We don't want to link all applications with the code for all three forms of +* logging, because that would inflate the size of the binaries. Instead, +* each logging option is documented by a set of function pointers in an +* "unified logging" structure. If the logging code is used, the structure +* is documented in the QUIC context created by the application. +* +* The application may document all three contexts if it wants to keep +* three types of logs, but it does not have to do that. If a logging +* type is documented, all three functions for that type shall +* be documented as well. +*/ +#include "picoquic.h" +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* Log an event that cannot be attached to a specific connection */ +typedef void (*picoquic_log_quic_app_message_fn)(picoquic_quic_t* quic, const picoquic_connection_id_t* cid, const char* fmt, va_list vargs); + +/* Log arrival or departure of an UDP datagram for an unknown connection */ +typedef void (*picoquic_log_quic_pdu_fn)(picoquic_quic_t* quic, int receiving, uint64_t current_time, uint64_t cid64, + const struct sockaddr* addr_peer, const struct sockaddr* addr_local, size_t packet_length); + +/* Log an event relating to a specific connection */ +typedef void (*picoquic_log_app_message_fn)(picoquic_cnx_t* cnx, const char* fmt, va_list vargs); + +/* Log arrival or departure of an UDP datagram on a connection */ +typedef void (*picoquic_log_pdu_fn)(picoquic_cnx_t* cnx, int receiving, uint64_t current_time, + const struct sockaddr* addr_peer, const struct sockaddr* addr_local, size_t packet_length); + +/* Log a decrypted packet - receiving = 1 if arrival, = 0 if sending */ +typedef void (*picoquic_log_packet_fn)(picoquic_cnx_t* cnx, int receiving, uint64_t current_time, + struct st_picoquic_packet_header_t* ph, const uint8_t* bytes, size_t bytes_max); + +/* Report that a packet was dropped due to some error */ +typedef void (*picoquic_log_dropped_packet_fn)(picoquic_cnx_t* cnx, struct st_picoquic_packet_header_t* ph, size_t packet_size, int err, uint8_t* raw_data, uint64_t current_time); + +/* Report that packet was buffered waiting for decryption */ +typedef void (*picoquic_log_buffered_packet_fn)(picoquic_cnx_t* cnx, picoquic_packet_type_enum ptype, uint64_t current_time); + +/* Log that a packet was formatted, ready to be sent. */ +typedef void (*picoquic_log_outgoing_packet_fn)(picoquic_cnx_t* cnx, + uint8_t* bytes, uint64_t sequence_number, size_t pn_length, size_t length, + uint8_t* send_buffer, size_t send_length, uint64_t current_time); + +/* Log packet lost events */ +typedef void (*picoquic_log_packet_lost_fn)(picoquic_cnx_t* cnx, + picoquic_packet_type_enum ptype, uint64_t sequence_number, char const* trigger, + picoquic_connection_id_t* dcid, size_t packet_size, + uint64_t current_time); + +/* log negotiated ALPN */ +typedef void (*picoquic_log_negotiated_alpn_fn)(picoquic_cnx_t* cnx, int is_local, + uint8_t const* sni, size_t sni_len, uint8_t const* alpn, size_t alpn_len, + const ptls_iovec_t* alpn_list, size_t alpn_count); + +/* log transport extension -- either formatted by the loacl peer (is_local=1) or received from remote peer */ +typedef void (*picoquic_log_transport_extension_fn)(picoquic_cnx_t* cnx, int is_local, + size_t param_length, uint8_t* params); + +/* log TLS ticket */ +typedef void (*picoquic_log_tls_ticket_fn)(picoquic_cnx_t * cnx, + uint8_t* ticket, uint16_t ticket_length); + +/* log the start of a connection */ +typedef void (*picoquic_log_new_connection_fn)(picoquic_cnx_t* cnx); +/* log the end of a connection */ +typedef void (*picoquic_log_close_connection_fn)(picoquic_cnx_t* cnx); + +/* log congestion control parameters */ +typedef void (*picoquic_log_cc_dump_fn)(picoquic_cnx_t* cnx, uint64_t current_time); + +typedef struct st_picoquic_unified_logging_t { + /* Per context log function */ + picoquic_log_quic_app_message_fn log_quic_app_message; + picoquic_log_quic_pdu_fn log_quic_pdu; + /* Per connection functions */ + picoquic_log_app_message_fn log_app_message; + picoquic_log_pdu_fn log_pdu; + picoquic_log_packet_fn log_packet; + picoquic_log_dropped_packet_fn log_dropped_packet; + picoquic_log_buffered_packet_fn log_buffered_packet; + picoquic_log_outgoing_packet_fn log_outgoing_packet; + picoquic_log_packet_lost_fn log_packet_lost; + picoquic_log_negotiated_alpn_fn log_negotiated_alpn; + picoquic_log_transport_extension_fn log_transport_extension; + picoquic_log_tls_ticket_fn log_picotls_ticket; + picoquic_log_new_connection_fn log_new_connection; + picoquic_log_close_connection_fn log_close_connection; + picoquic_log_cc_dump_fn log_cc_dump; +} picoquic_unified_logging_t; + +/* Log an event that cannot be attached to a specific connection */ +void picoquic_log_context_free_app_message(picoquic_quic_t* quic, const picoquic_connection_id_t* cid, const char* fmt, ...); + +/* Log arrival or departure of an UDP datagram for an unknown connection */ +void picoquic_log_quic_pdu(picoquic_quic_t* quic, int receiving, uint64_t current_time, uint64_t cid64, + const struct sockaddr* addr_peer, const struct sockaddr* addr_local, size_t packet_length); + +/* Log an event relating to a specific connection */ +void picoquic_log_app_message(picoquic_cnx_t* cnx, const char* fmt, ...); + +/* Log arrival or departure of an UDP datagram on a connection */ +void picoquic_log_pdu(picoquic_cnx_t* cnx, int receiving, uint64_t current_time, + const struct sockaddr* addr_peer, const struct sockaddr* addr_local, size_t packet_length); + +/* Log a decrypted packet - receiving = 1 if arrival, = 0 if sending */ +void picoquic_log_packet(picoquic_cnx_t* cnx, int receiving, uint64_t current_time, + struct st_picoquic_packet_header_t* ph, const uint8_t* bytes, size_t bytes_max); + +/* Report that a packet was dropped due to some error */ +void picoquic_log_dropped_packet(picoquic_cnx_t* cnx, struct st_picoquic_packet_header_t* ph, size_t packet_size, int err, uint8_t* raw_data, uint64_t current_time); + +/* Report that packet was buffered waiting for decryption */ +void picoquic_log_buffered_packet(picoquic_cnx_t* cnx, picoquic_packet_type_enum ptype, uint64_t current_time); + +/* Log that a packet was formatted, ready to be sent. */ +void picoquic_log_outgoing_packet(picoquic_cnx_t* cnx, + uint8_t* bytes, uint64_t sequence_number, size_t pn_length, size_t length, + uint8_t* send_buffer, size_t send_length, uint64_t current_time); + +/* Log packet lost events */ +void picoquic_log_packet_lost(picoquic_cnx_t* cnx, + picoquic_packet_type_enum ptype, uint64_t sequence_number, char const* trigger, + picoquic_connection_id_t* dcid, size_t packet_size, + uint64_t current_time); + +/* log negotiated ALPN */ +void picoquic_log_negotiated_alpn(picoquic_cnx_t* cnx, int is_local, + uint8_t const* sni, size_t sni_len, uint8_t const* alpn, size_t alpn_len, + const ptls_iovec_t* alpn_list, size_t alpn_count); + + +/* log transport extension -- either formatted by the loacl peer (is_local=1) or received from remote peer */ +void picoquic_log_transport_extension(picoquic_cnx_t* cnx, int is_local, + size_t param_length, uint8_t* params); + +/* log TLS ticket */ +void picoquic_log_tls_ticket(picoquic_cnx_t* cnx, uint8_t* ticket, uint16_t ticket_length); + +/* log the start of a connection */ +void picoquic_log_new_connection(picoquic_cnx_t* cnx); +/* log the end of a connection */ +void picoquic_log_close_connection(picoquic_cnx_t* cnx); + +/* log congestion control parameters */ +void picoquic_log_cc_dump(picoquic_cnx_t* cnx, uint64_t current_time); + + +#ifdef __cplusplus +} +#endif + + + +#endif /* PICOQUIC_UNIFIED_LOG_H */ diff --git a/deps/picoquic/picoquic_utils.h b/deps/picoquic/picoquic_utils.h new file mode 100644 index 0000000..e7135b0 --- /dev/null +++ b/deps/picoquic/picoquic_utils.h @@ -0,0 +1,310 @@ +/* +* Author: Igor Lubashev +* Copyright (c) 2017, Private Octopus, Inc. +* All rights reserved. +* +* Permission to use, copy, modify, and distribute this software for any +* purpose with or without fee is hereby granted, provided that the above +* copyright notice and this permission notice appear in all copies. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL Private Octopus, Inc. BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef PICOQUIC_UTILS_H +#define PICOQUIC_UTILS_H + +#include +#include +#include "picoquic.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef WIN32 +#define PRIst "Iu" +#ifndef PRIu64 +#define PRIu64 "I64u" +#endif +#ifndef PRIx64 +#define PRIx64 "I64x" +#endif +#else +#define PRIst "zu" +#endif + +#ifdef _WINDOWS +#ifndef _WINDOWS64 +#ifndef PICOQUIC_USE_CONSTANT_TIME_MEMCMP +#define PICOQUIC_USE_CONSTANT_TIME_MEMCMP +#endif +#endif +#endif + +#ifdef __APPLE__ +#ifndef PICOQUIC_USE_CONSTANT_TIME_MEMCMP +#define PICOQUIC_USE_CONSTANT_TIME_MEMCMP +#endif +#endif + +/* File identifiers in simple tracing functions */ +#define PICOQUIC_SENDER 1 +#define PICOQUIC_PACKET 2 +#define PICOQUIC_QUICCTX 3 +#define PICOQUIC_FRAME 4 +#define SET_LAST_WAKE(quic, file_id) ((quic)->wake_file = file_id, (quic)->wake_line = __LINE__) + + +void debug_set_stream(FILE *F); +void debug_printf(const char* fmt, ...); +void debug_printf_push_stream(FILE* f); +void debug_printf_pop_stream(void); +void debug_printf_suspend(void); +void debug_printf_resume(void); +int debug_printf_reset(int suspended); +void debug_dump(const void * x, int len); + +/* utilities */ +char* picoquic_string_create(const char* original, size_t len); +char* picoquic_string_duplicate(const char* original); +char* picoquic_string_free(char* str); +int picoquic_sprintf(char* buf, size_t buf_len, size_t * nb_chars, const char* fmt, ...); + +extern const picoquic_connection_id_t picoquic_null_connection_id; +uint8_t picoquic_format_connection_id(uint8_t* bytes, size_t bytes_max, picoquic_connection_id_t cnx_id); +uint8_t picoquic_parse_connection_id(const uint8_t* bytes, uint8_t len, picoquic_connection_id_t *cnx_id); +int picoquic_is_connection_id_null(const picoquic_connection_id_t * cnx_id); +int picoquic_compare_connection_id(const picoquic_connection_id_t * cnx_id1, const picoquic_connection_id_t * cnx_id2); +uint64_t picoquic_connection_id_hash(const picoquic_connection_id_t * cid); +uint64_t picoquic_val64_connection_id(picoquic_connection_id_t cnx_id); +void picoquic_set64_connection_id(picoquic_connection_id_t * cnx_id, uint64_t val64); +uint64_t picoquic_hash_addr(const struct sockaddr* addr); +uint8_t picoquic_parse_connection_id_hexa(char const * hex_input, size_t input_length, picoquic_connection_id_t * cnx_id); +int picoquic_print_connection_id_hexa(char* buf, size_t buf_len, const picoquic_connection_id_t* cnxid); +uint8_t picoquic_create_packet_header_cnxid_lengths(uint8_t dest_len, uint8_t srce_len); + +int picoquic_compare_addr(const struct sockaddr* expected, const struct sockaddr* actual); +int picoquic_addr_length(const struct sockaddr* addr); +void picoquic_store_addr(struct sockaddr_storage * stored_addr, const struct sockaddr * addr); +void picoquic_get_ip_addr(struct sockaddr * addr, uint8_t ** ip_addr, uint8_t * ip_addr_len); +int picoquic_store_text_addr(struct sockaddr_storage* stored_addr, const char* ip_address_text, uint16_t port); +char const* picoquic_addr_text(struct sockaddr* addr, char* text, size_t text_size); + +/* Setting the solution dir when not executing from default location */ +void picoquic_set_solution_dir(char const* solution_dir); +int picoquic_get_input_path(char * target_file_path, size_t file_path_max, const char * solution_path, const char * file_name); + +#ifndef MAX +#define MAX(a, b) ((a) > (b) ? (a) : (b)) +#endif + +#ifdef _WINDOWS +#define PICOQUIC_FILE_SEPARATOR "\\" +#ifdef _WINDOWS64 +#define PICOQUIC_DEFAULT_SOLUTION_DIR "..\\..\\" +#else +#define PICOQUIC_DEFAULT_SOLUTION_DIR "..\\" +#endif +#else +#define PICOQUIC_DEFAULT_SOLUTION_DIR "./" +#define PICOQUIC_FILE_SEPARATOR "/" +#endif + +#ifndef DISABLE_DEBUG_PRINTF + +#define DBG_PRINTF_FILENAME_MAX 24 +#define DBG_PRINTF(fmt, ...) \ + debug_printf("%s:%u [%s]: " fmt "\n", \ + &__FILE__[MAX(DBG_PRINTF_FILENAME_MAX, sizeof(__FILE__)) - DBG_PRINTF_FILENAME_MAX], \ + __LINE__, __FUNCTION__, __VA_ARGS__) + +#define DBG_FATAL_PRINTF(fmt, ...) \ + do { \ + DBG_PRINTF("(FATAL) " fmt "\n", __VA_ARGS__); \ + exit(1); \ + } while (0) + +#else + +#define DBG_PRINTF(fmt, ...) +#define DBG_FATAL_PRINTF(fmt, ...) + +#endif //#ifdef DISABLE_DEBUG_PRINTF + +/* Safely open files in a portable way */ +FILE * picoquic_file_open_ex(char const * file_name, char const * flags, int * last_err); +FILE * picoquic_file_open(char const * file_name, char const * flags); +FILE * picoquic_file_close(FILE * F); + +int picoquic_file_delete(char const* file_name, int* last_err); + +/* Skip and decoding functions */ +const uint8_t* picoquic_frames_fixed_skip(const uint8_t * bytes, const uint8_t * bytes_max, size_t size); +const uint8_t* picoquic_frames_varint_skip(const uint8_t * bytes, const uint8_t * bytes_max); +const uint8_t* picoquic_frames_varint_decode(const uint8_t * bytes, const uint8_t * bytes_max, uint64_t * n64); +const uint8_t* picoquic_frames_varlen_decode(const uint8_t * bytes, const uint8_t * bytes_max, size_t * n); +const uint8_t* picoquic_frames_uint8_decode(const uint8_t * bytes, const uint8_t * bytes_max, uint8_t * n); +const uint8_t* picoquic_frames_uint16_decode(const uint8_t * bytes, const uint8_t * bytes_max, uint16_t * n); +const uint8_t* picoquic_frames_uint32_decode(const uint8_t * bytes, const uint8_t * bytes_max, uint32_t * n); +const uint8_t* picoquic_frames_uint64_decode(const uint8_t * bytes, const uint8_t * bytes_max, uint64_t * n); +const uint8_t* picoquic_frames_length_data_skip(const uint8_t * bytes, const uint8_t * bytes_max); +const uint8_t* picoquic_frames_cid_decode(const uint8_t * bytes, const uint8_t * bytes_max, picoquic_connection_id_t * cid); + +#define VARINT_LEN(bytes) ((size_t)1 << (((bytes)[0] & 0xC0) >> 6)) + +/* Encoding functions of the form uint8_t * picoquic_frame_XXX_encode(uint8_t * bytes, uint8_t * bytes-max, ...) + */ +uint8_t* picoquic_frames_varint_encode(uint8_t* bytes, const uint8_t* bytes_max, uint64_t n64); +uint8_t* picoquic_frames_varlen_encode(uint8_t* bytes, const uint8_t* bytes_max, size_t n); +uint8_t* picoquic_frames_uint8_encode(uint8_t* bytes, const uint8_t* bytes_max, uint8_t n); +uint8_t* picoquic_frames_uint16_encode(uint8_t* bytes, const uint8_t* bytes_max, uint16_t n); +uint8_t* picoquic_frames_uint24_encode(uint8_t * bytes, const uint8_t * bytes_max, uint32_t n); +uint8_t* picoquic_frames_uint32_encode(uint8_t* bytes, const uint8_t* bytes_max, uint32_t n); +uint8_t* picoquic_frames_uint64_encode(uint8_t* bytes, const uint8_t* bytes_max, uint64_t n); +uint8_t* picoquic_frames_length_data_encode(uint8_t* bytes, const uint8_t* bytes_max, size_t l, const uint8_t* v); +uint8_t* picoquic_frames_cid_encode(uint8_t* bytes, const uint8_t* bytes_max, const picoquic_connection_id_t* cid); + + +/* Constant time memory comparison may be required on some platforms for testing reset secrets */ +int picoquic_constant_time_memcmp(const uint8_t* x, const uint8_t* y, size_t l); + +/* A set of portable function enables minimal support for + * thread, mutex and event in Windows and Linux + */ +#ifdef _WINDOWS +#define picoquic_thread_t HANDLE +#define picoquic_thread_return_t DWORD WINAPI +typedef DWORD (WINAPI* picoquic_thread_fn)(LPVOID lpParam); +#define picoquic_mutex_t HANDLE +#define picoquic_event_t HANDLE +#define picoquic_thread_do_return return 0 +#else + /* Linux routine returns */ +#define picoquic_thread_t pthread_t +#define picoquic_thread_return_t void* +typedef void* (*picoquic_thread_fn) (void* lpParam); +#define picoquic_mutex_t pthread_mutex_t +#define picoquic_thread_do_return return (void *)NULL + +typedef struct st_picoquic_event_t { + pthread_mutex_t mutex; + pthread_cond_t cond; +} picoquic_event_t; +#endif + +int picoquic_create_thread(picoquic_thread_t* thread, picoquic_thread_fn thread_fn, void* arg); +void picoquic_delete_thread(picoquic_thread_t* thread); + +int picoquic_create_mutex(picoquic_mutex_t* mutex); +int picoquic_delete_mutex(picoquic_mutex_t* mutex); +int picoquic_lock_mutex(picoquic_mutex_t* mutex); +int picoquic_unlock_mutex(picoquic_mutex_t* mutex); + +int picoquic_create_event(picoquic_event_t* event); +void picoquic_delete_event(picoquic_event_t* event); +int picoquic_signal_event(picoquic_event_t* event); +int picoquic_wait_for_event(picoquic_event_t* event, uint64_t microsec_wait); + +/* Set of random number generation functions, designed for tests. + * The random numbers are defined by a 64 bit context, initialized to a seed. + * The same seed will always generate the same sequence. + */ + +uint64_t picoquic_test_random(uint64_t* random_context); +void picoquic_test_random_bytes(uint64_t* random_context, uint8_t* bytes, size_t bytes_max); +uint64_t picoquic_test_uniform_random(uint64_t* random_context, uint64_t rnd_max); +double picoquic_test_gauss_random(uint64_t* random_context); /* random gaussian of variance 1.0, average 0 */ + +/* Really basic network simulator, only simulates a simple link using a + * packet structure. + * Init: link creation. Returns a link structure with defined bandwidth, + * latency, loss pattern and initial time. The link is empty. The loss + * pattern is a 64 bit bit mask. + * Submit packet of length L at time t. The packet is queued to the link. + * Get packet out of link at time T + L + Queue. + */ + +typedef struct st_picoquictest_sim_packet_t { + struct st_picoquictest_sim_packet_t* next_packet; + uint64_t arrival_time; + size_t length; + struct sockaddr_storage addr_from; + struct sockaddr_storage addr_to; + uint8_t bytes[PICOQUIC_MAX_PACKET_SIZE]; +} picoquictest_sim_packet_t; + +typedef struct st_picoquictest_sim_link_t { + uint64_t next_send_time; + uint64_t queue_time; + uint64_t queue_delay_max; + uint64_t picosec_per_byte; + uint64_t microsec_latency; + uint64_t* loss_mask; + uint64_t packets_dropped; + uint64_t packets_sent; + uint64_t jitter; + uint64_t jitter_seed; + size_t path_mtu; + picoquictest_sim_packet_t* first_packet; + picoquictest_sim_packet_t* last_packet; + /* Variables for random early drop simulation */ + uint64_t red_drop_mask; + uint64_t red_queue_max; + /* Variables for rate limiter simulation */ + double bucket_increase_per_microsec; + uint64_t bucket_max; + double bucket_current; + uint64_t bucket_arrival_last; +} picoquictest_sim_link_t; + +picoquictest_sim_link_t* picoquictest_sim_link_create(double data_rate_in_gps, + uint64_t microsec_latency, uint64_t* loss_mask, uint64_t queue_delay_max, uint64_t current_time); + +void picoquictest_sim_link_delete(picoquictest_sim_link_t* link); + +picoquictest_sim_packet_t* picoquictest_sim_link_create_packet(); + +uint64_t picoquictest_sim_link_next_arrival(picoquictest_sim_link_t* link, uint64_t current_time); + +picoquictest_sim_packet_t* picoquictest_sim_link_dequeue(picoquictest_sim_link_t* link, + uint64_t current_time); + +void picoquictest_sim_link_submit(picoquictest_sim_link_t* link, picoquictest_sim_packet_t* packet, + uint64_t current_time); + +/* SNI, Stores and Certificates used for test + */ + +#define PICOQUIC_TEST_SNI "test.example.com" + +#ifdef _WINDOWS +#define PICOQUIC_TEST_FILE_SERVER_CERT "certs\\cert.pem" +#define PICOQUIC_TEST_FILE_SERVER_BAD_CERT "certs\\badcert.pem" +#define PICOQUIC_TEST_FILE_SERVER_KEY "certs\\key.pem" +#define PICOQUIC_TEST_FILE_CERT_STORE "certs\\test-ca.crt" +#define PICOQUIC_TEST_FILE_ESNI_KEY "certs\\esni-secp256r1.key" +#define PICOQUIC_TEST_FILE_ESNI_RR "certs\\esni-rr.bin" +#else +#define PICOQUIC_TEST_FILE_SERVER_CERT "certs/cert.pem" +#define PICOQUIC_TEST_FILE_SERVER_BAD_CERT "certs/badcert.pem" +#define PICOQUIC_TEST_FILE_SERVER_KEY "certs/key.pem" +#define PICOQUIC_TEST_FILE_CERT_STORE "certs/test-ca.crt" +#define PICOQUIC_TEST_FILE_ESNI_KEY "certs/esni-secp256r1.key" +#define PICOQUIC_TEST_FILE_ESNI_RR "certs/esni-rr.bin" +#endif + + /* To set the solution directory for tests */ +extern char const* picoquic_solution_dir; +#ifdef __cplusplus +} +#endif +#endif diff --git a/deps/picoquic/picosocks.c b/deps/picoquic/picosocks.c new file mode 100644 index 0000000..bcecbf1 --- /dev/null +++ b/deps/picoquic/picosocks.c @@ -0,0 +1,1281 @@ +/* +* Author: Christian Huitema +* Copyright (c) 2017, Private Octopus, Inc. +* All rights reserved. +* +* Permission to use, copy, modify, and distribute this software for any +* purpose with or without fee is hereby granted, provided that the above +* copyright notice and this permission notice appear in all copies. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL Private Octopus, Inc. BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "picosocks.h" +#include "picoquic_utils.h" + +int picoquic_bind_to_port(SOCKET_TYPE fd, int af, int port) +{ + struct sockaddr_storage sa; + int addr_length = 0; + + memset(&sa, 0, sizeof(sa)); + + if (af == AF_INET) { + struct sockaddr_in* s4 = (struct sockaddr_in*)&sa; +#ifdef _WINDOWS + s4->sin_family = (ADDRESS_FAMILY)af; +#else + s4->sin_family = af; +#endif + s4->sin_port = htons((unsigned short)port); + addr_length = sizeof(struct sockaddr_in); + } else { + struct sockaddr_in6* s6 = (struct sockaddr_in6*)&sa; + + s6->sin6_family = AF_INET6; + s6->sin6_port = htons((unsigned short)port); + addr_length = sizeof(struct sockaddr_in6); + } + + return bind(fd, (struct sockaddr*)&sa, addr_length); +} + +int picoquic_get_local_address(SOCKET_TYPE sd, struct sockaddr_storage * addr) +{ + socklen_t name_len = sizeof(struct sockaddr_storage); + return getsockname(sd, (struct sockaddr *)addr, &name_len); +} + +int picoquic_socket_set_pkt_info(SOCKET_TYPE sd, int af) +{ + int ret; +#ifdef _WINDOWS + int option_value = 1; + if (af == AF_INET6) { + ret = setsockopt(sd, IPPROTO_IPV6, IPV6_PKTINFO, (char*)&option_value, sizeof(int)); + } + else { + ret = setsockopt(sd, IPPROTO_IP, IP_PKTINFO, (char*)&option_value, sizeof(int)); + } +#else + if (af == AF_INET6) { + int val = 1; + ret = setsockopt(sd, IPPROTO_IPV6, IPV6_V6ONLY, + &val, sizeof(val)); + if (ret == 0) { + val = 1; + ret = setsockopt(sd, IPPROTO_IPV6, IPV6_RECVPKTINFO, (char*)&val, sizeof(int)); + } + } + else { + int val = 1; +#ifdef IP_PKTINFO + ret = setsockopt(sd, IPPROTO_IP, IP_PKTINFO, (char*)&val, sizeof(int)); +#else + /* The IP_PKTINFO structure is not defined on BSD */ + ret = setsockopt(sd, IPPROTO_IP, IP_RECVDSTADDR, (char*)&val, sizeof(int)); +#endif + } +#endif + + return ret; +} + +int picoquic_socket_set_ecn_options(SOCKET_TYPE sd, int af, int * recv_set, int * send_set) +{ + int ret = -1; +#ifdef _WINDOWS + + if (af == AF_INET6) { +#ifdef IPV6_ECN + { + DWORD ecn = PICOQUIC_ECN_ECT_0; + DWORD recvEcn = 1; + /* Request receiving ECN reports in recvmsg */ + ret = setsockopt(sd, IPPROTO_IPV6, IPV6_ECN, (char *)&recvEcn, sizeof(recvEcn)); + if (ret < 0) { + DBG_PRINTF("setsockopt IPV6_ECN (0x%x) fails, errno: %d\n", recvEcn, GetLastError()); + ret = -1; + *recv_set = 0; + } + else { + *recv_set = 1; + ret = 0; + } + + /* Request setting ECN_ECT_0 in outgoing packets */ + if (setsockopt(sd, IPPROTO_IP, IPV6_ECN, (const char *)&ecn, sizeof(ecn)) < 0) { + DBG_PRINTF("setsockopt IPv6_ECN (0x%x) fails, errno: %d\n", ecn, errno); + *send_set = 0; + } + else { + *send_set = 1; + } + } +#else + * recv_set = 0; + * send_set = 0; +#endif + } + else { + /* Using IPv4 options. */ +#if defined(IP_ECN) + { + DWORD ecn = PICOQUIC_ECN_ECT_0; + INT recvEcn =1; + + /* Request receiving ECN reports in recvmsg */ + ret = setsockopt(sd, IPPROTO_IP, IP_ECN, (CHAR*)&recvEcn, sizeof(recvEcn)); + if (ret < 0) { + DBG_PRINTF("setsockopt IP_ECN (0x%x) fails, errno: %d\n", recvEcn, GetLastError()); + ret = -1; + *recv_set = 0; + } + else { + *recv_set = 1; + ret = 0; + } + + /* Request setting ECN_ECT_0 in outgoing packets */ + if (setsockopt(sd, IPPROTO_IP, IP_ECN, (const char*)&ecn, sizeof(ecn)) < 0) { + DBG_PRINTF("setsockopt IP_ECN (0x%x) fails, errno: %d\n", ecn, errno); + *send_set = 0; + } + else { + *send_set = 1; + } + } +#else + * recv_set = 0; +#endif + *send_set = 0; + } +#else + if (af == AF_INET6) { +#if defined(IPV6_TCLASS) + { + unsigned int ecn = PICOQUIC_ECN_ECT_0; /* Setting ECN_ECT_0 in outgoing packets */ + if (setsockopt(sd, IPPROTO_IPV6, IPV6_TCLASS, &ecn, sizeof(ecn)) < 0) { + DBG_PRINTF("setsockopt IPV6_TCLASS (0x%x) fails, errno: %d\n", ecn, errno); + *send_set = 0; + } + else { + *send_set = 1; + } + } +#else + DBG_PRINTF("%s", "IPV6_TCLASS is not defined\n"); + *send_set = 0; +#endif +#ifdef IPV6_RECVTCLASS + { + unsigned int set = 0x01; + + /* Request receiving TOS reports in recvmsg */ + if (setsockopt(sd, IPPROTO_IPV6, IPV6_RECVTCLASS, &set, sizeof(set)) < 0) { + DBG_PRINTF("setsockopt IPv6 IPV6_RECVTCLASS (0x%x) fails, errno: %d\n", set, errno); + ret = -1; + *recv_set = 0; + } + else { + *recv_set = 1; + ret = 0; + } + } +#else + DBG_PRINTF("%s", "IPV6_RECVTCLASS is not defined\n"); + *recv_set = 0; +#endif + + } + else { +#if defined(IP_TOS) + { + unsigned int ecn = PICOQUIC_ECN_ECT_0; + /* Request setting ECN_ECT_0 in outgoing packets */ + if (setsockopt(sd, IPPROTO_IP, IP_TOS, &ecn, sizeof(ecn)) < 0) { + DBG_PRINTF("setsockopt IPv4 IP_TOS (0x%x) fails, errno: %d\n", ecn, errno); + *send_set = 0; + } + else { + *send_set = 1; + } + } +#else + *send_set = 0; + DBG_PRINTF("%s", "IP_TOS is not defined\n"); +#endif + +#ifdef IP_RECVTOS + { + unsigned int set = 1; + + /* Request receiving TOS reports in recvmsg */ + if (setsockopt(sd, IPPROTO_IP, IP_RECVTOS, &set, sizeof(set)) < 0) { + DBG_PRINTF("setsockopt IPv4 IP_RECVTOS (0x%x) fails, errno: %d\n", set, errno); + ret = -1; + *recv_set = 0; + } + else { + *recv_set = 1; + ret = 0; + } + } +#else + *recv_set = 0; + DBG_PRINTF("%s", "IP_RECVTOS is not defined\n"); +#endif + } +#endif + + return ret; +} + +SOCKET_TYPE picoquic_open_client_socket(int af) +{ +#ifdef _WINDOWS + WSADATA wsaData = { 0 }; + (void)WSA_START(MAKEWORD(2, 2), &wsaData); + SOCKET_TYPE sd = WSASocket(af, SOCK_DGRAM, IPPROTO_UDP, NULL, 0, WSA_FLAG_OVERLAPPED); +#else + SOCKET_TYPE sd = socket(af, SOCK_DGRAM, IPPROTO_UDP); +#endif + + if (sd != INVALID_SOCKET) { + int send_set = 0; + int recv_set = 0; + + if (picoquic_socket_set_pkt_info(sd, af) != 0) { + DBG_PRINTF("Cannot set PKTINFO option (af=%d)\n", af); + } + if (picoquic_socket_set_ecn_options(sd, af, &recv_set, &send_set) != 0) { + DBG_PRINTF("Cannot set ECN options (af=%d)\n", af); + } + } + else { +#ifdef _WINDOWS + DBG_PRINTF("Cannot open socket(AF=%d), error: %d\n", af, GetLastError()); +#else + DBG_PRINTF("Cannot open socket(AF=%d), error: %d\n", af, errno); +#endif + } + + return sd; +} + +int picoquic_open_server_sockets(picoquic_server_sockets_t* sockets, int port) +{ + int ret = 0; + +#ifdef _WINDOWS + WSADATA wsaData = { 0 }; + if (WSA_START(MAKEWORD(2, 2), &wsaData)) { + ret = -1; + } +#endif + + const int sock_af[] = { AF_INET6, AF_INET }; + + for (int i = 0; i < PICOQUIC_NB_SERVER_SOCKETS; i++) { + if (ret == 0) { + sockets->s_socket[i] = socket(sock_af[i], SOCK_DGRAM, IPPROTO_UDP); + } else { + sockets->s_socket[i] = INVALID_SOCKET; + } + + if (sockets->s_socket[i] == INVALID_SOCKET) { + ret = -1; + } + else { + int recv_set = 0; + int send_set = 0; + if (picoquic_socket_set_ecn_options(sockets->s_socket[i], sock_af[i], &recv_set, &send_set) != 0) { + DBG_PRINTF("Cannot set ECN options (af=%d)\n", sock_af[i]); + } + ret = picoquic_socket_set_pkt_info(sockets->s_socket[i], sock_af[i]); + if (ret == 0) { + ret = picoquic_bind_to_port(sockets->s_socket[i], sock_af[i], port); + } + } + } + + return ret; +} + +void picoquic_close_server_sockets(picoquic_server_sockets_t* sockets) +{ + for (int i = 0; i < PICOQUIC_NB_SERVER_SOCKETS; i++) { + if (sockets->s_socket[i] != INVALID_SOCKET) { + SOCKET_CLOSE(sockets->s_socket[i]); + sockets->s_socket[i] = INVALID_SOCKET; + } + } +} + +void picoquic_socks_cmsg_parse( + void* vmsg, + struct sockaddr_storage* addr_dest, + int* dest_if, + unsigned char* received_ecn, + size_t * udp_coalesced_size) +{ + /* Assume that msg has been filled by a call to recvmsg */ +#if _WINDOWS + struct cmsghdr* cmsg; + WSAMSG* msg = (WSAMSG*)vmsg; + + /* Get the control information */ + for (cmsg = WSA_CMSG_FIRSTHDR(msg); cmsg != NULL; cmsg = WSA_CMSG_NXTHDR(msg, cmsg)) { + if (cmsg->cmsg_level == IPPROTO_IP) { + if (cmsg->cmsg_type == IP_PKTINFO) { + if (addr_dest != NULL) { + IN_PKTINFO* pPktInfo = (IN_PKTINFO*)WSA_CMSG_DATA(cmsg); + ((struct sockaddr_in*)addr_dest)->sin_family = AF_INET; + ((struct sockaddr_in*)addr_dest)->sin_port = 0; + ((struct sockaddr_in*)addr_dest)->sin_addr.s_addr = pPktInfo->ipi_addr.s_addr; + + if (dest_if != NULL) { + *dest_if = (int)pPktInfo->ipi_ifindex; + } + } + } + else if (cmsg->cmsg_type == IP_TOS +#ifdef IP_ECN + || cmsg->cmsg_type == IP_ECN +#endif + ) { + if (cmsg->cmsg_len > 0) { + if (received_ecn != NULL) { + *received_ecn = *((unsigned char*)WSA_CMSG_DATA(cmsg)); + } + } + } + else { + DBG_PRINTF("Cmsg level: %d, type: %d\n", cmsg->cmsg_level, cmsg->cmsg_type); + } + } + else if (cmsg->cmsg_level == IPPROTO_IPV6) { + if (cmsg->cmsg_type == IPV6_PKTINFO) { + if (addr_dest != NULL) { + IN6_PKTINFO* pPktInfo6 = (IN6_PKTINFO*)WSA_CMSG_DATA(cmsg); + ((struct sockaddr_in6*)addr_dest)->sin6_family = AF_INET6; + ((struct sockaddr_in6*)addr_dest)->sin6_port = 0; + memcpy(&((struct sockaddr_in6*)addr_dest)->sin6_addr, &pPktInfo6->ipi6_addr, sizeof(IN6_ADDR)); + + if (dest_if != NULL) { + *dest_if = (int)pPktInfo6->ipi6_ifindex; + } + } + } + else if (cmsg->cmsg_type == IPV6_TCLASS +#ifdef IPV6_ECN + || cmsg->cmsg_type == IPV6_ECN +#endif + ) { + if (cmsg->cmsg_len > 0 && received_ecn != NULL) { + *received_ecn = *((unsigned char*)WSA_CMSG_DATA(cmsg)); + } + } + else { + DBG_PRINTF("Cmsg level: %d, type: %d\n", cmsg->cmsg_level, cmsg->cmsg_type); + } + } +#ifdef UDP_COALESCED_INFO + else if (cmsg->cmsg_level == UDP_COALESCED_INFO) { + if (cmsg->cmsg_len > 0) { + if (udp_coalesced_size != NULL) { + *udp_coalesced_size = *((DWORD*)WSA_CMSG_DATA(cmsg)); + } + } + else { + DBG_PRINTF("Cmsg level: %d, type: %d\n", cmsg->cmsg_level, cmsg->cmsg_type); + } + } +#endif + else { + DBG_PRINTF("Cmsg level: %d, type: %d\n", cmsg->cmsg_level, cmsg->cmsg_type); + } + } +#else + /* Get the control information */ + struct msghdr* msg = (struct msghdr*)vmsg; + struct cmsghdr* cmsg; + + for (cmsg = CMSG_FIRSTHDR(msg); cmsg != NULL; cmsg = CMSG_NXTHDR(msg, cmsg)) { + if (cmsg->cmsg_level == IPPROTO_IP) { +#ifdef IP_PKTINFO + if (cmsg->cmsg_type == IP_PKTINFO) { + if (addr_dest != NULL) { + struct in_pktinfo* pPktInfo = (struct in_pktinfo*)CMSG_DATA(cmsg); + ((struct sockaddr_in*)addr_dest)->sin_family = AF_INET; + ((struct sockaddr_in*)addr_dest)->sin_port = 0; + ((struct sockaddr_in*)addr_dest)->sin_addr.s_addr = pPktInfo->ipi_addr.s_addr; + + if (dest_if != NULL) { + *dest_if = (int)pPktInfo->ipi_ifindex; + } + } + } +#else + /* The IP_PKTINFO structure is not defined on BSD */ + if (cmsg->cmsg_type == IP_RECVDSTADDR) { + if (addr_dest != NULL) { + struct in_addr* pPktInfo = (struct in_addr*)CMSG_DATA(cmsg); + ((struct sockaddr_in*)addr_dest)->sin_family = AF_INET; + ((struct sockaddr_in*)addr_dest)->sin_port = 0; + ((struct sockaddr_in*)addr_dest)->sin_addr.s_addr = pPktInfo->s_addr; + + if (dest_if != NULL) { + *dest_if = 0; + } + } + } +#endif + else if ((cmsg->cmsg_type == IP_TOS || cmsg->cmsg_type == IP_RECVTOS) && cmsg->cmsg_len > 0) { + if (received_ecn != NULL) { + *received_ecn = *((unsigned char*)CMSG_DATA(cmsg)); + } + } + } + else if (cmsg->cmsg_level == IPPROTO_IPV6) { + if (cmsg->cmsg_type == IPV6_PKTINFO) { + if (addr_dest != NULL) { + struct in6_pktinfo* pPktInfo6 = (struct in6_pktinfo*)CMSG_DATA(cmsg); + + ((struct sockaddr_in6*)addr_dest)->sin6_family = AF_INET6; + ((struct sockaddr_in6*)addr_dest)->sin6_port = 0; + memcpy(&((struct sockaddr_in6*)addr_dest)->sin6_addr, &pPktInfo6->ipi6_addr, sizeof(struct in6_addr)); + + if (dest_if != NULL) { + *dest_if = (int)pPktInfo6->ipi6_ifindex; + } + } + } + else if (cmsg->cmsg_type == IPV6_TCLASS) { + if (cmsg->cmsg_len > 0 && received_ecn != NULL) { + *received_ecn = *((unsigned char*)CMSG_DATA(cmsg)); + } + } + } + } +#endif +} + +#ifdef _WINDOWS +static void* cmsg_format_header_return_data_ptr(WSAMSG* msg, struct cmsghdr** last_cmsg, int * control_length, + INT cmsg_level, INT cmsg_type, size_t cmsg_data_len) +{ + void* cmsg_data_ptr = NULL; + WSACMSGHDR* cmsg = (*last_cmsg == NULL)? WSA_CMSG_FIRSTHDR(msg): WSA_CMSG_NXTHDR(msg, *last_cmsg); + + if (cmsg != NULL) { + size_t cmsg_required_space = WSA_CMSG_SPACE(cmsg_data_len); + *control_length += (INT)cmsg_required_space; + memset(cmsg, 0, cmsg_required_space); + cmsg->cmsg_level = cmsg_level; + cmsg->cmsg_type = cmsg_type; + cmsg->cmsg_len = WSA_CMSG_LEN(cmsg_data_len); + cmsg_data_ptr = (void*)WSA_CMSG_DATA(cmsg); + *last_cmsg = cmsg; + } + + return cmsg_data_ptr; +} +#endif + +void picoquic_socks_cmsg_format( + void* vmsg, + size_t message_length, + size_t send_msg_size, + struct sockaddr* addr_from, + int dest_if) +{ +#ifdef _WINDOWS + WSAMSG* msg = (WSAMSG*)vmsg; + int control_length = 0; + struct cmsghdr* last_cmsg = NULL; + int is_null = 0; + + /* Format the control message */ + if (addr_from != NULL && addr_from->sa_family != 0) { + if (addr_from->sa_family == AF_INET) { + struct in_pktinfo* pktinfo = (struct in_pktinfo*)cmsg_format_header_return_data_ptr(msg, &last_cmsg, + &control_length, IPPROTO_IP, IP_PKTINFO, sizeof(struct in_pktinfo)); + if (pktinfo != NULL) { + pktinfo->ipi_addr.s_addr = ((struct sockaddr_in*)addr_from)->sin_addr.s_addr; + pktinfo->ipi_ifindex = (unsigned long)dest_if; + } + else { + is_null = 1; + } + + if (!is_null && message_length > PICOQUIC_INITIAL_MTU_IPV4) { + int* pval = (int*)cmsg_format_header_return_data_ptr(msg, &last_cmsg, + &control_length, IPPROTO_IP, IP_DONTFRAGMENT, sizeof(int)); + if (pval != NULL) { + *pval = 1; + } + else { + is_null = 1; + } + } + } + else { + struct in6_pktinfo* pktinfo6 = (struct in6_pktinfo*)cmsg_format_header_return_data_ptr(msg, &last_cmsg, + &control_length, IPPROTO_IPV6, IPV6_PKTINFO, sizeof(struct in6_pktinfo)); + if (pktinfo6 != NULL) { + memcpy(&pktinfo6->ipi6_addr.u, &((struct sockaddr_in6*)addr_from)->sin6_addr.u, sizeof(IN6_ADDR)); + pktinfo6->ipi6_ifindex = (unsigned long)dest_if; + } + else { + is_null = 1; + } + if (!is_null) { + int* pval = (int*)cmsg_format_header_return_data_ptr(msg, &last_cmsg, + &control_length, IPPROTO_IPV6, IPV6_DONTFRAG, sizeof(int)); + if (pval != NULL) { + *pval = 1; + } + else { + is_null = 1; + } + } + } + } + if (!is_null && send_msg_size > 0) { + DWORD* pdw = (DWORD*)cmsg_format_header_return_data_ptr(msg, &last_cmsg, + &control_length, IPPROTO_UDP, UDP_SEND_MSG_SIZE, sizeof(DWORD)); + if (pdw != NULL) { + *pdw = (DWORD)send_msg_size; + } + } + + msg->Control.len = control_length; + if (control_length == 0) { + msg->Control.buf = NULL; + } + +#else + struct msghdr* msg = (struct msghdr*)vmsg; + int control_length = 0; + struct cmsghdr* cmsg; + /* Format the control message */ + cmsg = CMSG_FIRSTHDR(msg); + + if (addr_from != NULL && addr_from->sa_family != 0) { + if (addr_from->sa_family == AF_INET) { +#ifdef IP_PKTINFO + memset(cmsg, 0, CMSG_SPACE(sizeof(struct in_pktinfo))); + cmsg->cmsg_level = IPPROTO_IP; + cmsg->cmsg_type = IP_PKTINFO; + cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo)); + struct in_pktinfo* pktinfo = (struct in_pktinfo*)CMSG_DATA(cmsg); + pktinfo->ipi_addr.s_addr = ((struct sockaddr_in*)addr_from)->sin_addr.s_addr; + pktinfo->ipi_ifindex = (unsigned int)dest_if; + control_length += CMSG_SPACE(sizeof(struct in_pktinfo)); +#else + /* The IP_PKTINFO structure is not defined on BSD */ + memset(cmsg, 0, CMSG_SPACE(sizeof(struct in_addr))); + cmsg->cmsg_level = IPPROTO_IP; + cmsg->cmsg_type = IP_SENDSRCADDR; + cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_addr)); + struct in_addr* pktinfo = (struct in_addr*)CMSG_DATA(cmsg); + pktinfo->s_addr = ((struct sockaddr_in*)addr_from)->sin_addr.s_addr; + control_length += CMSG_SPACE(sizeof(struct in_addr)); +#endif + } + else if (addr_from->sa_family == AF_INET6) { + memset(cmsg, 0, CMSG_SPACE(sizeof(struct in6_pktinfo))); + cmsg->cmsg_level = IPPROTO_IPV6; + cmsg->cmsg_type = IPV6_PKTINFO; + cmsg->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo)); + struct in6_pktinfo* pktinfo6 = (struct in6_pktinfo*)CMSG_DATA(cmsg); + memcpy(&pktinfo6->ipi6_addr, &((struct sockaddr_in6*)addr_from)->sin6_addr, sizeof(struct in6_addr)); + pktinfo6->ipi6_ifindex = dest_if; + + control_length += CMSG_SPACE(sizeof(struct in6_pktinfo)); + } + else { + DBG_PRINTF("Unexpected address family: %d\n", addr_from->sa_family); + } +#ifdef IPV6_DONTFRAG + if (addr_from->sa_family == AF_INET6) { +#ifdef CMSG_ALIGN + struct cmsghdr* cmsg_2 = (struct cmsghdr*)((unsigned char*)cmsg + CMSG_ALIGN(cmsg->cmsg_len)); + { +#else + struct cmsghdr* cmsg_2 = CMSG_NXTHDR((msg), cmsg); + if (cmsg_2 == NULL) { + DBG_PRINTF("Cannot obtain second CMSG (control_length: %d)\n", control_length); + } + else { +#endif + int val = 1; + cmsg_2->cmsg_level = IPPROTO_IPV6; + cmsg_2->cmsg_type = IPV6_DONTFRAG; + cmsg_2->cmsg_len = CMSG_LEN(sizeof(int)); + memcpy(CMSG_DATA(cmsg_2), &val, sizeof(int)); + control_length += CMSG_SPACE(sizeof(int)); + } + } +#endif +#if defined(IP_DONTFRAG) + if (addr_from->sa_family == AF_INET6 && length > PICOQUIC_INITIAL_MTU_IPV6) { +#ifdef CMSG_ALIGN + struct cmsghdr* cmsg_2 = (struct cmsghdr*)((unsigned char*)cmsg + CMSG_ALIGN(cmsg->cmsg_len)); +#else + struct cmsghdr* cmsg_2 = CMSG_NXTHDR((msg), cmsg); +#endif + if (cmsg_2 == NULL) { + DBG_PRINTF("Cannot obtain second CMSG (control_length: %d)\n", control_length); + } + else + { + /* On BSD systems, just use IP_DONTFRAG */ + int val = 1; + cmsg_2->cmsg_level = IPPROTO_IP; + cmsg_2->cmsg_type = IP_DONTFRAG; + cmsg_2->cmsg_len = CMSG_LEN(sizeof(int)); + memcpy(CMSG_DATA(cmsg_2), &val, sizeof(int)); + control_length += CMSG_SPACE(sizeof(int)); + } + } +#endif + } + + msg->msg_controllen = control_length; + if (control_length == 0) { + msg->msg_control = NULL; + } +#endif +} + + +#ifdef _WINDOWS + +void picoquic_delete_async_socket(picoquic_recvmsg_async_ctx_t * ctx) +{ + if (ctx->fd != INVALID_SOCKET) { + SOCKET_CLOSE(ctx->fd); + ctx->fd = INVALID_SOCKET; + } + + if (ctx->overlap.hEvent != WSA_INVALID_EVENT) { + WSACloseEvent(ctx->overlap.hEvent); + ctx->overlap.hEvent = WSA_INVALID_EVENT; + } + + free(ctx); +} + +picoquic_recvmsg_async_ctx_t * picoquic_create_async_socket(int af, int recv_coalesced, int send_coalesced) +{ + int ret = 0; + int last_error = 0; + picoquic_recvmsg_async_ctx_t * ctx = (picoquic_recvmsg_async_ctx_t *)malloc(sizeof(picoquic_recvmsg_async_ctx_t)); + + if (ctx == NULL) { + DBG_PRINTF("Could not create async socket context, AF = %d!\n", af); + } + else { + memset(ctx, 0, sizeof(picoquic_recvmsg_async_ctx_t)); + ctx->overlap.hEvent = WSA_INVALID_EVENT; + + ctx->fd = picoquic_open_client_socket(af); + + if (ctx->fd == INVALID_SOCKET) { + last_error = WSAGetLastError(); + DBG_PRINTF("Could not initialize UDP socket, AF = %d, err=%d!\n", + af, last_error); + ret = -1; + } + else { + GUID WSARecvMsg_GUID = WSAID_WSARECVMSG; + DWORD NumberOfBytes; + int nResult = WSAIoctl(ctx->fd, SIO_GET_EXTENSION_FUNCTION_POINTER, + &WSARecvMsg_GUID, sizeof(WSARecvMsg_GUID), + &ctx->WSARecvMsg, sizeof(ctx->WSARecvMsg), + &NumberOfBytes, NULL, NULL); + + if (nResult == SOCKET_ERROR) { + last_error = WSAGetLastError(); + DBG_PRINTF("Could not initialize WSARecvMsg on UDP socket %d= %d!\n", + (int)ctx->fd, last_error); + ret = -1; + } + else { + GUID WSASendMsg_GUID = WSAID_WSASENDMSG; + nResult = WSAIoctl(ctx->fd, SIO_GET_EXTENSION_FUNCTION_POINTER, + &WSASendMsg_GUID, sizeof(WSASendMsg_GUID), + &ctx->WSASendMsg, sizeof(ctx->WSASendMsg), + &NumberOfBytes, NULL, NULL); + + if (nResult == SOCKET_ERROR) { + last_error = WSAGetLastError(); + DBG_PRINTF("Could not initialize WSASendMsg on UDP socket %d= %d!\n", + (int)ctx->fd, last_error); + ret = -1; + } + else { + // ctx->overlap.hEvent = WSACreateEvent(); + ctx->overlap.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + if (ctx->overlap.hEvent == WSA_INVALID_EVENT) { + last_error = WSAGetLastError(); + DBG_PRINTF("Could not create WSA event for UDP socket %d= %d!\n", + (int)ctx->fd, last_error); + ret = -1; + } + } +#ifdef UDP_RECV_MAX_COALESCED_SIZE + if (ret == 0 && recv_coalesced) { + int n_messages = (recv_coalesced) ? 1 : 10; + DWORD coalesced_size = n_messages * PICOQUIC_MAX_PACKET_SIZE; + ctx->recv_buffer_size = coalesced_size; + ctx->recv_buffer = (uint8_t*)malloc(ctx->recv_buffer_size); + ctx->supports_udp_recv_coalesced = recv_coalesced; + ctx->supports_udp_send_coalesced = send_coalesced; + if (ctx->recv_buffer == NULL) { + DBG_PRINTF("Could allocate buffer size %zu for socket %d!\n", + ctx->recv_buffer_size, (int)ctx->fd); + ret = -1; + } + else if ((ret = setsockopt(ctx->fd, IPPROTO_UDP, UDP_RECV_MAX_COALESCED_SIZE, (char*)&coalesced_size, + (int)sizeof(coalesced_size))) != 0) { + last_error = GetLastError(); + DBG_PRINTF("Cannot set UDP_RECV_MAX_COALESCED_SIZE %d, returns %d (%d)", + coalesced_size, ret, last_error); + ret = -1; + } + } +#endif + } + } + + if (ret != 0) { + picoquic_delete_async_socket(ctx); + ctx = NULL; + } + } + + return ctx; +} + +int picoquic_recvmsg_async_finish( + picoquic_recvmsg_async_ctx_t * ctx) +{ + DWORD cbTransferred = 0; + DWORD ret = 0; + DWORD flags = 0; + + if (ctx == NULL) { + return -1; + } + + if (!WSAGetOverlappedResult(ctx->fd, &ctx->overlap, &cbTransferred, FALSE, &flags)) { + ret = WSAGetLastError(); + if (ret == WSAECONNRESET) { + ctx->bytes_recv = 0; + ret = 0; + } + else { + DBG_PRINTF("Could not complete async call (WSARecvMsg) on UDP socket %d = %d!\n", + (int)ctx->fd, ret); + ctx->bytes_recv = -1; + } + } + else { + ctx->bytes_recv = cbTransferred; + ctx->from_length = ctx->msg.namelen; + + picoquic_socks_cmsg_parse(&ctx->msg, &ctx->addr_dest, &ctx->dest_if, &ctx->received_ecn, &ctx->udp_coalesced_size); + } + + return ret; +} + +int picoquic_recvmsg_async_start(picoquic_recvmsg_async_ctx_t* ctx) +{ + int last_error; + int ret = 0; + DWORD numberOfBytesReceived = 0; + int should_retry; + + do { + should_retry = 0; + ctx->from_length = 0; + ctx->dest_length = 0; + ctx->dest_if = 0; + ctx->received_ecn = 0; + ctx->bytes_recv = 0; + ctx->udp_coalesced_size = 0; + + ctx->overlap.Internal = 0; + ctx->overlap.InternalHigh = 0; + ctx->overlap.Offset = 0; + ctx->overlap.OffsetHigh = 0; + + ctx->dataBuf.buf = (char*)ctx->recv_buffer; + ctx->dataBuf.len = (ULONG)ctx->recv_buffer_size; + + ctx->msg.name = (struct sockaddr*) & ctx->addr_from; + ctx->msg.namelen = sizeof(ctx->addr_from); + ctx->msg.lpBuffers = &ctx->dataBuf; + ctx->msg.dwBufferCount = 1; + ctx->msg.dwFlags = 0; + ctx->msg.Control.buf = ctx->cmsg_buffer; + ctx->msg.Control.len = sizeof(ctx->cmsg_buffer); + + /* Setting the &nbReceived parameter to NULL to force async behavior */ + ret = ctx->WSARecvMsg(ctx->fd, &ctx->msg, &numberOfBytesReceived, &ctx->overlap, NULL); + + if (ret != 0) { + last_error = WSAGetLastError(); + if (last_error == WSA_IO_PENDING) { + ret = 0; + } + else if (last_error == WSAECONNRESET) { + /* Ignore the ICMP errors */ + should_retry = 1; + ret = 0; + } + else { + DBG_PRINTF("Could not start receive async (WSARecvMsg) on UDP socket %d = %d!\n", + (int)ctx->fd, last_error); + ctx->bytes_recv = -1; + } + } + else { + DBG_PRINTF("Receive async immediate (WSARecvMsg) on UDP socket %d -- %d bytes !\n", + (int)ctx->fd, numberOfBytesReceived); + ctx->nb_immediate_receive++; + } + } while (should_retry); + + return ret; +} + +#endif + +int picoquic_recvmsg(SOCKET_TYPE fd, + struct sockaddr_storage* addr_from, + struct sockaddr_storage* addr_dest, + int* dest_if, + unsigned char* received_ecn, + uint8_t* buffer, int buffer_max) +#ifdef _WINDOWS +{ + GUID WSARecvMsg_GUID = WSAID_WSARECVMSG; + LPFN_WSARECVMSG WSARecvMsg; + char cmsg_buffer[1024]; + DWORD NumberOfBytes; + int nResult; + WSAMSG msg; + WSABUF dataBuf; + int recv_ret = 0; + int bytes_recv; + int last_error; + + if (dest_if != NULL) { + *dest_if = 0; + } + + if (received_ecn != NULL) { + *received_ecn = 0; + } + + nResult = WSAIoctl(fd, SIO_GET_EXTENSION_FUNCTION_POINTER, + &WSARecvMsg_GUID, sizeof WSARecvMsg_GUID, + &WSARecvMsg, sizeof WSARecvMsg, + &NumberOfBytes, NULL, NULL); + + if (nResult == SOCKET_ERROR) { + last_error = WSAGetLastError(); + DBG_PRINTF("Could not initialize WSARecvMsg) on UDP socket %d= %d!\n", + (int)fd, last_error); + bytes_recv = -1; + } else { + dataBuf.buf = (char*)buffer; + dataBuf.len = buffer_max; + + msg.name = (struct sockaddr*)addr_from; + msg.namelen = sizeof(struct sockaddr_storage); + msg.lpBuffers = &dataBuf; + msg.dwBufferCount = 1; + msg.dwFlags = 0; + msg.Control.buf = cmsg_buffer; + msg.Control.len = sizeof(cmsg_buffer); + + recv_ret = WSARecvMsg(fd, &msg, &NumberOfBytes, NULL, NULL); + + if (recv_ret != 0) { + last_error = WSAGetLastError(); + DBG_PRINTF("Could not receive message (WSARecvMsg) on UDP socket %d = %d!\n", + (int)fd, last_error); + bytes_recv = -1; + } else { + bytes_recv = NumberOfBytes; + picoquic_socks_cmsg_parse(&msg, addr_dest, dest_if, received_ecn, NULL); + } + } + + return bytes_recv; +} +#else +{ + int bytes_recv = 0; + struct msghdr msg; + struct iovec dataBuf; + char cmsg_buffer[1024]; + + if (dest_if != NULL) { + *dest_if = 0; + } + + dataBuf.iov_base = (char*)buffer; + dataBuf.iov_len = buffer_max; + + msg.msg_name = (struct sockaddr*)addr_from; + msg.msg_namelen = sizeof(struct sockaddr_storage); + msg.msg_iov = &dataBuf; + msg.msg_iovlen = 1; + msg.msg_flags = 0; + msg.msg_control = (void*)cmsg_buffer; + msg.msg_controllen = sizeof(cmsg_buffer); + + bytes_recv = recvmsg(fd, &msg, 0); + + if (bytes_recv <= 0) { + addr_from->ss_family = 0; + } else { + picoquic_socks_cmsg_parse(&msg, addr_dest, dest_if, received_ecn, NULL); + } + + return bytes_recv; +} +#endif + +int picoquic_sendmsg(SOCKET_TYPE fd, + struct sockaddr* addr_dest, + struct sockaddr* addr_from, + int dest_if, + const char* bytes, int length) +#ifdef _WINDOWS +{ + GUID WSASendMsg_GUID = WSAID_WSASENDMSG; + LPFN_WSASENDMSG WSASendMsg; + char cmsg_buffer[1024]; + DWORD NumberOfBytes; + int ret = 0; + DWORD dwBytesSent = 0; + WSAMSG msg; + WSABUF dataBuf; + int bytes_sent; + int last_error; + + ret = WSAIoctl(fd, SIO_GET_EXTENSION_FUNCTION_POINTER, + &WSASendMsg_GUID, sizeof WSASendMsg_GUID, + &WSASendMsg, sizeof WSASendMsg, + &NumberOfBytes, NULL, NULL); + + if (ret == SOCKET_ERROR) { + last_error = WSAGetLastError(); + DBG_PRINTF("Could not initialize WSASendMsg on UDP socket %d= %d!\n", + (int)fd, last_error); + bytes_sent = -1; + } + else { + /* Format the control message header */ + memset(&msg, 0, sizeof(msg)); + msg.name = addr_dest; + msg.namelen = picoquic_addr_length(addr_dest); + dataBuf.buf = (char*)bytes; + dataBuf.len = length; + msg.lpBuffers = &dataBuf; + msg.dwBufferCount = 1; + msg.Control.buf = (char*)cmsg_buffer; + msg.Control.len = sizeof(cmsg_buffer); + + /* Format the control message */ + picoquic_socks_cmsg_format(&msg, length, 0, addr_from, dest_if); + + /* Send the message */ + ret = WSASendMsg(fd, &msg, 0, &dwBytesSent, NULL, NULL); + + if (ret != 0) { + bytes_sent = -1; + } else { + bytes_sent = (int)dwBytesSent; + } + } + + return bytes_sent; +} +#else +{ + struct msghdr msg; + struct iovec dataBuf; + char cmsg_buffer[1024]; + int bytes_sent; + + /* Format the message header */ + + dataBuf.iov_base = (char*)bytes; + dataBuf.iov_len = length; + + memset(&msg, 0, sizeof(msg)); + msg.msg_name = addr_dest; + msg.msg_namelen = picoquic_addr_length(addr_dest); + msg.msg_iov = &dataBuf; + msg.msg_iovlen = 1; + msg.msg_control = (void*)cmsg_buffer; + msg.msg_controllen = sizeof(cmsg_buffer); + + /* Format the control message */ + picoquic_socks_cmsg_format(&msg, length, 0, addr_from, dest_if); + + bytes_sent = sendmsg(fd, &msg, 0); + + return bytes_sent; +} +#endif + +int picoquic_select_ex(SOCKET_TYPE* sockets, + int nb_sockets, + struct sockaddr_storage* addr_from, + struct sockaddr_storage* addr_dest, + int* dest_if, + unsigned char * received_ecn, + uint8_t* buffer, int buffer_max, + int64_t delta_t, + int * socket_rank, + uint64_t* current_time) +{ + fd_set readfds; + struct timeval tv; + int ret_select = 0; + int bytes_recv = 0; + int sockmax = 0; + + if (received_ecn != NULL) { + *received_ecn = 0; + } + + FD_ZERO(&readfds); + + for (int i = 0; i < nb_sockets; i++) { + if (sockmax < (int)sockets[i]) { + sockmax = (int)sockets[i]; + } + FD_SET(sockets[i], &readfds); + } + + if (delta_t <= 0) { + tv.tv_sec = 0; + tv.tv_usec = 0; + } else { + if (delta_t > 10000000) { + tv.tv_sec = (long)10; + tv.tv_usec = 0; + } else { + tv.tv_sec = (long)(delta_t / 1000000); + tv.tv_usec = (long)(delta_t % 1000000); + } + } + + ret_select = select(sockmax + 1, &readfds, NULL, NULL, &tv); + + if (ret_select < 0) { + bytes_recv = -1; + DBG_PRINTF("Error: select returns %d\n", ret_select); + } else if (ret_select > 0) { + for (int i = 0; i < nb_sockets; i++) { + if (FD_ISSET(sockets[i], &readfds)) { + *socket_rank = i; + bytes_recv = picoquic_recvmsg(sockets[i], addr_from, + addr_dest, dest_if, received_ecn, + buffer, buffer_max); + + if (bytes_recv <= 0) { +#ifdef _WINDOWS + int last_error = WSAGetLastError(); + + if (last_error == WSAECONNRESET || last_error == WSAEMSGSIZE) { + bytes_recv = 0; + continue; + } +#endif + DBG_PRINTF("Could not receive packet on UDP socket[%d]= %d!\n", + i, (int)sockets[i]); + + break; + } else { + break; + } + } + } + } + + *current_time = picoquic_current_time(); + + return bytes_recv; +} + +int picoquic_select(SOCKET_TYPE* sockets, + int nb_sockets, + struct sockaddr_storage* addr_from, + struct sockaddr_storage* addr_dest, + int* dest_if, + unsigned char* received_ecn, + uint8_t* buffer, int buffer_max, + int64_t delta_t, + uint64_t* current_time) { + int socket_rank; + return picoquic_select_ex(sockets, nb_sockets, addr_from, addr_dest, dest_if, + received_ecn, buffer, buffer_max, delta_t, &socket_rank, current_time); +} + +int picoquic_send_through_socket( + SOCKET_TYPE fd, + struct sockaddr* addr_dest, + struct sockaddr* addr_from, int from_if, + const char* bytes, int length, int* sock_err) +{ + int sent = picoquic_sendmsg(fd, addr_dest, addr_from, from_if, bytes, length); + +#ifndef DISABLE_DEBUG_PRINTF + if (sent <= 0) { +#ifdef _WINDOWS + int last_error = WSAGetLastError(); +#else + int last_error = errno; +#endif + DBG_PRINTF("Could not send packet on UDP socket[AF=%d]= %d!\n", + addr_dest->sa_family, last_error); + if (sock_err != NULL) { + *sock_err = last_error; + } + } +#endif + + return sent; +} + +int picoquic_send_through_server_sockets( + picoquic_server_sockets_t* sockets, + struct sockaddr* addr_dest, + struct sockaddr* addr_from, int from_if, + const char* bytes, int length, int* sock_err) +{ + /* Both Linux and Windows use separate sockets for V4 and V6 */ + int socket_index = (addr_dest->sa_family == AF_INET) ? 1 : 0; + + return picoquic_send_through_socket(sockets->s_socket[socket_index], addr_dest, addr_from, from_if, bytes, length, sock_err); +} + +int picoquic_get_server_address(const char* ip_address_text, int server_port, + struct sockaddr_storage* server_address, int* is_name) +{ + int ret = 0; + struct sockaddr_in* ipv4_dest = (struct sockaddr_in*)server_address; + struct sockaddr_in6* ipv6_dest = (struct sockaddr_in6*)server_address; + + /* get the IP address of the server */ + memset(server_address, 0, sizeof(struct sockaddr_storage)); + *is_name = 0; + + if (inet_pton(AF_INET, ip_address_text, &ipv4_dest->sin_addr) == 1) { + /* Valid IPv4 address */ + ipv4_dest->sin_family = AF_INET; + ipv4_dest->sin_port = htons((unsigned short)server_port); + } else if (inet_pton(AF_INET6, ip_address_text, &ipv6_dest->sin6_addr) == 1) { + /* Valid IPv6 address */ + ipv6_dest->sin6_family = AF_INET6; + ipv6_dest->sin6_port = htons((unsigned short)server_port); + } else { + /* Server is described by name. Do a lookup for the IP address, + * and then use the name as SNI parameter */ + struct addrinfo* result = NULL; + struct addrinfo hints; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_DGRAM; + hints.ai_protocol = IPPROTO_UDP; + + if ((ret = getaddrinfo(ip_address_text, NULL, &hints, &result)) != 0) { +#ifdef _WINDOWS + int err = GetLastError(); +#else + int err = ret; +#endif + fprintf(stderr, "Cannot get IP address for %s, err = %d (0x%x)\n", ip_address_text, err, err); + ret = -1; + } else { + *is_name = 1; + + switch (result->ai_family) { + case AF_INET: + ipv4_dest->sin_family = AF_INET; + ipv4_dest->sin_port = htons((unsigned short)server_port); +#ifdef _WINDOWS + ipv4_dest->sin_addr.S_un.S_addr = ((struct sockaddr_in*)result->ai_addr)->sin_addr.S_un.S_addr; +#else + ipv4_dest->sin_addr.s_addr = ((struct sockaddr_in*)result->ai_addr)->sin_addr.s_addr; +#endif + break; + case AF_INET6: + ipv6_dest->sin6_family = AF_INET6; + ipv6_dest->sin6_port = htons((unsigned short)server_port); + memcpy(&ipv6_dest->sin6_addr, + &((struct sockaddr_in6*)result->ai_addr)->sin6_addr, + sizeof(ipv6_dest->sin6_addr)); + break; + default: + fprintf(stderr, "Error getting IPv6 address for %s, family = %d\n", + ip_address_text, result->ai_family); + ret = -1; + break; + } + + freeaddrinfo(result); + } + } + + return ret; +} + +/* Wireshark needs the session keys in order to decrypt and analyze packets. + * In Unix and Windows, Wireshark reads these keys from a file. The name + * of the file is passed in the environment variable SSLKEYLOGFILE, + * which is accessed through system dependent API. + */ + +void picoquic_set_key_log_file_from_env(picoquic_quic_t* quic) +{ + char* keylog_filename = NULL; + +#ifdef _WINDOWS + size_t len; + + if (_dupenv_s(&keylog_filename, &len, "SSLKEYLOGFILE") != 0 || + keylog_filename == NULL) { + return; + } +#else + keylog_filename = getenv("SSLKEYLOGFILE"); + if (keylog_filename == NULL) { + return; + } +#endif + + picoquic_set_key_log_file(quic, keylog_filename); +} diff --git a/deps/picoquic/picosocks.h b/deps/picoquic/picosocks.h new file mode 100644 index 0000000..14d2041 --- /dev/null +++ b/deps/picoquic/picosocks.h @@ -0,0 +1,246 @@ +/* +* Author: Christian Huitema +* Copyright (c) 2017, Private Octopus, Inc. +* All rights reserved. +* +* Permission to use, copy, modify, and distribute this software for any +* purpose with or without fee is hereby granted, provided that the above +* copyright notice and this permission notice appear in all copies. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL Private Octopus, Inc. BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef PICOSOCKS_H +#define PICOSOCKS_H + +#ifdef _WINDOWS +/* clang-format off */ +#define WIN32_LEAN_AND_MEAN +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef SOCKET_TYPE +#define SOCKET_TYPE SOCKET +#endif +#ifndef SOCKET_CLOSE +#define SOCKET_CLOSE(x) closesocket(x) +#endif +#ifndef WSA_START_DATA +#define WSA_START_DATA WSADATA +#endif +#ifndef WSA_START +#define WSA_START(x, y) WSAStartup((x), (y)) +#endif +#ifndef WSA_LAST_ERROR +#define WSA_LAST_ERROR(x) WSAGetLastError() +#endif +#ifndef socklen_t +#define socklen_t int +#endif +/* clang-format on */ +#else /* Linux */ + +#include "getopt.h" +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef __USE_XOPEN2K +#define __USE_XOPEN2K +#endif +#ifndef __USE_POSIX +#define __USE_POSIX +#endif +#ifndef __USE_GNU +#define __USE_GNU +#endif +#ifndef __APPLE_USE_RFC_3542 +#define __APPLE_USE_RFC_3542 /* IPV6_PKTINFO */ +#endif + +#include +#include +#include +#include +#include + +#ifndef SOCKET_TYPE +#define SOCKET_TYPE int +#endif +#ifndef INVALID_SOCKET +#define INVALID_SOCKET -1 +#endif +#ifndef SOCKET_CLOSE +#define SOCKET_CLOSE(x) close(x) +#endif +#ifndef WSA_LAST_ERROR +#define WSA_LAST_ERROR(x) ((long)(x)) +#endif +#ifndef IPV6_RECVPKTINFO +#define IPV6_RECVPKTINFO IPV6_PKTINFO /* Cygwin */ +#endif +#endif + +#define PICOQUIC_ECN_ECT_0 0x02 +#define PICOQUIC_ECN_ECT_1 0x01 +#define PICOQUIC_ECN_CE 0x03 + +#include "picoquic.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef _WINDOWS +typedef struct st_picoquic_recvmsg_async_ctx_t { + WSAOVERLAPPED overlap; + LPFN_WSARECVMSG WSARecvMsg; + LPFN_WSASENDMSG WSASendMsg; + WSABUF dataBuf; + WSAMSG msg; + char cmsg_buffer[1024]; + size_t recv_buffer_size; + uint8_t* recv_buffer; + struct sockaddr_storage addr_from; + struct sockaddr_storage addr_dest; + socklen_t from_length; + socklen_t dest_length; + SOCKET_TYPE fd; + int dest_if; + unsigned char received_ecn; + size_t udp_coalesced_size; + int nb_immediate_receive; + int bytes_recv; + unsigned int is_started : 1; + unsigned int supports_udp_send_coalesced : 1; + unsigned int supports_udp_recv_coalesced : 1; + +} picoquic_recvmsg_async_ctx_t; + +// +// Not yet available in the SDK. When available this code can be removed. +// +#ifndef UDP_SEND_MSG_SIZE +#define UDP_SEND_MSG_SIZE 2 +#endif +#ifndef UDP_RECV_MAX_COALESCED_SIZE +#define UDP_RECV_MAX_COALESCED_SIZE 3 +#endif +#ifndef UDP_COALESCED_INFO +#define UDP_COALESCED_INFO 3 +#endif + +picoquic_recvmsg_async_ctx_t * picoquic_create_async_socket(int af, int recv_coalesced, int send_coalesced); +void picoquic_delete_async_socket(picoquic_recvmsg_async_ctx_t * ctx); +int picoquic_recvmsg_async_start(picoquic_recvmsg_async_ctx_t * ctx); +int picoquic_recvmsg_async_finish(picoquic_recvmsg_async_ctx_t * ctx); + +#endif + +#define PICOQUIC_NB_SERVER_SOCKETS 2 + +typedef struct st_picoquic_server_sockets_t { + SOCKET_TYPE s_socket[PICOQUIC_NB_SERVER_SOCKETS]; +} picoquic_server_sockets_t; + +int picoquic_bind_to_port(SOCKET_TYPE fd, int af, int port); + +int picoquic_get_local_address(SOCKET_TYPE sd, struct sockaddr_storage * addr); + +SOCKET_TYPE picoquic_open_client_socket(int af); + +int picoquic_open_server_sockets(picoquic_server_sockets_t* sockets, int port); + +void picoquic_close_server_sockets(picoquic_server_sockets_t* sockets); + +int picoquic_socket_set_pkt_info(SOCKET_TYPE sd, int af); +int picoquic_socket_set_ecn_options(SOCKET_TYPE sd, int af, int * recv_set, int * send_set); + +int picoquic_select(SOCKET_TYPE* sockets, int nb_sockets, + struct sockaddr_storage* addr_from, + struct sockaddr_storage* addr_dest, + int* dest_if, + unsigned char * received_ecn, + uint8_t* buffer, int buffer_max, + int64_t delta_t, + uint64_t* current_time); + +int picoquic_select_ex(SOCKET_TYPE* sockets, + int nb_sockets, + struct sockaddr_storage* addr_from, + struct sockaddr_storage* addr_dest, + int* dest_if, + unsigned char* received_ecn, + uint8_t* buffer, int buffer_max, + int64_t delta_t, + int* socket_rank, + uint64_t* current_time); + +int picoquic_send_through_socket( + SOCKET_TYPE fd, + struct sockaddr* addr_dest, + struct sockaddr* addr_from, int from_if, + const char* bytes, int length, int* sock_err); + +int picoquic_send_through_server_sockets( + picoquic_server_sockets_t* sockets, + struct sockaddr* addr_dest, + struct sockaddr* addr_from, int from_if, + const char* bytes, int length, int * sock_err); + +int picoquic_get_server_address(const char* ip_address_text, int server_port, + struct sockaddr_storage* server_address, + int* is_name); + +/* Wireshark needs the session keys in order to decrypt and analyze packets. + * In Unix and Windows, Wireshark reads these keys from a file. The name + * of the file is passed in the environment variable SSLKEYLOGFILE, + * which is accessed through system dependent API. + */ + +void picoquic_set_key_log_file_from_env(picoquic_quic_t* quic); + +/* Utility functions for implementing async sockets + */ + +void picoquic_socks_cmsg_parse( + void* vmsg, + struct sockaddr_storage* addr_dest, + int* dest_if, + unsigned char* received_ecn, + size_t* udp_coalesced_size); + +void picoquic_socks_cmsg_format( + void* vmsg, + size_t message_length, + size_t send_msg_size, + struct sockaddr* addr_from, + int dest_if); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/deps/picoquic/picosplay.c b/deps/picoquic/picosplay.c new file mode 100644 index 0000000..14d4094 --- /dev/null +++ b/deps/picoquic/picosplay.c @@ -0,0 +1,329 @@ +/* +* Author: Christian Huitema +* Copyright (c) 2018, Private Octopus, Inc. +* All rights reserved. +* +* Permission to use, copy, modify, and distribute this software for any +* purpose with or without fee is hereby granted, provided that the above +* copyright notice and this permission notice appear in all copies. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL Private Octopus, Inc. BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* +* This code is copied and adapted from https://github.com/lrem/splay, +* copyright (c) Remigiusz Modrzejewski 2014, filed on Github with MIT license. +*/ + +#include +#include +#include "picosplay.h" + +/* The single most important utility function. */ +static void rotate(picosplay_node_t *child); +/* And a few more. */ +static picosplay_node_t* leftmost(picosplay_node_t *node); +static picosplay_node_t* rightmost(picosplay_node_t *node); + + +/* The meat: splay the node x. */ +static void zig(picosplay_node_t *x); +static void zigzig(picosplay_node_t *x, picosplay_node_t *p); +static void zigzag(picosplay_node_t *x); +static void splay(picosplay_tree_t *tree, picosplay_node_t *x) { + while(1) { + picosplay_node_t *p = x->parent; + if(p == NULL) { + tree->root = x; + return; + } + picosplay_node_t *g = p->parent; + if(p->parent == NULL) + zig(x); + else + if((x == p->left && p == g->left) || + (x == p->right && p == g->right)) + zigzig(x, p); + else + zigzag(x); + } +} + +/* When p is root, rotate on the edge between x and p.*/ +static void zig(picosplay_node_t *x) { + rotate(x); +} + +/* When both x and p are left (or both right) children, + * rotate on edge between p and g, then on edge between x and p. + */ +static void zigzig(picosplay_node_t *x, picosplay_node_t *p) { + rotate(p); + rotate(x); +} + +/* When one of x and p is a left child and the other a right child, + * rotate on the edge between x and p, then on the new edge between x and g. + */ +static void zigzag(picosplay_node_t *x) { + rotate(x); + rotate(x); +} + +/* Initialize an empty tree, storing the picosplay_comparator. */ +void picosplay_init_tree(picosplay_tree_t* tree, picosplay_comparator comp, picosplay_create create, picosplay_delete_node delete_node, picosplay_node_value node_value) { + tree->comp = comp; + tree->create = create; + tree->delete_node = delete_node; + tree->node_value = node_value; + tree->root = NULL; + tree->size = 0; +} + +/* Return an empty tree, storing the picosplay_comparator. */ +picosplay_tree_t* picosplay_new_tree(picosplay_comparator comp, picosplay_create create, picosplay_delete_node delete_node, picosplay_node_value node_value) { + picosplay_tree_t *new = malloc(sizeof(picosplay_tree_t)); + if (new != NULL) { + picosplay_init_tree(new, comp, create, delete_node, node_value); + } + return new; +} + +/* picosplay_insert and return a new node with the given value, splaying the tree. + * The insertion is essentially a generic BST insertion. + */ +picosplay_node_t* picosplay_insert(picosplay_tree_t *tree, void *value) { + picosplay_node_t *new = tree->create(value); + + if (new != NULL) { + new->left = NULL; + new->right = NULL; + if (tree->root == NULL) { + tree->root = new; + new->parent = NULL; + } + else { + picosplay_node_t *curr = tree->root; + picosplay_node_t *parent = NULL; + int left = 0; + while (curr != NULL) { + parent = curr; + if (tree->comp(tree->node_value(new), tree->node_value(curr)) < 0) { + left = 1; + curr = curr->left; + } + else { + left = 0; + curr = curr->right; + } + } + new->parent = parent; + if (left) + parent->left = new; + else + parent->right = new; + } + splay(tree, new); + tree->size++; + } + + return new; +} + +/* Find a node with the given value, splaying the tree. */ +picosplay_node_t* picosplay_find(picosplay_tree_t *tree, void *value) +{ + picosplay_node_t *curr = tree->root; + int found = 0; + while(curr != NULL && !found) { + int64_t relation = tree->comp(value, tree->node_value(curr)); + if(relation == 0) { + found = 1; + } else if(relation < 0) { + curr = curr->left; + } else { + curr = curr->right; + } + } + + /* TODO: there may or may not be a need to perform a splay on a find operation. + * The Wikipedia example omits it, but this code keeps it. We should + * perform measurements with and without it and keep the best alternative. */ + if(curr != NULL) + splay(tree, curr); + return curr; +} + +/* Find a node with the given value, splaying the tree. */ +picosplay_node_t* picosplay_find_previous(picosplay_tree_t* tree, void* value) +{ + picosplay_node_t* curr = tree->root; + picosplay_node_t* previous = NULL; + int found = 0; + while (curr != NULL && !found) { + int64_t relation = tree->comp(value, tree->node_value(curr)); + if (relation == 0) { + found = 1; + previous = curr; + } + else if (relation < 0) { + curr = curr->left; + } + else { + previous = curr; + curr = curr->right; + } + } + + return previous; +} + +/* Remove a node with the given value, splaying the tree. */ +void picosplay_delete(picosplay_tree_t *tree, void *value) { + picosplay_node_t *node = picosplay_find(tree, value); + picosplay_delete_hint(tree, node); +} + +/* Remove the node given by the pointer, splaying the tree. */ +void picosplay_delete_hint(picosplay_tree_t *tree, picosplay_node_t *node) { + if(node == NULL) + return; + splay(tree, node); /* Now node is tree's root. */ + if(node->left == NULL) { + tree->root = node->right; + if(tree->root != NULL) + tree->root->parent = NULL; + } else if(node->right == NULL) { + tree->root = node->left; + tree->root->parent = NULL; + } else { + picosplay_node_t *x = leftmost(node->right); + if(x->parent != node) { + x->parent->left = x->right; + if(x->right != NULL) + x->right->parent = x->parent; + x->right = node->right; + x->right->parent = x; + } + tree->root = x; + x->parent = NULL; + x->left = node->left; + x->left->parent = x; + } + tree->delete_node(tree, node); + tree->size--; +} + +void picosplay_empty_tree(picosplay_tree_t * tree) +{ + if (tree != NULL) { + while (tree->root != NULL) { + picosplay_delete_hint(tree, tree->root); + } + } +} + +picosplay_node_t* picosplay_first(picosplay_tree_t *tree) { + return leftmost(tree->root); +} + +/* Return the minimal node that is bigger than the given. + * This is either: + * - leftmost child in the right subtree + * - closest ascendant for which given node is in left subtree + */ +picosplay_node_t* picosplay_next(picosplay_node_t *node) { + if(node->right != NULL) + return leftmost(node->right); + while(node->parent != NULL && node == node->parent->right) + node = node->parent; + return node->parent; +} + +picosplay_node_t* picosplay_last(picosplay_tree_t *tree) { + return rightmost(tree->root); +} + +#if 0 +/* analyzer flags a memory leak in this code. We do not use it yet. */ +/* TODO: fix memory leak before restoring this. */ +/* An in-order traversal of the tree. */ +static void store(picosplay_node_t *node, void ***out); +void* picosplay_contents(picosplay_tree_t *tree) { + if(tree->size == 0) + return NULL; + void **out = malloc(tree->size * sizeof(void*)); + void ***tmp = &out; + store(tree->root, tmp); + return out - tree->size; +} + +static void store(picosplay_node_t *node, void ***out) { + if(node->left != NULL) + store(node->left, out); + **out = node->value; + (*out)++; + if(node->right != NULL) + store(node->right, out); +} +#endif +/* This mutates the parental relationships, copy pointer to old parent. */ +static void mark_gp(picosplay_node_t *child); + +/* Rotate to make the given child take its parent's place in the tree. */ +static void rotate(picosplay_node_t *child) { + picosplay_node_t *parent = child->parent; + assert(parent != NULL); + if(parent->left == child) { /* A left child given. */ + mark_gp(child); + parent->left = child->right; + if(child->right != NULL) + child->right->parent = parent; + child->right = parent; + } else { /* A right child given. */ + mark_gp(child); + parent->right = child->left; + if(child->left != NULL) + child->left->parent = parent; + child->left = parent; + } +} + +static void mark_gp(picosplay_node_t *child) { + picosplay_node_t *parent = child->parent; + picosplay_node_t *grand = parent->parent; + child->parent = grand; + parent->parent = child; + if(grand == NULL) + return; + if(grand->left == parent) + grand->left = child; + else + grand->right = child; +} + +static picosplay_node_t* leftmost(picosplay_node_t *node) { + picosplay_node_t *parent = NULL; + while(node != NULL) { + parent = node; + node = node->left; + } + return parent; +} + +static picosplay_node_t* rightmost(picosplay_node_t *node) { + picosplay_node_t *parent = NULL; + while(node != NULL) { + parent = node; + node = node->right; + } + return parent; +} + diff --git a/deps/picoquic/picosplay.h b/deps/picoquic/picosplay.h new file mode 100644 index 0000000..ce93b2a --- /dev/null +++ b/deps/picoquic/picosplay.h @@ -0,0 +1,66 @@ +/* +* Author: Christian Huitema +* Copyright (c) 2018, Private Octopus, Inc. +* All rights reserved. +* +* Permission to use, copy, modify, and distribute this software for any +* purpose with or without fee is hereby granted, provided that the above +* copyright notice and this permission notice appear in all copies. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL Private Octopus, Inc. BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* +* This code is copied and adapted from https://github.com/lrem/splay, +* copyright (c) Remigiusz Modrzejewski 2014, filed on Github with MIT license. +*/ + +#ifndef PICOSPLAY_H +#define PICOSPLAY_H + +#include + +typedef struct st_picosplay_node_t { + struct st_picosplay_node_t *parent, *left, *right; +} picosplay_node_t; + +typedef int64_t(*picosplay_comparator)(void *left, void *right); +typedef picosplay_node_t * (*picosplay_create)(void * value); +typedef void(*picosplay_delete_node)(void * tree, picosplay_node_t * node); +typedef void* (*picosplay_node_value)(picosplay_node_t * node); + +typedef struct st_picosplay_tree_t { + picosplay_node_t *root; + picosplay_comparator comp; + picosplay_create create; + picosplay_delete_node delete_node; + picosplay_node_value node_value; + int size; +} picosplay_tree_t; + + +void picosplay_init_tree(picosplay_tree_t* tree, picosplay_comparator comp, picosplay_create create, picosplay_delete_node delete_node, picosplay_node_value node_value); +picosplay_tree_t* picosplay_new_tree(picosplay_comparator comp, picosplay_create create, picosplay_delete_node delete_node, picosplay_node_value node_value); +picosplay_node_t* picosplay_insert(picosplay_tree_t *tree, void *value); +picosplay_node_t* picosplay_find(picosplay_tree_t *tree, void *value); +picosplay_node_t* picosplay_find_previous(picosplay_tree_t* tree, void* value); +picosplay_node_t* picosplay_first(picosplay_tree_t *tree); +picosplay_node_t* picosplay_next(picosplay_node_t *node); +picosplay_node_t* picosplay_last(picosplay_tree_t *tree); +#if 0 +/* analyzer flags a memory leak in this code. We do not use it yet. */ +/* TODO: fix memory leak before restoring this. */ +void* picosplay_contents(picosplay_tree_t *tree); +#endif +void picosplay_delete(picosplay_tree_t *tree, void *value); +void picosplay_delete_hint(picosplay_tree_t *tree, picosplay_node_t *node); +void picosplay_empty_tree(picosplay_tree_t *tree); + +#endif /* PICOSPLAY_H */ diff --git a/deps/picoquic/picotls/CMakeLists.txt b/deps/picoquic/picotls/CMakeLists.txt new file mode 100644 index 0000000..b045b1c --- /dev/null +++ b/deps/picoquic/picotls/CMakeLists.txt @@ -0,0 +1,83 @@ +CMAKE_MINIMUM_REQUIRED(VERSION 2.8.11) +CMAKE_POLICY(SET CMP0003 NEW) +cmake_policy(SET CMP0048 NEW) +project(picotls VERSION 0.0.1) + +FIND_PACKAGE(PkgConfig REQUIRED) + +SET(CMAKE_C_FLAGS "-std=c99 -Wall -O2 -g ${CC_WARNING_FLAGS} ${CMAKE_C_FLAGS}") +INCLUDE_DIRECTORIES( + deps/cifra/src/ext + deps/cifra/src + deps/micro-ecc + deps/picotest + include + ${CMAKE_CURRENT_BINARY_DIR}) +SET(MINICRYPTO_LIBRARY_FILES + deps/micro-ecc/uECC.c + deps/cifra/src/aes.c + deps/cifra/src/blockwise.c + deps/cifra/src/chacha20.c + deps/cifra/src/chash.c + deps/cifra/src/curve25519.c + deps/cifra/src/drbg.c + deps/cifra/src/hmac.c + deps/cifra/src/gcm.c + deps/cifra/src/gf128.c + deps/cifra/src/modes.c + deps/cifra/src/poly1305.c + deps/cifra/src/sha256.c + deps/cifra/src/sha512.c) +SET(CORE_FILES + lib/picotls.c + lib/pembase64.c) + +PKG_CHECK_MODULES(BROTLI_DEC libbrotlidec) +PKG_CHECK_MODULES(BROTLI_ENC libbrotlienc) +IF (BROTLI_DEC_FOUND AND BROTLI_ENC_FOUND) + INCLUDE_DIRECTORIES(${BROTLI_DEC_INCLUDE_DIRS} ${BROTLI_ENC_INCLUDE_DIRS}) + LINK_DIRECTORIES(${BROTLI_DEC_LIBRARY_DIRS} ${BROTLI_ENC_LIBRARY_DIRS}) + SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DPICOTLS_USE_BROTLI=1") + LIST(APPEND CORE_FILES + lib/certificate_compression.c) + LIST(APPEND CORE_EXTRA_LIBS ${BROTLI_DEC_LIBRARIES} ${BROTLI_ENC_LIBRARIES}) +ENDIF () + +ADD_LIBRARY(picotls-core ${CORE_FILES}) +TARGET_LINK_LIBRARIES(picotls-core ${CORE_EXTRA_LIBS}) + +ADD_LIBRARY(picotls-minicrypto EXCLUDE_FROM_ALL + ${MINICRYPTO_LIBRARY_FILES} + lib/cifra.c + lib/cifra/x25519.c + lib/cifra/chacha20.c + lib/cifra/aes128.c + lib/cifra/aes256.c + lib/cifra/random.c + lib/minicrypto-pem.c + lib/uecc.c + lib/asn1.c + lib/ffx.c) +TARGET_LINK_LIBRARIES(picotls-minicrypto picotls-core) + +FIND_PACKAGE(OpenSSL) +IF (OPENSSL_FOUND AND NOT (OPENSSL_VERSION VERSION_LESS "1.0.1")) + MESSAGE(STATUS " Enabling OpenSSL support") + INCLUDE_DIRECTORIES(${OPENSSL_INCLUDE_DIR}) + ADD_LIBRARY(picotls-openssl lib/openssl.c) + TARGET_LINK_LIBRARIES(picotls-openssl ${OPENSSL_LIBRARIES} picotls-core ${CMAKE_DL_LIBS}) + ADD_EXECUTABLE(picotls-esni EXCLUDE_FROM_ALL src/esni.c) + TARGET_LINK_LIBRARIES(picotls-esni picotls-openssl picotls-core ${OPENSSL_LIBRARIES} ${CMAKE_DL_LIBS}) +ELSE () + MESSAGE(WARNING "Disabling OpenSSL support (requires 1.0.1 or newer)") +ENDIF () + +IF ((CMAKE_SIZEOF_VOID_P EQUAL 8) AND + (CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64") OR + (CMAKE_SYSTEM_PROCESSOR STREQUAL "amd64") OR + (CMAKE_SYSTEM_PROCESSOR STREQUAL "AMD64")) + MESSAGE(STATUS " Enabling fusion support") + ADD_LIBRARY(picotls-fusion lib/fusion.c) + SET_TARGET_PROPERTIES(picotls-fusion PROPERTIES COMPILE_FLAGS "-mavx2 -maes -mpclmul") + TARGET_LINK_LIBRARIES(picotls-fusion picotls-core) +ENDIF () diff --git a/deps/picoquic/picotls/README.md b/deps/picoquic/picotls/README.md new file mode 100644 index 0000000..afbc60e --- /dev/null +++ b/deps/picoquic/picotls/README.md @@ -0,0 +1,82 @@ +picotls +=== + +[![Build Status](https://travis-ci.org/h2o/picotls.svg?branch=master)](https://travis-ci.org/h2o/picotls) + +Picotls is a [TLS 1.3 (RFC 8446)](https://tools.ietf.org/html/rfc8446) protocol stack written in C, with the following features: +* support for three crypto engines + * "OpenSSL" backend using libcrypto for crypto and X.509 operations + * "minicrypto" backend using [cifra](https://github.com/ctz/cifra) for most crypto and [micro-ecc](https://github.com/kmackay/micro-ecc) for secp256r1 + * ["fusion" AES-GCM engine, optimized for QUIC and other protocols that use short AEAD blocks](https://github.com/h2o/picotls/pull/310) +* support for PSK, PSK-DHE resumption using 0-RTT +* API for dealing directly with TLS handshake messages (essential for QUIC) +* support for new extensions: Encrypted SNI (wg-draft-02), Certificate Compression (wg-draft-10) + +Primary goal of the project is to create a fast, tiny, low-latency TLS 1.3 implementation that can be used with the HTTP/2 protocol stack and the upcoming QUIC stack of the [H2O HTTP/2 server](https://h2o.examp1e.net). + +The TLS protocol implementation of picotls is licensed under the MIT license. + +License and the cryptographic algorithms supported by the crypto bindings are as follows: + +| Binding | License | Key Exchange | Certificate | AEAD cipher | +|:-----:|:-----:|:-----:|:-----:|:-----:| +| minicrypto | [CC0](https://github.com/ctz/cifra/) / [2-clause BSD](https://github.com/kmackay/micro-ecc) | secp256r1, x25519 | ECDSA (P256)1 | AES-128-GCM, chacha20-poly1305 | +| OpenSSL | OpenSSL | secp256r1, secp384r1, secp521r1, x25519 | RSA, ECDSA (P256) | AES-128-GCM, AES-256-GCM, chacha20-poly1305 | + +Note 1: Minicrypto binding is capable of signing a handshake using the certificate's key, but cannot verify a signature sent by the peer. + +Building picotls +--- + +If you have cloned picotls from git then ensure that you have initialised the submodules: +``` +% git submodule init +% git submodule update +``` + +Build using cmake: +``` +% cmake . +% make +% make check +``` + +A dedicated documentation for using picotls with Visual Studio can be found in [WindowsPort.md](WindowsPort.md). + +Developer documentation +--- + +Developer documentation should be available on [the wiki](https://github.com/h2o/picotls/wiki). + +Using the cli command +--- + +Run the test server (at 127.0.0.1:8443): +``` +% ./cli -c /path/to/certificate.pem -k /path/to/private-key.pem 127.0.0.1 8443 +``` + +Connect to the test server: +``` +% ./cli 127.0.0.1 8443 +``` + +Using resumption: +``` +% ./cli -s session-file 127.0.0.1 8443 +``` +The session-file is read-write. +The cli server implements a single-entry session cache. +The cli server sends NewSessionTicket when it first sends application data after receiving ClientFinished. + +Using early-data: +``` +% ./cli -s session-file -e 127.0.0.1 8443 +``` +When `-e` option is used, client first waits for user input, and then sends CLIENT_HELLO along with the early-data. + +License +--- + +The software is provided under the MIT license. +Note that additional licences apply if you use the minicrypto binding (see above). diff --git a/deps/picoquic/picotls/WindowsPort.md b/deps/picoquic/picotls/WindowsPort.md new file mode 100644 index 0000000..b2989e3 --- /dev/null +++ b/deps/picoquic/picotls/WindowsPort.md @@ -0,0 +1,34 @@ +Compiling PicoTLS with Visual Studio 2017 + +The source contains a Visual Studio 2017 solution (picotls/picotlsvs/picotlsvs.sln) +which itself contains 5 projects: + +* Three libraries, picotls.lib and its dependencies cifra.lib and uecc.lib; + +* A test project, testopenssl.exe, which will run on a console and + execute the OpenSSL tests; + +* And, an example project, picotlsvs.exe, which will perform a TLS exchange + in memory, and demonstrate how to use PicoTLS in windows. + +The code has a dependency on OpenSSL. When building the 32 bit projects (WIN32) it +expects to find: + +* OpenSSL header files under $(OPENSSLDIR)\include + +* OpenSSL library libcrypto.lib under $(OPENSSLDIR) + +When building the 64 bits projects (X64), it expects to find: + +* OpenSSL header files under $(OPENSSL64DIR)\include + +* OpenSSL library libcrypto.lib under $(OPENSSL64DIR) + +You will need also to copy libcrypto.dll to the library that contains your +executable, or to otherwise register that library. Be sure to copy the 32 bit +library or 64 bit library, depending on which type of project you build. + +Only two configurations are tested: "X86 Debug" and "X86 Release". The X64 configurations +will compile, but are not tested yet. + +Feel free to provide feedback and contribute. diff --git a/deps/picoquic/picotls/deps/cifra/.travis.yml b/deps/picoquic/picotls/deps/cifra/.travis.yml new file mode 100644 index 0000000..44b1a26 --- /dev/null +++ b/deps/picoquic/picotls/deps/cifra/.travis.yml @@ -0,0 +1,29 @@ +language: c + +before_install: + - sudo pip install cpp-coveralls + +script: + - cd src + - make clean + - make test + +after_success: + - coveralls --exclude src/ext --gcov-options '\-lp' + +compiler: + - gcc + +env: + global: + - secure: "W9IUnUWNc03gIBGLPOxXCVJUOb3RY2fS4lgOo+wIsCSk0rsJAB6gruAZDoz1sMxCJyBhCv0dKV5WPkAxkZ0y55wiuUcCepgR0qis6S6rJxldPdEWc3RQaeU9LbVRKRBYUBqSsFlXr5pdOvl0CBDWmx8lhcRxAw8z+JVDrAGVsTQ=" + +addons: + coverity_scan: + project: + name: "ctz/cifra" + description: "Cifra is a collection of cryptographic primitives targeted at embedded use." + notification_email: jpixton@gmail.com + build_command_prepend: "cd src ; make clean" + build_command: "make" + branch_pattern: coverity-scan diff --git a/deps/picoquic/picotls/deps/cifra/COPYING b/deps/picoquic/picotls/deps/cifra/COPYING new file mode 100644 index 0000000..0e259d4 --- /dev/null +++ b/deps/picoquic/picotls/deps/cifra/COPYING @@ -0,0 +1,121 @@ +Creative Commons Legal Code + +CC0 1.0 Universal + + CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE + LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN + ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS + INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES + REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS + PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM + THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED + HEREUNDER. + +Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer +exclusive Copyright and Related Rights (defined below) upon the creator +and subsequent owner(s) (each and all, an "owner") of an original work of +authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for +the purpose of contributing to a commons of creative, cultural and +scientific works ("Commons") that the public can reliably and without fear +of later claims of infringement build upon, modify, incorporate in other +works, reuse and redistribute as freely as possible in any form whatsoever +and for any purposes, including without limitation commercial purposes. +These owners may contribute to the Commons to promote the ideal of a free +culture and the further production of creative, cultural and scientific +works, or to gain reputation or greater distribution for their Work in +part through the use and efforts of others. + +For these and/or other purposes and motivations, and without any +expectation of additional consideration or compensation, the person +associating CC0 with a Work (the "Affirmer"), to the extent that he or she +is an owner of Copyright and Related Rights in the Work, voluntarily +elects to apply CC0 to the Work and publicly distribute the Work under its +terms, with knowledge of his or her Copyright and Related Rights in the +Work and the meaning and intended legal effect of CC0 on those rights. + +1. Copyright and Related Rights. A Work made available under CC0 may be +protected by copyright and related or neighboring rights ("Copyright and +Related Rights"). Copyright and Related Rights include, but are not +limited to, the following: + + i. the right to reproduce, adapt, distribute, perform, display, + communicate, and translate a Work; + ii. moral rights retained by the original author(s) and/or performer(s); +iii. publicity and privacy rights pertaining to a person's image or + likeness depicted in a Work; + iv. rights protecting against unfair competition in regards to a Work, + subject to the limitations in paragraph 4(a), below; + v. rights protecting the extraction, dissemination, use and reuse of data + in a Work; + vi. database rights (such as those arising under Directive 96/9/EC of the + European Parliament and of the Council of 11 March 1996 on the legal + protection of databases, and under any national implementation + thereof, including any amended or successor version of such + directive); and +vii. other similar, equivalent or corresponding rights throughout the + world based on applicable law or treaty, and any national + implementations thereof. + +2. Waiver. To the greatest extent permitted by, but not in contravention +of, applicable law, Affirmer hereby overtly, fully, permanently, +irrevocably and unconditionally waives, abandons, and surrenders all of +Affirmer's Copyright and Related Rights and associated claims and causes +of action, whether now known or unknown (including existing as well as +future claims and causes of action), in the Work (i) in all territories +worldwide, (ii) for the maximum duration provided by applicable law or +treaty (including future time extensions), (iii) in any current or future +medium and for any number of copies, and (iv) for any purpose whatsoever, +including without limitation commercial, advertising or promotional +purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each +member of the public at large and to the detriment of Affirmer's heirs and +successors, fully intending that such Waiver shall not be subject to +revocation, rescission, cancellation, termination, or any other legal or +equitable action to disrupt the quiet enjoyment of the Work by the public +as contemplated by Affirmer's express Statement of Purpose. + +3. Public License Fallback. Should any part of the Waiver for any reason +be judged legally invalid or ineffective under applicable law, then the +Waiver shall be preserved to the maximum extent permitted taking into +account Affirmer's express Statement of Purpose. In addition, to the +extent the Waiver is so judged Affirmer hereby grants to each affected +person a royalty-free, non transferable, non sublicensable, non exclusive, +irrevocable and unconditional license to exercise Affirmer's Copyright and +Related Rights in the Work (i) in all territories worldwide, (ii) for the +maximum duration provided by applicable law or treaty (including future +time extensions), (iii) in any current or future medium and for any number +of copies, and (iv) for any purpose whatsoever, including without +limitation commercial, advertising or promotional purposes (the +"License"). The License shall be deemed effective as of the date CC0 was +applied by Affirmer to the Work. Should any part of the License for any +reason be judged legally invalid or ineffective under applicable law, such +partial invalidity or ineffectiveness shall not invalidate the remainder +of the License, and in such case Affirmer hereby affirms that he or she +will not (i) exercise any of his or her remaining Copyright and Related +Rights in the Work or (ii) assert any associated claims and causes of +action with respect to the Work, in either case contrary to Affirmer's +express Statement of Purpose. + +4. Limitations and Disclaimers. + + a. No trademark or patent rights held by Affirmer are waived, abandoned, + surrendered, licensed or otherwise affected by this document. + b. Affirmer offers the Work as-is and makes no representations or + warranties of any kind concerning the Work, express, implied, + statutory or otherwise, including without limitation warranties of + title, merchantability, fitness for a particular purpose, non + infringement, or the absence of latent or other defects, accuracy, or + the present or absence of errors, whether or not discoverable, all to + the greatest extent permissible under applicable law. + c. Affirmer disclaims responsibility for clearing rights of other persons + that may apply to the Work or any use thereof, including without + limitation any person's Copyright and Related Rights in the Work. + Further, Affirmer disclaims responsibility for obtaining any necessary + consents, permissions or other rights required for any use of the + Work. + d. Affirmer understands and acknowledges that Creative Commons is not a + party to this document and has no duty or obligation with respect to + this CC0 or use of the Work. diff --git a/deps/picoquic/picotls/deps/cifra/README.md b/deps/picoquic/picotls/deps/cifra/README.md new file mode 100644 index 0000000..e972912 --- /dev/null +++ b/deps/picoquic/picotls/deps/cifra/README.md @@ -0,0 +1,246 @@ +# Cifra +Cifra is a collection of cryptographic primitives targeted at embedded use. + +[![Build Status](https://travis-ci.org/ctz/cifra.svg?branch=master)](https://travis-ci.org/ctz/cifra) + +[![Documentation](https://readthedocs.org/projects/pip/badge/)](https://cifra.readthedocs.org/en/latest/) + +[![Analysis Status](https://scan.coverity.com/projects/4324/badge.svg)](https://scan.coverity.com/projects/4324) + +[![Coverage Status](https://coveralls.io/repos/ctz/cifra/badge.svg?branch=coveralls-scan)](https://coveralls.io/r/ctz/cifra?branch=coveralls-scan) + +## Aims +In order of descending emphasis, cifra aims for: + +* **Clarity** and **simplicity**. +* Countermeasures for side channel leaks inherent in some + algorithms. +* Suitability for embedded use. Particularly: cifra uses an + absolute minimum of the standard C library and is reasonably + efficient with respect to code and data space. + +## Features +* **AES** in the **GCM**, **CCM**, **EAX** and **OCB** authenticated encryption modes. +* **NORX** authenticated encryption system. +* **SHA224**, **SHA256**, **SHA384** and **SHA512** hash functions (including **HMAC** and **PBKDF2**). +* **SHA3-224**, **SHA3-256**, **SHA3-384**, **SHA3-512** hash functions (FIPS 202 compatible). +* **ChaCha20** and **Salsa20** stream ciphers. +* **Poly1305** one time MAC. +* 100% code coverage by line, zero static analysis defects, valgrind-clean. + +Additionally cifra imports curve25519 from elsewhere (μNaCl, NaCl, tweetNaCl, +Adam Langley's curve25519-donna) for comparison between various implementations +on embedded targets. + +## Documentation +Available at [Read the Docs](https://cifra.readthedocs.org/en/latest/). + +## Testing +There is quite a lot of testing available: + +* **Host builds**: run `make test` in the `src` directory. This builds and + runs assorted test programs. +* **Emulated embedded builds**: run `make test` in the `src/arm` directory. This + expects to find `qemu-system-gnuarmeclipse` on the path. These tests assume + a Cortex-M3 target. +* **Cortex-M0 on-target tests**: run `make test.stm32f0` in the `src/arm` directory. + This expects to find `openocd` on the path, with an STM32F0xx attached via + stlinkv2. It uses ARM semihosting to report results. +* **Cortex-M3/4 on-target tests**: run `make test.stm32f1` or `make test.stm32f3` as above. + +Additionally all embedded targets expect to find the `arm-none-eabi` toolchain +to be on the path. + +## Measurements +All measurements performed at `-Os` (optimise for space), on the following MCUs: + +Core | Part number | Price (1s) | Max clock | Flash | SRAM +---------- | ------------- | ------------ | ---------- | ----- | ----- +Cortex-M0 | STM32F030F4P6 | 1.17EUR | 48MHz | 16KB | 4KB +Cortex-M3 | STM32F103C8T6 | 2.87EUR | 72MHz | 64KB | 20KB +Cortex-M4F | STM32F303K6T6 | 4.53EUR | 72MHz | 32KB | 12KB + +More measurements are available for AEAD modes on my blog post: +[Benchmarking Modern Authenticated Encryption on €1 devices](http://jbp.io/2015/06/01/modern-authenticated-encryption-for-1-euro/). + +## AES +This test does a key schedule, then encrypts one block. + +### 128-bit key +Core | Cycles (key schedule + block) | Cycles (key schedule) | Cycles (block) | Stack | Code size +---------- | ----------------------------- | --------------------- | -------------- | ----- | --------- +Cortex-M0 | 7156 | 2147 | 5009 | 312B | 1020B +Cortex-M3 | 4692 | 1591 | 3101 | 300B | 960B +Cortex-M4F | 4591 | 1571 | 3020 | 300B | 960B + +### 256-bit key +Core | Cycles (key schedule + block) | Cycles (key schedule) | Cycles (block) | Stack | Code size +---------- | ----------------------------- | --------------------- | -------------- | ----- | --------- +Cortex-M0 | 10611 | 3650 | 6961 | 396B | 1100B +Cortex-M3 | 6735 | 2450 | 4285 | 380B | 1048B +Cortex-M4F | 6588 | 2416 | 4172 | 380B | 1048B + +## AES128-GCM +This test encrypts and authenticates a 16 byte message, +with 16 bytes additionally authenticated data. It includes +the initial key schedule. + +Core | Cycles | Stack | Code size +---------- | ------ | ----- | --------- +Cortex-M0 | 57022 | 796B | 2600B +Cortex-M3 | 44306 | 812B | 2644B +Cortex-M4F | 43657 | 812B | 2644B + +## AES128-EAX +This test encrypts and authenticates a 16 byte message, +with 16 bytes additionally authenticated data. It includes +the initial key schedule. + +Core | Cycles | Stack | Code size +---------- | ------ | ----- | --------- +Cortex-M0 | 50564 | 932B | 2836B +Cortex-M3 | 32855 | 932B | 2780B +Cortex-M4F | 32159 | 932B | 2780B + +## AES128-CCM +This test encrypts and authenticates a 16 byte message, +with 16 bytes additionally authenticated data. It includes +the initial key schedule. + +Core | Cycles | Stack | Code size +---------- | ------ | ----- | --------- +Cortex-M0 | 37677 | 772B | 2280B +Cortex-M3 | 24462 | 780B | 2256B +Cortex-M4F | 23949 | 780B | 2256B + +## NORX32 +This test encrypts and authenticates a 16 byte message, +with 16 bytes additionally authenticated data. + +Core | Cycles | Stack | Code size +---------- | ------ | ----- | --------- +Cortex-M0 | 10692 | 320B | 1636B +Cortex-M3 | 6909 | 320B | 1820B +Cortex-M4F | 6855 | 320B | 1820B + +## ChaCha20 +This test encrypts a 64 byte message. + +Core | Cycles | Stack | Code size +---------- | ------ | ----- | --------- +Cortex-M0 | 5981 | 564B | 1416B +Cortex-M3 | 3487 | 544B | 1328B +Cortex-M4F | 3468 | 544B | 1328B + +(For comparison with AES, add an AES256 key schedule plus 4 blocks. +That's about 33K cycles.) + +## Salsa20 +This test encrypts a 64 byte message. + +Core | Cycles | Stack | Code size +---------- | ------ | ----- | --------- +Cortex-M0 | 6173 | 560B | 1412B +Cortex-M3 | 3320 | 552B | 1272B +Cortex-M4F | 3311 | 552B | 1272B + +## SHA256 +This test hashes the empty string (one compression function invocation). + +Core | Cycles | Stack | Code size +---------- | ------ | ----- | --------- +Cortex-M0 | 11561 | 312B | 1760B +Cortex-M3 | 6530 | 300B | 1708B +Cortex-M4F | 6278 | 300B | 1708B + +## SHA512 +This test hashes the empty string (one compression function invocation). + +Core | Cycles | Stack | Code size +---------- | ------ | ----- | --------- +Cortex-M0 | 38447 | 796B | 2888B +Cortex-M3 | 28771 | 836B | 2988B +Cortex-M4F | 28777 | 836B | 2980B + +## SHA3-256 +This test hashes the empty string (one sponge permutation). + +Core | Cycles | Stack | Code size +---------- | ------ | ----- | --------- +Cortex-M0 | 93648 | 848B | 2212B +Cortex-M3 | 74321 | 856B | 2164B +Cortex-M4F | 72215 | 856B | 2140B + +## SHA3-512 +This test hashes the empty string (one sponge permutation). + +Core | Cycles | Stack | Code size +---------- | ------ | ----- | --------- +Cortex-M0 | 92565 | 880B | 2212B +Cortex-M3 | 73509 | 888B | 2164B +Cortex-M4F | 71419 | 888B | 2140B + +## HMAC-SHA256 +This test computes a MAC with a 32 byte key over the +message "hello world". + +Core | Cycles | Stack | Code size +---------- | ------ | ----- | --------- +Cortex-M0 | 48924 | 1328B | 2200B +Cortex-M3 | 28333 | 1324B | 2132B +Cortex-M4F | 27337 | 1324B | 2132B + +## Poly1305-AES +This test computes a MAC with a 32 byte key over the +message "hello world". It includes the AES nonce +processing. + +Core | Cycles | Stack | Code size +---------- | ------ | ----- | --------- +Cortex-M0 | 15719 | 704B | 1920B +Cortex-M3 | 11328 | 696B | 1964B +Cortex-M4F | 10706 | 696B | 1932B + +## Curve25519 +This test is one point multiplication. + +This uses the implementation from [μNaCl](http://munacl.cryptojedi.org/curve25519-cortexm0.shtml) +by Düll, Haase, Hinterwälder, Hutter, Paar, Sánchez and Schwabe. + +Core | Cycles | Stack | Code size +---------- | ------- | ----- | --------- +Cortex-M0 | 4070271 | 464B | 5596B +Cortex-M3 | 3720363 | 448B | 5536B +Cortex-M4F | 3720105 | 448B | 5536B + +See [curve25519-shootout](curve25519-shootout.md) for comparitive measurements +for other curve25519 implementations. + +## C library requirements +Cifra requires `memcpy`, `memset`, and `abort`. + +## Future +* ~~Keccak hash function (aka SHA3)~~. +* ~~Poly1305 one-time MAC~~. +* Constant time curve25519 for Cortex-M4F using the FPU. +* Constant time curve25519 for Cortex-M3 (avoiding the variable-time multiplier). + +## Notable past bugs/advisories +* [Issue #2](https://github.com/ctz/cifra/issues/2): in all versions before commit + [d62aa26e](https://github.com/ctz/cifra/commit/d62aa26e2c3c49e5b8a4298644cff290406d9357) + (September 16th 2015) too much padding was added when hashing messages of certain + lengths. Specifically, messages whose length satisfied `len % 64 = 55` for + SHA1/SHA224/SHA256 or `len % 128 = 119` for SHA384/SHA512. SHA3 was not affected. + Better testing is now in place. +* [Issue #3](https://github.com/ctz/cifra/issues/3): in all versions before commit + [82d77cd8](https://github.com/ctz/cifra/commit/82d77cd8323f6d4473fcb68517752a778970138d) + (April 16th 2016) EAX would produce wrong tags for empty AADs or messages. The + underlying CMAC is now more resistant to this case. + +## License +[CC0](https://creativecommons.org/publicdomain/zero/1.0/). + +Please attribute the author. This is a request only, and not a license term. + +## Author +Joseph Birr-Pixton diff --git a/deps/picoquic/picotls/deps/cifra/curve25519-shootout.md b/deps/picoquic/picotls/deps/cifra/curve25519-shootout.md new file mode 100644 index 0000000..6f05758 --- /dev/null +++ b/deps/picoquic/picotls/deps/cifra/curve25519-shootout.md @@ -0,0 +1,16 @@ +## Curve25519 on Cortex-M0 shootout +Implementation | Optimisation | Cycles | Code size | Stack usage +-------------- | ------------ | --------- | --------- | ----------- +donna | `-Os` | 15748K | 7.4KB | 3148B +donna | `-O2` | 15218K | 7.9KB | 3148B +donna | `-O3` | 12907K | 16KB | 3380B +naclref | `-Os` | 47813K | 3.2KB | 4012B +naclref | `-O2` | 34309K | 3.5KB | 4036B +naclref | `-O3` | 35059K | 4.1KB | 4044B +tweetnacl | `-Os` | 75979K | 2.8KB | 2244B +tweetnacl | `-O2` | 68876K | 3.0KB | 2268B +tweetnacl | `-O3` | 69622K | 8.9KB | 2900B + +naclref at -O2 seems to give a good balance. If you can spare the flash, +donna is quite significantly quicker. + diff --git a/deps/picoquic/picotls/deps/cifra/doc/Makefile b/deps/picoquic/picotls/deps/cifra/doc/Makefile new file mode 100644 index 0000000..2b7f5c0 --- /dev/null +++ b/deps/picoquic/picotls/deps/cifra/doc/Makefile @@ -0,0 +1,3 @@ +default: all +all: + sphinx-build -b html . _build diff --git a/deps/picoquic/picotls/deps/cifra/doc/build.py b/deps/picoquic/picotls/deps/cifra/doc/build.py new file mode 100644 index 0000000..0b00682 --- /dev/null +++ b/deps/picoquic/picotls/deps/cifra/doc/build.py @@ -0,0 +1,274 @@ +""" +Extracts documentation from cifra headers. + +We want to embed documentation in headers for good +locality. But want to write rst for formatting by sphinx. +This is a problem. + +'Breathe' provides a bridge between doxygen and sphinx, +but doxygen's documentation markup is pretty awful. + +Therefore, we write rst directly in C headers, and then +extract it here. The rules are: a C block comment +starting exactly '/* .. ' is dedented, and then included +verbatim. The convention is that the documentation +preceeds the C declarations they apply to, and that struct +members are documented before the struct they are contained +within. eg: + +/* .. c:function:: int foo(int bar) + * Foos a bar, returning the foo coefficient. + */ +int foo(int bar); + +/* .. c:type:: thing + * Container for things. + * + * .. c:member:: int thing.foo + * Count of foos. + * + * .. c:member:: int thing.bar + * Count of bars. + */ +typedef struct +{ + int foo; + int bar; +} thing; + +As a special effect, the following tokens are replaced +in the comments: + + - $DECL: the immediately following function declaration +""" + +import glob +import re +import StringIO + +# We know which headers constitute the external interface. +EXTERNAL = """ +aes +cf_config +chash +hmac +modes +pbkdf2 +prp +salsa20 +sha1 +sha2 +sha3 +norx +poly1305 +chacha20poly1305 +drbg +""".split() + +# Basic idea of a C identifier +C_IDENTIFIER = '[a-zA-Z_][a-zA-Z0-9_]+' + +DECL_RE = re.compile(r'^\s*(.+?' + C_IDENTIFIER + '\(.+?\));', re.MULTILINE | re.DOTALL) +COMMENT_RE = re.compile(r'^\s*\/\* (\.\..*?) \*\/$', re.MULTILINE | re.DOTALL) +INTRO_RE = re.compile(r'^\s*\/\*\*(.*?)\*\/$', re.MULTILINE | re.DOTALL) +NEW_SECTION_RE = re.compile(r'^..+\n(==+|--+)$', re.MULTILINE) + +class section(object): + def __init__(self): + self.intro = [] + self.macros = [] + self.types = [] + self.functions = [] + self.values = [] + + def __repr__(self): + return repr(self.format()[:30]) + + def __str__(self): + return repr(self.format()[:30]) + + def format(self): + f = StringIO.StringIO() + + def emit(title, section): + if len(section) == 0: + return + + if title: + print >>f, title + print >>f, '*' * len(title) + print >>f + + for s in section: + print >>f, s.getvalue() + + emit(None, self.intro) + emit('Macros', self.macros) + emit('Types', self.types) + emit('Functions', self.functions) + emit('Values', self.values) + + return f.getvalue() + + def is_empty(self): + items = len(self.intro) + len(self.macros) + len(self.types) + \ + len(self.functions) + len(self.values) + return 0 == items + + def add_item(self, sec): + f = StringIO.StringIO() + sec.append(f) + return f + +def massage_decl(decl): + """ + Tart-up a C function declaration: remove storage qualifiers, + smush onto one line, escape asterisks. + """ + for storage in 'extern static inline'.split(): + decl = decl.replace(storage + ' ', '') + + fixed_lines = ' '.join(line.strip() for line in decl.splitlines()) + + return fixed_lines.replace('*', '\\*') + +def replace_decl(comment, comment_match, header): + if '$DECL' not in comment: + return comment + + start = comment_match.end(0) + 1 + decl_match = DECL_RE.match(header, start) + + if decl_match is None: + print 'Cause:', comment + print 'Trailer:', header[start:start+60] + raise IOError('$DECL present but cannot find following DECL') + + decl = decl_match.group(1) + decl = massage_decl(decl) + return comment.replace('$DECL', decl) + +def decomment(lines): + for i in range(len(lines)): + if lines[i].startswith(' *'): + lines[i] = lines[i][3:] + lines[i] = lines[i].strip() + +def drop_empty_prefix(lines): + while len(lines) and lines[0] == '': + lines.pop(0) + +def starts_new_section(lines): + txt = '\n'.join(lines) + r = NEW_SECTION_RE.search(txt) is not None + return r + +def process(header, rst): + """ + Converts a header into restructured text. + + header is a file-like opened to read the header. + rst is a file-like opened to write the rst results. + """ + + hh = header.read() + + # Collect definitions into sections + sec = None + all_sections = [] + intro, macros, types, functions, values = [], [], [], [], [] + + def add_section(): + if sec and not sec.is_empty(): + all_sections.append(sec) + return section() + + sec = add_section() + + offs = 0 + + while True: + intro_match = INTRO_RE.search(hh, offs) + comment_match = COMMENT_RE.search(hh, offs) + + if intro_match is None and comment_match is None: + break + + # process earliest occuring + if intro_match is not None and (comment_match is None or intro_match.start(0) < comment_match.start(0)): + txt = intro_match.group(1) + + lines = txt.splitlines() + decomment(lines) + drop_empty_prefix(lines) + + if starts_new_section(lines): + sec = add_section() + + outf = sec.add_item(sec.intro) + + for l in lines: + print >>outf, l + offs = intro_match.end(0) + 1 + continue + + if comment_match is not None and (intro_match is None or comment_match.start(0) < intro_match.start(0)): + txt = comment_match.group(1) + + # work out which section this goes into + outf = None + if '.. c:macro::' in txt: + outf = sec.add_item(sec.macros) + elif '.. c:type::' in txt: + outf = sec.add_item(sec.types) + elif '.. c:var::' in txt: + outf = sec.add_item(sec.values) + elif '.. c:function::' in txt: + outf = sec.add_item(sec.functions) + elif '.. c:member::' in txt: + if len(types) == 0: + raise IOError('c:member must come after a c:type') + outf = types[-1] + else: + raise IOError('Cannot categorise item: ' + txt) + + # expand $DECL + txt = replace_decl(txt, comment_match, hh) + + # decomment lines + lines = txt.splitlines() + decomment(lines) + + # domain lines are unindented + while lines and lines[0].startswith('.. '): + print >>outf, lines.pop(0) + print >>outf + + # empty prefix lines are stripped + drop_empty_prefix(lines) + + # other lines are indented + for line in lines: + if len(line): + line = ' ' + line + print >>outf, line + + offs = comment_match.end(0) + 1 + continue + + add_section() + + for sec in all_sections: + rst.write(sec.format()) + +def run(): + for fn in EXTERNAL: + print '** build', fn + header = '../src/' + fn + '.h' + rst = fn + '.rst' + with open(header, 'r') as fh: + with open(rst, 'w') as fr: + process(fh, fr) + +if __name__ == '__main__': + run() diff --git a/deps/picoquic/picotls/deps/cifra/doc/conf.py b/deps/picoquic/picotls/deps/cifra/doc/conf.py new file mode 100644 index 0000000..8354354 --- /dev/null +++ b/deps/picoquic/picotls/deps/cifra/doc/conf.py @@ -0,0 +1,263 @@ +# -*- coding: utf-8 -*- +# +# Cifra documentation build configuration file, created by +# sphinx-quickstart on Sat Feb 21 18:02:37 2015. +# +# This file is execfile()d with the current directory set to its +# containing dir. +# +# Note that not all possible configuration values are present in this +# autogenerated file. +# +# All configuration values have a default; values that are commented out +# serve to show the default. + +import sys +import os + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +sys.path.insert(0, os.path.abspath('.')) + +import build +build.run() + +# -- General configuration ------------------------------------------------ + +# If your documentation needs a minimal Sphinx version, state it here. +#needs_sphinx = '1.0' + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom +# ones. +extensions = [ + 'sphinx.ext.mathjax', +] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# The suffix of source filenames. +source_suffix = '.rst' + +# The encoding of source files. +#source_encoding = 'utf-8-sig' + +# The master toctree document. +master_doc = 'index' + +# General information about the project. +project = u'Cifra' +copyright = u'2015, Joseph Birr-Pixton' + +# The version info for the project you're documenting, acts as replacement for +# |version| and |release|, also used in various other places throughout the +# built documents. +# +# The short X.Y version. +version = '0.1' +# The full version, including alpha/beta/rc tags. +release = '0.1' + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +#language = None + +# There are two options for replacing |today|: either, you set today to some +# non-false value, then it is used: +#today = '' +# Else, today_fmt is used as the format for a strftime call. +#today_fmt = '%B %d, %Y' + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +exclude_patterns = ['_build'] + +# The reST default role (used for this markup: `text`) to use for all +# documents. +#default_role = None + +# If true, '()' will be appended to :func: etc. cross-reference text. +#add_function_parentheses = True + +# If true, the current module name will be prepended to all description +# unit titles (such as .. function::). +#add_module_names = True + +# If true, sectionauthor and moduleauthor directives will be shown in the +# output. They are ignored by default. +#show_authors = False + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'sphinx' + +# A list of ignored prefixes for module index sorting. +#modindex_common_prefix = [] + +# If true, keep warnings as "system message" paragraphs in the built documents. +#keep_warnings = False + + +# -- Options for HTML output ---------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +html_theme = 'default' + +# Theme options are theme-specific and customize the look and feel of a theme +# further. For a list of options available for each theme, see the +# documentation. +#html_theme_options = {} + +# Add any paths that contain custom themes here, relative to this directory. +#html_theme_path = [] + +# The name for this set of Sphinx documents. If None, it defaults to +# " v documentation". +#html_title = None + +# A shorter title for the navigation bar. Default is the same as html_title. +#html_short_title = None + +# The name of an image file (relative to this directory) to place at the top +# of the sidebar. +#html_logo = None + +# The name of an image file (within the static path) to use as favicon of the +# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 +# pixels large. +#html_favicon = None + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ['_static'] + +# Add any extra paths that contain custom files (such as robots.txt or +# .htaccess) here, relative to this directory. These files are copied +# directly to the root of the documentation. +#html_extra_path = [] + +# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, +# using the given strftime format. +#html_last_updated_fmt = '%b %d, %Y' + +# If true, SmartyPants will be used to convert quotes and dashes to +# typographically correct entities. +#html_use_smartypants = True + +# Custom sidebar templates, maps document names to template names. +#html_sidebars = {} + +# Additional templates that should be rendered to pages, maps page names to +# template names. +#html_additional_pages = {} + +# If false, no module index is generated. +#html_domain_indices = True + +# If false, no index is generated. +#html_use_index = True + +# If true, the index is split into individual pages for each letter. +#html_split_index = False + +# If true, links to the reST sources are added to the pages. +#html_show_sourcelink = True + +# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. +#html_show_sphinx = True + +# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. +#html_show_copyright = True + +# If true, an OpenSearch description file will be output, and all pages will +# contain a tag referring to it. The value of this option must be the +# base URL from which the finished HTML is served. +#html_use_opensearch = '' + +# This is the file name suffix for HTML files (e.g. ".xhtml"). +#html_file_suffix = None + +# Output file base name for HTML help builder. +htmlhelp_basename = 'Cifradoc' + + +# -- Options for LaTeX output --------------------------------------------- + +latex_elements = { +# The paper size ('letterpaper' or 'a4paper'). +#'papersize': 'letterpaper', + +# The font size ('10pt', '11pt' or '12pt'). +#'pointsize': '10pt', + +# Additional stuff for the LaTeX preamble. +#'preamble': '', +} + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, +# author, documentclass [howto, manual, or own class]). +latex_documents = [ + ('index', 'Cifra.tex', u'Cifra Documentation', + u'Joseph Birr-Pixton', 'manual'), +] + +# The name of an image file (relative to this directory) to place at the top of +# the title page. +#latex_logo = None + +# For "manual" documents, if this is true, then toplevel headings are parts, +# not chapters. +#latex_use_parts = False + +# If true, show page references after internal links. +#latex_show_pagerefs = False + +# If true, show URL addresses after external links. +#latex_show_urls = False + +# Documents to append as an appendix to all manuals. +#latex_appendices = [] + +# If false, no module index is generated. +#latex_domain_indices = True + + +# -- Options for manual page output --------------------------------------- + +# One entry per manual page. List of tuples +# (source start file, name, description, authors, manual section). +man_pages = [ + ('index', 'cifra', u'Cifra Documentation', + [u'Joseph Birr-Pixton'], 1) +] + +# If true, show URL addresses after external links. +#man_show_urls = False + + +# -- Options for Texinfo output ------------------------------------------- + +# Grouping the document tree into Texinfo files. List of tuples +# (source start file, target name, title, author, +# dir menu entry, description, category) +texinfo_documents = [ + ('index', 'Cifra', u'Cifra Documentation', + u'Joseph Birr-Pixton', 'Cifra', 'One line description of project.', + 'Miscellaneous'), +] + +# Documents to append as an appendix to all manuals. +#texinfo_appendices = [] + +# If false, no module index is generated. +#texinfo_domain_indices = True + +# How to display URL addresses: 'footnote', 'no', or 'inline'. +#texinfo_show_urls = 'footnote' + +# If true, do not generate a @detailmenu in the "Top" node's menu. +#texinfo_no_detailmenu = False diff --git a/deps/picoquic/picotls/deps/cifra/doc/index.rst b/deps/picoquic/picotls/deps/cifra/doc/index.rst new file mode 100644 index 0000000..e853de4 --- /dev/null +++ b/deps/picoquic/picotls/deps/cifra/doc/index.rst @@ -0,0 +1,33 @@ +.. Cifra documentation master file, created by + sphinx-quickstart on Sat Feb 21 18:02:37 2015. + You can adapt this file completely to your liking, but it should at least + contain the root `toctree` directive. + +Welcome to Cifra's documentation! +================================= + +Contents: + +.. toctree:: + :maxdepth: 2 + + cf_config + prp + chash + aes + norx + salsa20 + modes + hmac + poly1305 + chacha20poly1305 + pbkdf2 + sha1 + sha2 + sha3 + drbg + +Index +----- +* :ref:`genindex` + diff --git a/deps/picoquic/picotls/deps/cifra/extra_vecs/.gitignore b/deps/picoquic/picotls/deps/cifra/extra_vecs/.gitignore new file mode 100644 index 0000000..2163875 --- /dev/null +++ b/deps/picoquic/picotls/deps/cifra/extra_vecs/.gitignore @@ -0,0 +1 @@ +openssl-hash diff --git a/deps/picoquic/picotls/deps/cifra/extra_vecs/Makefile b/deps/picoquic/picotls/deps/cifra/extra_vecs/Makefile new file mode 100644 index 0000000..065721c --- /dev/null +++ b/deps/picoquic/picotls/deps/cifra/extra_vecs/Makefile @@ -0,0 +1,13 @@ +CFLAGS += -std=gnu99 -O0 -g -Wall -Werror + +all: run + +openssl-hash: openssl-hash.c + $(CC) $(CFLAGS) -o $@ $^ -lcrypto + +run: openssl-hash + ./openssl-hash + python python-hash.py + +clean: + rm -rf *.o openssl-hash diff --git a/deps/picoquic/picotls/deps/cifra/extra_vecs/README.md b/deps/picoquic/picotls/deps/cifra/extra_vecs/README.md new file mode 100644 index 0000000..a7d3828 --- /dev/null +++ b/deps/picoquic/picotls/deps/cifra/extra_vecs/README.md @@ -0,0 +1,2 @@ +This directory contains assorted programs for generating test vectors +from other crypto libraries, like OpenSSL. diff --git a/deps/picoquic/picotls/deps/cifra/extra_vecs/openssl-hash.c b/deps/picoquic/picotls/deps/cifra/extra_vecs/openssl-hash.c new file mode 100644 index 0000000..9df6856 --- /dev/null +++ b/deps/picoquic/picotls/deps/cifra/extra_vecs/openssl-hash.c @@ -0,0 +1,60 @@ +#include +#include + +#include + +#define MAX_LENGTH 1024 + +static void printhex(const uint8_t *buf, size_t len) +{ + for (size_t i = 0; i < len; i++) + printf("%02x", buf[i]); +} + +/* This test produces a single hash value which depends on + * hashes with all preimage lengths up to max. + * + * It emits + * H(H(t(0)) || H(t(1)) || ... || H(t(max-1)))) + * where + * t(n) = (n % 256) ^ n + * (informally, t(n) is a n-length octet string of octets with value n mod 256) + */ +static void emit_length_test(const char *name, const EVP_MD *h, size_t max) +{ + EVP_MD_CTX outer, inner; + EVP_DigestInit(&outer, h); + uint8_t digest[EVP_MAX_MD_SIZE]; + unsigned int digestlen; + + for (size_t n = 0; n < max; n++) + { + EVP_DigestInit(&inner, h); + for (size_t i = 0; i < n; i++) + { + uint8_t byte = n & 0xff; + EVP_DigestUpdate(&inner, &byte, 1); + } + digestlen = sizeof digest; + EVP_DigestFinal(&inner, digest, &digestlen); + + EVP_DigestUpdate(&outer, digest, digestlen); + } + + digestlen = sizeof digest; + EVP_DigestFinal(&outer, digest, &digestlen); + + printf("%s(%zu) = ", name, max); + printhex(digest, (size_t) digestlen); + printf("\n"); +} + +int main(void) +{ + emit_length_test("SHA1", EVP_sha1(), MAX_LENGTH); + emit_length_test("SHA224", EVP_sha224(), MAX_LENGTH); + emit_length_test("SHA256", EVP_sha256(), MAX_LENGTH); + emit_length_test("SHA384", EVP_sha384(), MAX_LENGTH); + emit_length_test("SHA512", EVP_sha512(), MAX_LENGTH); + return 0; +} diff --git a/deps/picoquic/picotls/deps/cifra/extra_vecs/python-hash.py b/deps/picoquic/picotls/deps/cifra/extra_vecs/python-hash.py new file mode 100644 index 0000000..90aa248 --- /dev/null +++ b/deps/picoquic/picotls/deps/cifra/extra_vecs/python-hash.py @@ -0,0 +1,38 @@ +#!/usr/bin/python2 + +# +# see openssl-hash for details of what this is computing +# you'll need python-sha3 from https://github.com/bjornedstrom/python-sha3 +# + +import hashlib +import sha3 + +# check sha3 at least works; pysha3 *DOES NOT* (it is keccak, not sha3) +assert '3a985da74fe225b2045c172d6bd390bd855f086e3e9d525b46bfe24511431532' == hashlib.sha3_256('abc').hexdigest() + +def hh(x): + return ''.join(['\\x' + x[y:y+2] for y in range(0, len(x), 2)]) + +def len_test(name, H, max): + outer = H() + + for n in range(max): + inner = H() + inner.update(chr(n & 0xff) * n) + outer.update(inner.digest()) + + result = outer.hexdigest() + print '%s(%d) = %s or %s' % (name, max, result, hh(result)) + +if __name__ == '__main__': + MAX = 1024 + len_test('SHA1', hashlib.sha1, MAX) + len_test('SHA224', hashlib.sha224, MAX) + len_test('SHA256', hashlib.sha256, MAX) + len_test('SHA384', hashlib.sha384, MAX) + len_test('SHA512', hashlib.sha512, MAX) + len_test('SHA3-224', hashlib.sha3_224, MAX) + len_test('SHA3-256', hashlib.sha3_256, MAX) + len_test('SHA3-384', hashlib.sha3_384, MAX) + len_test('SHA3-512', hashlib.sha3_512, MAX) diff --git a/deps/picoquic/picotls/deps/cifra/shitlisp/.gitignore b/deps/picoquic/picotls/deps/cifra/shitlisp/.gitignore new file mode 100644 index 0000000..3bd3275 --- /dev/null +++ b/deps/picoquic/picotls/deps/cifra/shitlisp/.gitignore @@ -0,0 +1,5 @@ +*.o +*.so +*.gcov +*.gcno +*.gcda diff --git a/deps/picoquic/picotls/deps/cifra/shitlisp/Makefile b/deps/picoquic/picotls/deps/cifra/shitlisp/Makefile new file mode 100644 index 0000000..ccbda7e --- /dev/null +++ b/deps/picoquic/picotls/deps/cifra/shitlisp/Makefile @@ -0,0 +1,17 @@ +CFLAGS += -g -O0 -std=gnu99 -fPIC -Wall -Wextra -Werror -Wno-unused-parameter +CPPFLAGS += -I../src -I../../bignum/out -I../../shitlisp/out + +all: cifra.so + +SOURCES = aes.o sha256.o sha512.o chash.o hmac.o pbkdf2.o modes.o eax.o \ + blockwise.o cmac.o salsa20.o chacha20.o curve25519.o + +cifra.so: $(addprefix ../src/, $(SOURCES)) sl-cifra.o + $(CC) $(CPPFLAGS) $(CFLAGS) -o $@ $^ -shared + +clean: + rm -f *.o *.pyc $(TARGETS) *.gcov *.gcda *.gcno + +test: $(wildcard test-*.sl) cifra.so + ../../shitlisp/out/shitlisp --mod=./cifra.so $(wildcard test-*.sl) + diff --git a/deps/picoquic/picotls/deps/cifra/shitlisp/sl-cifra.c b/deps/picoquic/picotls/deps/cifra/shitlisp/sl-cifra.c new file mode 100644 index 0000000..6d7b944 --- /dev/null +++ b/deps/picoquic/picotls/deps/cifra/shitlisp/sl-cifra.c @@ -0,0 +1,206 @@ +#include "handy.h" +#include "dstr.h" +#include "shitlisp.h" +#include "aes.h" +#include "sha2.h" +#include "hmac.h" +#include "pbkdf2.h" + +#include + +static sl_value * aes_block_fn(sl_value *self, sl_value *args, sl_symboltab *tab, + void (*blockfn)(const cf_aes_context *ctx, + const uint8_t *in, + uint8_t *out)) +{ + sl_iter it = sl_iter_start(args); + sl_value *key = sl_iter_convert(&it, sl_preprocess_eval, sl_assert_bytes, tab); + sl_value *block = sl_iter_convert(&it, sl_preprocess_eval, sl_assert_bytes, tab); + + sl_value *ret = NULL; + + if (!key || !block || + (key->u.bytes.len != 16 && key->u.bytes.len != 24 && key->u.bytes.len != 32) || + block->u.bytes.len != AES_BLOCKSZ) + { + ret = sl_get_nil(); + goto x_err; + } + + cf_aes_context ctx; + cf_aes_init(&ctx, key->u.bytes.buf, key->u.bytes.len); + uint8_t blockout[AES_BLOCKSZ]; + blockfn(&ctx, block->u.bytes.buf, blockout); + ret = sl_new_bytes(blockout, AES_BLOCKSZ); + cf_aes_finish(&ctx); + +x_err: + sl_decref(key); + sl_decref(block); + return ret; +} + +static sl_value * aes_block_encrypt(sl_value *self, sl_value *args, sl_symboltab *tab) +{ + return aes_block_fn(self, args, tab, cf_aes_encrypt); +} + +static sl_value * aes_block_decrypt(sl_value *self, sl_value *args, sl_symboltab *tab) +{ + return aes_block_fn(self, args, tab, cf_aes_decrypt); +} + +/* Hashing */ +static sl_value * hash_fn(sl_value *self, sl_value *args, sl_symboltab *tab, const cf_chash *h) +{ + sl_iter it = sl_iter_start(args); + sl_value *msg = sl_iter_convert(&it, sl_preprocess_eval, sl_assert_bytes, tab); + + if (!msg) + return sl_get_nil(); + + cf_chash_ctx ctx; + assert(h->ctxsz <= CF_CHASH_MAXCTX); + h->init(&ctx); + h->update(&ctx, msg->u.bytes.buf, msg->u.bytes.len); + sl_decref(msg); + + uint8_t result[CF_MAXHASH]; + assert(h->hashsz <= CF_MAXHASH); + h->digest(&ctx, result); + + return sl_new_bytes(result, h->hashsz); +} + +static sl_value * sha224(sl_value *self, sl_value *args, sl_symboltab *tab) +{ + return hash_fn(self, args, tab, &cf_sha224); +} + +static sl_value * sha256(sl_value *self, sl_value *args, sl_symboltab *tab) +{ + return hash_fn(self, args, tab, &cf_sha256); +} + +static sl_value * sha384(sl_value *self, sl_value *args, sl_symboltab *tab) +{ + return hash_fn(self, args, tab, &cf_sha384); +} + +static sl_value * sha512(sl_value *self, sl_value *args, sl_symboltab *tab) +{ + return hash_fn(self, args, tab, &cf_sha512); +} + +/* HMAC */ +static sl_value * hmac_fn(sl_value *self, sl_value *args, sl_symboltab *tab, const cf_chash *h) +{ + sl_iter it = sl_iter_start(args); + sl_value *key = sl_iter_convert(&it, sl_preprocess_eval, sl_assert_bytes, tab); + sl_value *msg = sl_iter_convert(&it, sl_preprocess_eval, sl_assert_bytes, tab); + + if (!key || !msg) + { + sl_decref(key); + sl_decref(msg); + return sl_get_nil(); + } + + uint8_t result[CF_MAXHASH]; + cf_hmac(key->u.bytes.buf, key->u.bytes.len, + msg->u.bytes.buf, msg->u.bytes.len, + result, + h); + + sl_decref(key); + sl_decref(msg); + return sl_new_bytes(result, h->hashsz); +} + +static sl_value * hmac_sha224(sl_value *self, sl_value *args, sl_symboltab *tab) +{ return hmac_fn(self, args, tab, &cf_sha224); } + +static sl_value * hmac_sha256(sl_value *self, sl_value *args, sl_symboltab *tab) +{ return hmac_fn(self, args, tab, &cf_sha256); } + +static sl_value * hmac_sha384(sl_value *self, sl_value *args, sl_symboltab *tab) +{ return hmac_fn(self, args, tab, &cf_sha384); } + +static sl_value * hmac_sha512(sl_value *self, sl_value *args, sl_symboltab *tab) +{ return hmac_fn(self, args, tab, &cf_sha512); } + + +/* PBKDF2 */ +static sl_value * do_pbkdf2(const cf_chash *h, sl_value *pw, sl_value *salt, + uint32_t iterations, uint32_t outlen) +{ + dstr out; + dstr_init(&out); + if (dstr_expand(&out, outlen)) + return NULL; + + cf_pbkdf2_hmac(pw->u.bytes.buf, pw->u.bytes.len, + salt->u.bytes.buf, salt->u.bytes.len, + iterations, + (uint8_t *) out.start, outlen, + h); + + sl_value *ret = sl_new_bytes((uint8_t *) out.start, outlen); + dstr_free(&out); + return ret; +} + +static sl_value * pbkdf2_fn(sl_value *self, sl_value *args, sl_symboltab *tab, const cf_chash *h) +{ + sl_iter it = sl_iter_start(args); + sl_value *pw = sl_iter_convert(&it, sl_preprocess_eval, sl_assert_bytes, tab); + sl_value *salt = sl_iter_convert(&it, sl_preprocess_eval, sl_assert_bytes, tab); + sl_value *iterations = sl_iter_convert(&it, sl_preprocess_eval, sl_assert_integer, tab); + sl_value *outlen = sl_iter_convert(&it, sl_preprocess_eval, sl_assert_integer, tab); + + sl_value *ret; + + if (!pw || !salt || !iterations || !outlen) + ret = sl_get_nil(); + else + { + assert(bignum_len_words(&iterations->u.integer.bn) == 1); + assert(bignum_len_words(&outlen->u.integer.bn) == 1); + ret = do_pbkdf2(h, pw, salt, + iterations->u.integer.bn.v[0], + outlen->u.integer.bn.v[0]); + } + + sl_decref(pw); + sl_decref(salt); + sl_decref(iterations); + sl_decref(outlen); + return ret; +} + +static sl_value * pbkdf2_sha224(sl_value *self, sl_value *args, sl_symboltab *tab) +{ + return pbkdf2_fn(self, args, tab, &cf_sha224); +} + +static sl_value * pbkdf2_sha256(sl_value *self, sl_value *args, sl_symboltab *tab) +{ + return pbkdf2_fn(self, args, tab, &cf_sha256); +} + +int SL_MODULE_ENTRY(sl_symboltab *tab) +{ + ER(sl_symboltab_add_name_native(tab, "aes-encrypt", aes_block_encrypt)); + ER(sl_symboltab_add_name_native(tab, "aes-decrypt", aes_block_decrypt)); + ER(sl_symboltab_add_name_native(tab, "sha224", sha224)); + ER(sl_symboltab_add_name_native(tab, "sha256", sha256)); + ER(sl_symboltab_add_name_native(tab, "sha384", sha384)); + ER(sl_symboltab_add_name_native(tab, "sha512", sha512)); + ER(sl_symboltab_add_name_native(tab, "hmac-sha224", hmac_sha224)); + ER(sl_symboltab_add_name_native(tab, "hmac-sha256", hmac_sha256)); + ER(sl_symboltab_add_name_native(tab, "hmac-sha384", hmac_sha384)); + ER(sl_symboltab_add_name_native(tab, "hmac-sha512", hmac_sha512)); + ER(sl_symboltab_add_name_native(tab, "pbkdf2-sha224", pbkdf2_sha224)); + ER(sl_symboltab_add_name_native(tab, "pbkdf2-sha256", pbkdf2_sha256)); + return 0; +} diff --git a/deps/picoquic/picotls/deps/cifra/shitlisp/test-aes.sl b/deps/picoquic/picotls/deps/cifra/shitlisp/test-aes.sl new file mode 100644 index 0000000..5535c5d --- /dev/null +++ b/deps/picoquic/picotls/deps/cifra/shitlisp/test-aes.sl @@ -0,0 +1,91 @@ +(def check-aes (key pt ct) + (+ + (assert (= + ct + (aes-encrypt key pt))) + (assert (= + pt + (aes-decrypt key ct))) + ) +) + +(check-aes + [000102030405060708090a0b0c0d0e0f] + [00112233445566778899aabbccddeeff] + [69c4e0d86a7b0430d8cdb78070b4c55a] +) +(check-aes + [000102030405060708090a0b0c0d0e0f1011121314151617] + [00112233445566778899aabbccddeeff] + [dda97ca4864cdfe06eaf70a0ec0d7191] +) +(check-aes + [000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f] + [00112233445566778899aabbccddeeff] + [8ea2b7ca516745bfeafc49904b496089] +) + +(check-aes + [2b7e151628aed2a6abf7158809cf4f3c] + [6bc1bee22e409f96e93d7e117393172a] + [3ad77bb40d7a3660a89ecaf32466ef97] +) +(check-aes + [2b7e151628aed2a6abf7158809cf4f3c] + [ae2d8a571e03ac9c9eb76fac45af8e51] + [f5d3d58503b9699de785895a96fdbaaf] +) +(check-aes + [2b7e151628aed2a6abf7158809cf4f3c] + [30c81c46a35ce411e5fbc1191a0a52ef] + [43b1cd7f598ece23881b00e3ed030688] +) +(check-aes + [2b7e151628aed2a6abf7158809cf4f3c] + [f69f2445df4f9b17ad2b417be66c3710] + [7b0c785e27e8ad3f8223207104725dd4] +) + +(check-aes + [8e73b0f7da0e6452c810f32b809079e562f8ead2522c6b7b] + [6bc1bee22e409f96e93d7e117393172a] + [bd334f1d6e45f25ff712a214571fa5cc] +) +(check-aes + [8e73b0f7da0e6452c810f32b809079e562f8ead2522c6b7b] + [ae2d8a571e03ac9c9eb76fac45af8e51] + [974104846d0ad3ad7734ecb3ecee4eef] +) +(check-aes + [8e73b0f7da0e6452c810f32b809079e562f8ead2522c6b7b] + [30c81c46a35ce411e5fbc1191a0a52ef] + [ef7afd2270e2e60adce0ba2face6444e] +) +(check-aes + [8e73b0f7da0e6452c810f32b809079e562f8ead2522c6b7b] + [f69f2445df4f9b17ad2b417be66c3710] + [9a4b41ba738d6c72fb16691603c18e0e] +) + +(check-aes + [603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4] + [6bc1bee22e409f96e93d7e117393172a] + [f3eed1bdb5d2a03c064b5a7e3db181f8] +) +(check-aes + [603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4] + [ae2d8a571e03ac9c9eb76fac45af8e51] + [591ccb10d410ed26dc5ba74a31362870] +) +(check-aes + [603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4] + [30c81c46a35ce411e5fbc1191a0a52ef] + [b6ed21b99ca6f4f9f153e7b1beafed1d] +) +(check-aes + [603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4] + [f69f2445df4f9b17ad2b417be66c3710] + [23304b7a39f9f3ff067d8d8f9e24ecc7] +) + +(puts success) diff --git a/deps/picoquic/picotls/deps/cifra/shitlisp/test-pbkdf2.sl b/deps/picoquic/picotls/deps/cifra/shitlisp/test-pbkdf2.sl new file mode 100644 index 0000000..b87f397 --- /dev/null +++ b/deps/picoquic/picotls/deps/cifra/shitlisp/test-pbkdf2.sl @@ -0,0 +1,36 @@ +(assert (= + (pbkdf2-sha256 (bytes "password") (bytes "salt") 1 32) + [120fb6cffcf8b32c43e7225256c4f837a86548c92ccc35480805987cb70be17b]) +) + +(assert (= + (pbkdf2-sha256 (bytes "password") (bytes "salt") 2 32) + [ae4d0c95af6b46d32d0adff928f06dd02a303f8ef3c251dfd6e2d85a95474c43]) +) + +(assert (= + (pbkdf2-sha256 (bytes "password") (bytes "salt") 4096 32) + [c5e478d59288c841aa530db6845c4c8d962893a001ce4e11a4963873aa98134a]) +) + +(assert (= + (pbkdf2-sha256 (bytes "passwordPASSWORDpassword") (bytes "saltSALTsaltSALTsaltSALTsaltSALTsalt") 4096 40) + [348c89dbcbd32b2f32d814b8116e84cf2b17347ebc1800181c4e2a1fb8dd53e1c635518c7dac47e9]) +) + +(assert (= + (pbkdf2-sha256 [] (bytes "salt") 1024 32) + [9e83f279c040f2a11aa4a02b24c418f2d3cb39560c9627fa4f47e3bcc2897c3d]) +) + +(assert (= + (pbkdf2-sha256 (bytes "password") [] 1024 32) + [ea5808411eb0c7e830deab55096cee582761e22a9bc034e3ece925225b07bf46]) +) + +(assert (= + (pbkdf2-sha256 [7061737300776f7264] [7361006c74] 4096 16) + [89b69d0516f829893c696226650a8687]) +) + +(puts success) diff --git a/deps/picoquic/picotls/deps/cifra/shitlisp/test-sha224.sl b/deps/picoquic/picotls/deps/cifra/shitlisp/test-sha224.sl new file mode 100644 index 0000000..5de9e15 --- /dev/null +++ b/deps/picoquic/picotls/deps/cifra/shitlisp/test-sha224.sl @@ -0,0 +1,41 @@ +(assert (= + (sha224 (bytes "abc")) + [23097d22 3405d822 8642a477 bda255b3 2aadbce4 bda0b3f7 e36c9da7]) +) + +(assert (= + (sha224 []) + [d14a028c 2a3a2bc9 476102bb 288234c4 15a2b01f 828ea62a c5b3e42f]) +) + +(assert (= + (hmac-sha224 (* [0b] 20) (bytes "Hi There")) + [896fb1128abbdf196832107cd49df33f47b4b1169912ba4f53684b22]) +) + +(assert (= + (hmac-sha224 (bytes "Jefe") (bytes "what do ya want for nothing?")) + [a30e01098bc6dbbf45690f3a7e9e6d0f8bbea2a39e6148008fd05e44]) +) + +(assert (= + (hmac-sha224 (* [aa] 20) (* [dd] 50)) + [7fb3cb3588c6c1f6ffa9694d7d6ad2649365b0c1f65d69d1ec8333ea]) +) + +(assert (= + (hmac-sha224 [0102030405060708090a0b0c0d0e0f10111213141516171819] (* [cd] 50)) + [6c11506874013cac6a2abc1bb382627cec6a90d86efc012de7afec5a]) +) + +(assert (= + (hmac-sha224 (* [aa] 131) (bytes "Test Using Larger Than Block-Size Key - Hash Key First")) + [95e9a0db962095adaebe9b2d6f0dbce2d499f112f2d2b7273fa6870e]) +) + +(assert (= + (hmac-sha224 (* [aa] 131) (bytes "This is a test using a larger than block-size key and a larger than block-size data. The key needs to be hashed before being used by the HMAC algorithm.")) + [3a854166ac5d9f023f54d517d0b39dbd946770db9c2b95c9f6f565d1]) +) + +(puts success) diff --git a/deps/picoquic/picotls/deps/cifra/shitlisp/test-sha256.sl b/deps/picoquic/picotls/deps/cifra/shitlisp/test-sha256.sl new file mode 100644 index 0000000..83c0457 --- /dev/null +++ b/deps/picoquic/picotls/deps/cifra/shitlisp/test-sha256.sl @@ -0,0 +1,62 @@ +(assert (= + (sha256 (bytes "abc")) + [ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad]) +) + +(assert (= + (sha256 (bytes "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq")) + [248d6a61d20638b8e5c026930c3e6039a33ce45964ff2167f6ecedd419db06c1]) +) + +(assert (= + (sha256 (* (bytes "a") 1000000)) + [cdc76e5c9914fb9281a1c7e284d73e67f1809a48a497200e046d39ccc7112cd0]) +) + +(assert (= + (sha256 (bytes "The quick brown fox jumps over the lazy dog")) + [d7a8fbb307d7809469ca9abcb0082e4f8d5651e46d3cdb762d02d0bf37c9e592]) +) + +(assert (= + (sha256 (bytes "The quick brown fox jumps over the lazy cog")) + [e4c4d8f3bf76b692de791a173e05321150f7a345b46484fe427f6acc7ecc81be]) +) + +(assert (= + (sha256 []) + [e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855]) +) + +(assert (= + (hmac-sha256 (* [0b] 20) (bytes "Hi There")) + [b0344c61d8db38535ca8afceaf0bf12b881dc200c9833da726e9376c2e32cff7]) +) + +(assert (= + (hmac-sha256 (bytes "Jefe") (bytes "what do ya want for nothing?")) + [5bdcc146bf60754e6a042426089575c75a003f089d2739839dec58b964ec3843]) +) + +(assert (= + (hmac-sha256 (* [aa] 20) (* [dd] 50)) + [773ea91e36800e46854db8ebd09181a72959098b3ef8c122d9635514ced565fe]) +) + +(assert (= + (hmac-sha256 [0102030405060708090a0b0c0d0e0f10111213141516171819] (* [cd] 50)) + [82558a389a443c0ea4cc819899f2083a85f0faa3e578f8077a2e3ff46729665b]) +) + +(assert (= + (hmac-sha256 (* [aa] 131) (bytes "Test Using Larger Than Block-Size Key - Hash Key First")) + [60e431591ee0b67f0d8a26aacbf5b77f8e0bc6213728c5140546040f0ee37f54]) +) + +(assert (= + (hmac-sha256 (* [aa] 131) (bytes "This is a test using a larger than block-size key and a larger than block-size data. The key needs to be hashed before being used by the HMAC algorithm.")) + [9b09ffa71b942fcb27635fbcd5b0e944bfdc63644f0713938a7f51535c3a35e2]) +) + + +(puts success) diff --git a/deps/picoquic/picotls/deps/cifra/shitlisp/test-sha384.sl b/deps/picoquic/picotls/deps/cifra/shitlisp/test-sha384.sl new file mode 100644 index 0000000..33a00ee --- /dev/null +++ b/deps/picoquic/picotls/deps/cifra/shitlisp/test-sha384.sl @@ -0,0 +1,67 @@ +(assert (= + (sha384 (bytes "abc")) + [cb00753f45a35e8b b5a03d699ac65007 272c32ab0eded163 1a8b605a43ff5bed 8086072ba1e7cc23 58baeca134c825a7]) +) + +(assert (= + (sha384 (bytes "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq")) + [3391fdddfc8dc739 3707a65b1b470939 7cf8b1d162af05ab fe8f450de5f36bc6 b0455a8520bc4e6f 5fe95b1fe3c8452b]) +) + +(assert (= + (sha384 (bytes "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu")) + [09330c33f71147e8 3d192fc782cd1b47 53111b173b3b05d2 2fa08086e3b0f712 fcc7c71a557e2db9 66c3e9fa91746039]) +) + +(assert (= + (sha384 (* (bytes "a") 1000000)) + [9d0e1809716474cb 086e834e310a4a1c ed149e9c00f24852 7972cec5704c2a5b 07b8b3dc38ecc4eb ae97ddd87f3d8985]) +) + +(assert (= + (sha384 (bytes "The quick brown fox jumps over the lazy dog")) + [ca737f1014a48f4c0b6dd43cb177b0afd9e5169367544c494011e3317dbf9a509cb1e5dc1e85a941bbee3d7f2afbc9b1]) +) + +(assert (= + (sha384 (bytes "The quick brown fox jumps over the lazy cog")) + [098cea620b0978caa5f0befba6ddcf22764bea977e1c70b3483edfdf1de25f4b40d6cea3cadf00f809d422feb1f0161b]) +) + +(assert (= + (sha384 []) + [38b060a751ac9638 4cd9327eb1b1e36a 21fdb71114be0743 4c0cc7bf63f6e1da 274edebfe76f65fb d51ad2f14898b95b]) +) + +(assert (= + (hmac-sha384 (* [0b] 20) (bytes "Hi There")) + [afd03944d84895626b0825f4ab46907f15f9dadbe4101ec682aa034c7cebc59cfaea9ea9076ede7f4af152e8b2fa9cb6]) +) + +(assert (= + (hmac-sha384 (bytes "Jefe") (bytes "what do ya want for nothing?")) + [af45d2e376484031617f78d2b58a6b1b9c7ef464f5a01b47e42ec3736322445e8e2240ca5e69e2c78b3239ecfab21649]) +) + +(assert (= + (hmac-sha384 (* [aa] 20) (* [dd] 50)) + [88062608d3e6ad8a0aa2ace014c8a86f0aa635d947ac9febe83ef4e55966144b2a5ab39dc13814b94e3ab6e101a34f27]) +) + +(assert (= + (hmac-sha384 [0102030405060708090a0b0c0d0e0f10111213141516171819] (* [cd] 50)) + [3e8a69b7783c25851933ab6290af6ca77a9981480850009cc5577c6e1f573b4e6801dd23c4a7d679ccf8a386c674cffb]) +) + +(assert (= + (hmac-sha384 (* [aa] 131) (bytes "Test Using Larger Than Block-Size Key - Hash Key First")) + [4ece084485813e9088d2c63a041bc5b44f9ef1012a2b588f3cd11f05033ac4c60c2ef6ab4030fe8296248df163f44952]) +) + +(assert (= + (hmac-sha384 (* [aa] 131) (bytes "This is a test using a larger than block-size key and a larger than block-size data. The key needs to be hashed before being used by the HMAC algorithm.")) + [6617178e941f020d351e2f254e8fd32c602420feb0b8fb9adccebb82461e99c5a678cc31e799176d3860e6110c46523e]) +) + + +(puts success) diff --git a/deps/picoquic/picotls/deps/cifra/shitlisp/test-sha512.sl b/deps/picoquic/picotls/deps/cifra/shitlisp/test-sha512.sl new file mode 100644 index 0000000..4201019 --- /dev/null +++ b/deps/picoquic/picotls/deps/cifra/shitlisp/test-sha512.sl @@ -0,0 +1,67 @@ +(assert (= + (sha512 (bytes "abc")) + [ddaf35a193617aba cc417349ae204131 12e6fa4e89a97ea2 0a9eeee64b55d39a 2192992a274fc1a8 36ba3c23a3feebbd 454d4423643ce80e 2a9ac94fa54ca49f]) +) + +(assert (= + (sha512 (bytes "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq")) + [204a8fc6dda82f0a 0ced7beb8e08a416 57c16ef468b228a8 279be331a703c335 96fd15c13b1b07f9 aa1d3bea57789ca0 31ad85c7a71dd703 54ec631238ca3445]) +) + +(assert (= + (sha512 (bytes "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu")) + [8e959b75dae313da 8cf4f72814fc143f 8f7779c6eb9f7fa1 7299aeadb6889018 501d289e4900f7e4 331b99dec4b5433a c7d329eeb6dd2654 5e96e55b874be909]) +) + +(assert (= + (sha512 (* (bytes "a") 1000000)) + [e718483d0ce76964 4e2e42c7bc15b463 8e1f98b13b204428 5632a803afa973eb de0ff244877ea60a 4cb0432ce577c31b eb009c5c2c49aa2e 4eadb217ad8cc09b]) +) + +(assert (= + (sha512 (bytes "The quick brown fox jumps over the lazy dog")) + [07e547d9586f6a73f73fbac0435ed76951218fb7d0c8d788a309d785436bbb642e93a252a954f23912547d1e8a3b5ed6e1bfd7097821233fa0538f3db854fee6]) +) + +(assert (= + (sha512 (bytes "The quick brown fox jumps over the lazy cog")) + [3eeee1d0 e11733ef 152a6c29 503b3ae2 0c4f1f3c da4cb26f 1bc1a41f 91c7fe4a b3bd8649 4049e201 c4bd5155 f31ecb7a 3c860684 3c4cc8df cab7da11 c8ae5045]) +) + +(assert (= + (sha512 []) + [cf83e1357eefb8bd f1542850d66d8007 d620e4050b5715dc 83f4a921d36ce9ce 47d0d13c5d85f2b0 ff8318d2877eec2f 63b931bd47417a81 a538327af927da3e]) +) + +(assert (= + (hmac-sha512 (* [0b] 20) (bytes "Hi There")) + [87aa7cdea5ef619d4ff0b4241a1d6cb02379f4e2ce4ec2787ad0b30545e17cdedaa833b7d6b8a702038b274eaea3f4e4be9d914eeb61f1702e696c203a126854]) +) + +(assert (= + (hmac-sha512 (bytes "Jefe") (bytes "what do ya want for nothing?")) + [164b7a7bfcf819e2e395fbe73b56e0a387bd64222e831fd610270cd7ea2505549758bf75c05a994a6d034f65f8f0e6fdcaeab1a34d4a6b4b636e070a38bce737]) +) + +(assert (= + (hmac-sha512 (* [aa] 20) (* [dd] 50)) + [fa73b0089d56a284efb0f0756c890be9b1b5dbdd8ee81a3655f83e33b2279d39bf3e848279a722c806b485a47e67c807b946a337bee8942674278859e13292fb]) +) + +(assert (= + (hmac-sha512 [0102030405060708090a0b0c0d0e0f10111213141516171819] (* [cd] 50)) + [b0ba465637458c6990e5a8c5f61d4af7e576d97ff94b872de76f8050361ee3dba91ca5c11aa25eb4d679275cc5788063a5f19741120c4f2de2adebeb10a298dd]) +) + +(assert (= + (hmac-sha512 (* [aa] 131) (bytes "Test Using Larger Than Block-Size Key - Hash Key First")) + [80b24263c7c1a3ebb71493c1dd7be8b49b46d1f41b4aeec1121b013783f8f3526b56d037e05f2598bd0fd2215d6a1e5295e64f73f63f0aec8b915a985d786598]) +) + +(assert (= + (hmac-sha512 (* [aa] 131) (bytes "This is a test using a larger than block-size key and a larger than block-size data. The key needs to be hashed before being used by the HMAC algorithm.")) + [e37b6a775dc87dbaa4dfa9f96e5e3ffddebd71f8867289865df5a32d20cdc944b6022cac3c4982b10d5eeb55c3e4de15134676fb6de0446065c97440fa8c6a58]) +) + + +(puts success) diff --git a/deps/picoquic/picotls/deps/cifra/src/.gitignore b/deps/picoquic/picotls/deps/cifra/src/.gitignore new file mode 100644 index 0000000..abc5da3 --- /dev/null +++ b/deps/picoquic/picotls/deps/cifra/src/.gitignore @@ -0,0 +1,15 @@ +*.o +*.gcov +*.gcno +*.gcda +testaes +testmodes +testsha1 +testsha2 +testsha3 +testsalsa20 +testcurve25519 +testpoly1305 +testnorx +testchacha20poly1305 +testdrbg diff --git a/deps/picoquic/picotls/deps/cifra/src/Makefile b/deps/picoquic/picotls/deps/cifra/src/Makefile new file mode 100644 index 0000000..451548d --- /dev/null +++ b/deps/picoquic/picotls/deps/cifra/src/Makefile @@ -0,0 +1,54 @@ +CFLAGS += -g -O0 -std=gnu99 -fPIC -Wall -Wextra -Werror \ + -Wno-unused-parameter -Wno-missing-field-initializers +CPPFLAGS += -I./ext + +ifdef WITH_ASAN + LDFLAGS += -fsanitize=address + CFLAGS += -fsanitize=address +endif + +ifdef WITH_COVERAGE + LDFLAGS += -coverage + CFLAGS += -coverage +endif + +ifdef WITH_VALGRIND + VALGRIND := valgrind --leak-check=full --show-reachable=yes --track-origins=yes + TEST_OPT := --no-exec +endif + +TARGETS = testaes testmodes testsha1 testsha2 testsha3 testsalsa20 \ + testcurve25519 testpoly1305 testnorx testchacha20poly1305 \ + testdrbg +all: $(TARGETS) + +SOURCES = aes.o sha256.o sha512.o chash.o hmac.o pbkdf2.o modes.o eax.o \ + gf128.o blockwise.o cmac.o salsa20.o chacha20.o curve25519.o \ + gcm.o cbcmac.o ccm.o sha3.o sha1.o poly1305.o \ + norx.o chacha20poly1305.o drbg.o ocb.o + +testaes: $(SOURCES) testaes.o +testmodes: $(SOURCES) testmodes.o +testsha1: $(SOURCES) testsha1.o +testsha2: $(SOURCES) testsha2.o +testsha3: $(SOURCES) testsha3.o +testsalsa20: $(SOURCES) testsalsa20.o +testcurve25519: $(SOURCES) testcurve25519.o +testpoly1305: $(SOURCES) testpoly1305.o +testnorx: $(SOURCES) testnorx.o +testchacha20poly1305: $(SOURCES) testchacha20poly1305.o +testdrbg: $(SOURCES) testdrbg.o + +clean: + rm -f *.o *.pyc $(TARGETS) *.gcov *.gcda *.gcno + +test: $(TARGETS) + for x in $(TARGETS) ; do \ + echo "Running $$x" ; \ + $(VALGRIND) ./$$x $(TEST_OPT) ; \ + done + +cover: test + gcov *.c + echo 'Lines with missing coverage:' + grep '#####' *.gcov | grep -vE '(cutest|testutil).h.gcov' diff --git a/deps/picoquic/picotls/deps/cifra/src/aes.c b/deps/picoquic/picotls/deps/cifra/src/aes.c new file mode 100644 index 0000000..545588e --- /dev/null +++ b/deps/picoquic/picotls/deps/cifra/src/aes.c @@ -0,0 +1,419 @@ +/* + * cifra - embedded cryptography library + * Written in 2014 by Joseph Birr-Pixton + * + * To the extent possible under law, the author(s) have dedicated all + * copyright and related and neighboring rights to this software to the + * public domain worldwide. This software is distributed without any + * warranty. + * + * You should have received a copy of the CC0 Public Domain Dedication + * along with this software. If not, see + * . + */ + +#include +#include + +#include "cf_config.h" +#include "aes.h" +#include "handy.h" +#include "bitops.h" +#include "tassert.h" + +static const uint8_t S[256] = +{ + 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, + 0xd7, 0xab, 0x76, 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, + 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, + 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, 0x04, 0xc7, 0x23, 0xc3, + 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75, 0x09, + 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, + 0x2f, 0x84, 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, + 0x39, 0x4a, 0x4c, 0x58, 0xcf, 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, + 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8, 0x51, 0xa3, 0x40, 0x8f, 0x92, + 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, 0xcd, 0x0c, + 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, + 0x73, 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, + 0xde, 0x5e, 0x0b, 0xdb, 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, + 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, + 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08, 0xba, 0x78, 0x25, + 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a, + 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, + 0xc1, 0x1d, 0x9e, 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, + 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, + 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16 +}; + +static const uint8_t Rcon[11] = +{ + 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36 +}; + +#ifdef INLINE_FUNCS +static inline uint32_t word4(uint8_t b0, uint8_t b1, uint8_t b2, uint8_t b3) +{ + return b0 << 24 | b1 << 16 | b2 << 8 | b3; +} + +static inline uint8_t byte(uint32_t w, unsigned x) +{ + /* nb. bytes are numbered 0 (leftmost, top) + * to 3 (rightmost). */ + x = 3 - x; + return (w >> (x * 8)) & 0xff; +} + +static uint32_t round_constant(uint32_t i) +{ + return Rcon[i] << 24; +} + +static uint32_t rot_word(uint32_t w) +{ + /* Takes + * word [a0,a1,a2,a3] + * returns + * word [a1,a2,a3,a0] + * + */ + return rotl32(w, 8); +} +#endif + +#define word4(a, b, c, d) (((uint32_t)(a) << 24) | ((uint32_t)(b) << 16) | ((uint32_t)(c) << 8) | (d)) +#define byte(w, x) ((w >> ((3 - (x)) << 3)) & 0xff) +#define round_constant(i) ((uint32_t)(Rcon[i]) << 24) +#define rot_word(w) rotl32((w), 8) + +static uint32_t sub_word(uint32_t w, const uint8_t *sbox) +{ + uint8_t a = byte(w, 0), + b = byte(w, 1), + c = byte(w, 2), + d = byte(w, 3); +#if CF_CACHE_SIDE_CHANNEL_PROTECTION + select_u8x4(&a, &b, &c, &d, sbox, 256); +#else + a = sbox[a]; + b = sbox[b]; + c = sbox[c]; + d = sbox[d]; +#endif + return word4(a, b, c, d); +} + +static void aes_schedule(cf_aes_context *ctx, const uint8_t *key, size_t nkey) +{ + size_t i, + nb = AES_BLOCKSZ / 4, + nk = nkey / 4, + n = nb * (ctx->rounds + 1); + uint32_t *w = ctx->ks; + + /* First words are just the key. */ + for (i = 0; i < nk; i++) + { + w[i] = read32_be(key + i * 4); + } + + uint32_t i_div_nk = 1; + uint32_t i_mod_nk = 0; + + for (; i < n; i++, i_mod_nk++) + { + uint32_t temp = w[i - 1]; + + if (i_mod_nk == nk) + { + i_div_nk++; + i_mod_nk = 0; + } + + if (i_mod_nk == 0) + temp = sub_word(rot_word(temp), S) ^ round_constant(i_div_nk); + else if (nk > 6 && i_mod_nk == 4) + temp = sub_word(temp, S); + + w[i] = w[i - nk] ^ temp; + } +} + +void cf_aes_init(cf_aes_context *ctx, const uint8_t *key, size_t nkey) +{ + memset(ctx, 0, sizeof *ctx); + + switch (nkey) + { +#if CF_AES_MAXROUNDS >= AES128_ROUNDS + case 16: + ctx->rounds = AES128_ROUNDS; + aes_schedule(ctx, key, nkey); + break; +#endif + +#if CF_AES_MAXROUNDS >= AES192_ROUNDS + case 24: + ctx->rounds = AES192_ROUNDS; + aes_schedule(ctx, key, nkey); + break; +#endif + +#if CF_AES_MAXROUNDS >= AES256_ROUNDS + case 32: + ctx->rounds = AES256_ROUNDS; + aes_schedule(ctx, key, nkey); + break; +#endif + + default: + abort(); + } +} + +static void add_round_key(uint32_t state[4], const uint32_t rk[4]) +{ + state[0] ^= rk[0]; + state[1] ^= rk[1]; + state[2] ^= rk[2]; + state[3] ^= rk[3]; +} + +static void sub_block(uint32_t state[4]) +{ + state[0] = sub_word(state[0], S); + state[1] = sub_word(state[1], S); + state[2] = sub_word(state[2], S); + state[3] = sub_word(state[3], S); +} + +static void shift_rows(uint32_t state[4]) +{ + uint32_t u, v, x, y; + + u = word4(byte(state[0], 0), + byte(state[1], 1), + byte(state[2], 2), + byte(state[3], 3)); + + v = word4(byte(state[1], 0), + byte(state[2], 1), + byte(state[3], 2), + byte(state[0], 3)); + + x = word4(byte(state[2], 0), + byte(state[3], 1), + byte(state[0], 2), + byte(state[1], 3)); + + y = word4(byte(state[3], 0), + byte(state[0], 1), + byte(state[1], 2), + byte(state[2], 3)); + + state[0] = u; + state[1] = v; + state[2] = x; + state[3] = y; +} + +static uint32_t gf_poly_mul2(uint32_t x) +{ + return + ((x & 0x7f7f7f7f) << 1) ^ + (((x & 0x80808080) >> 7) * 0x1b); +} + +static uint32_t mix_column(uint32_t x) +{ + uint32_t x2 = gf_poly_mul2(x); + return x2 ^ rotr32(x ^ x2, 24) ^ rotr32(x, 16) ^ rotr32(x, 8); +} + +static void mix_columns(uint32_t state[4]) +{ + state[0] = mix_column(state[0]); + state[1] = mix_column(state[1]); + state[2] = mix_column(state[2]); + state[3] = mix_column(state[3]); +} + +void cf_aes_encrypt(const cf_aes_context *ctx, + const uint8_t in[AES_BLOCKSZ], + uint8_t out[AES_BLOCKSZ]) +{ + assert(ctx->rounds == AES128_ROUNDS || + ctx->rounds == AES192_ROUNDS || + ctx->rounds == AES256_ROUNDS); + + uint32_t state[4] = { + read32_be(in + 0), + read32_be(in + 4), + read32_be(in + 8), + read32_be(in + 12) + }; + + const uint32_t *round_keys = ctx->ks; + add_round_key(state, round_keys); + round_keys += 4; + + uint32_t round; + for (round = 1; round < ctx->rounds; round++) + { + sub_block(state); + shift_rows(state); + mix_columns(state); + add_round_key(state, round_keys); + round_keys += 4; + } + + sub_block(state); + shift_rows(state); + add_round_key(state, round_keys); + + write32_be(state[0], out + 0); + write32_be(state[1], out + 4); + write32_be(state[2], out + 8); + write32_be(state[3], out + 12); +} + +#if CF_AES_ENCRYPT_ONLY == 0 +static const uint8_t S_inv[256] = +{ + 0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, + 0xf3, 0xd7, 0xfb, 0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, + 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb, 0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, + 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e, 0x08, 0x2e, 0xa1, 0x66, + 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25, 0x72, + 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, + 0xb6, 0x92, 0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, + 0x57, 0xa7, 0x8d, 0x9d, 0x84, 0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, + 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06, 0xd0, 0x2c, 0x1e, 0x8f, 0xca, + 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b, 0x3a, 0x91, + 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, + 0x73, 0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, + 0x1c, 0x75, 0xdf, 0x6e, 0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, + 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b, 0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, + 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4, 0x1f, 0xdd, 0xa8, + 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f, + 0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, + 0xc9, 0x9c, 0xef, 0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, + 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61, 0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, + 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d +}; + +static void inv_sub_block(uint32_t state[4]) +{ + state[0] = sub_word(state[0], S_inv); + state[1] = sub_word(state[1], S_inv); + state[2] = sub_word(state[2], S_inv); + state[3] = sub_word(state[3], S_inv); +} + +static void inv_shift_rows(uint32_t state[4]) +{ + uint32_t u, v, x, y; + + u = word4(byte(state[0], 0), + byte(state[3], 1), + byte(state[2], 2), + byte(state[1], 3)); + + v = word4(byte(state[1], 0), + byte(state[0], 1), + byte(state[3], 2), + byte(state[2], 3)); + + x = word4(byte(state[2], 0), + byte(state[1], 1), + byte(state[0], 2), + byte(state[3], 3)); + + y = word4(byte(state[3], 0), + byte(state[2], 1), + byte(state[1], 2), + byte(state[0], 3)); + + state[0] = u; + state[1] = v; + state[2] = x; + state[3] = y; +} + +static uint32_t inv_mix_column(uint32_t x) +{ + uint32_t x2 = gf_poly_mul2(x), + x4 = gf_poly_mul2(x2), + x9 = x ^ gf_poly_mul2(x4), + x11 = x2 ^ x9, + x13 = x4 ^ x9; + + return x ^ x2 ^ x13 ^ rotr32(x11, 24) ^ rotr32(x13, 16) ^ rotr32(x9, 8); +} + +static void inv_mix_columns(uint32_t state[4]) +{ + state[0] = inv_mix_column(state[0]); + state[1] = inv_mix_column(state[1]); + state[2] = inv_mix_column(state[2]); + state[3] = inv_mix_column(state[3]); +} + +void cf_aes_decrypt(const cf_aes_context *ctx, + const uint8_t in[AES_BLOCKSZ], + uint8_t out[AES_BLOCKSZ]) +{ + assert(ctx->rounds == AES128_ROUNDS || + ctx->rounds == AES192_ROUNDS || + ctx->rounds == AES256_ROUNDS); + + uint32_t state[4] = { + read32_be(in + 0), + read32_be(in + 4), + read32_be(in + 8), + read32_be(in + 12) + }; + + const uint32_t *round_keys = &ctx->ks[ctx->rounds << 2]; + add_round_key(state, round_keys); + round_keys -= 4; + + uint32_t round; + for (round = ctx->rounds - 1; round != 0; round--) + { + inv_shift_rows(state); + inv_sub_block(state); + add_round_key(state, round_keys); + inv_mix_columns(state); + round_keys -= 4; + } + + inv_shift_rows(state); + inv_sub_block(state); + add_round_key(state, round_keys); + + write32_be(state[0], out + 0); + write32_be(state[1], out + 4); + write32_be(state[2], out + 8); + write32_be(state[3], out + 12); +} +#else +void cf_aes_decrypt(const cf_aes_context *ctx, + const uint8_t in[AES_BLOCKSZ], + uint8_t out[AES_BLOCKSZ]) +{ + abort(); +} +#endif + +void cf_aes_finish(cf_aes_context *ctx) +{ + mem_clean(ctx, sizeof *ctx); +} + +const cf_prp cf_aes = { + .blocksz = AES_BLOCKSZ, + .encrypt = (cf_prp_block) cf_aes_encrypt, + .decrypt = (cf_prp_block) cf_aes_decrypt +}; + diff --git a/deps/picoquic/picotls/deps/cifra/src/aes.h b/deps/picoquic/picotls/deps/cifra/src/aes.h new file mode 100644 index 0000000..2ee7f46 --- /dev/null +++ b/deps/picoquic/picotls/deps/cifra/src/aes.h @@ -0,0 +1,152 @@ +/* + * cifra - embedded cryptography library + * Written in 2014 by Joseph Birr-Pixton + * + * To the extent possible under law, the author(s) have dedicated all + * copyright and related and neighboring rights to this software to the + * public domain worldwide. This software is distributed without any + * warranty. + * + * You should have received a copy of the CC0 Public Domain Dedication + * along with this software. If not, see + * . + */ + +/** + * The AES block cipher + * ==================== + * + * This is a small, simple implementation of AES. Key expansion is done + * first, filling in a :c:type:`cf_aes_context`. Then encryption and + * decryption can be performed as desired. + * + * Usually you don't want to use AES directly; you should use it via + * a :doc:`block cipher mode `. + */ + +#ifndef AES_H +#define AES_H + +#include +#include + +#include "prp.h" + +/* .. c:macro:: AES_BLOCKSZ + * AES has a 128-bit block size. This quantity is in bytes. + */ +#define AES_BLOCKSZ 16 + +/* --- Size configuration --- */ + +/* .. c:macro:: AES128_ROUNDS + * .. c:macro:: AES192_ROUNDS + * .. c:macro:: AES256_ROUNDS + * + * Round counts for different key sizes. + */ +#define AES128_ROUNDS 10 +#define AES192_ROUNDS 12 +#define AES256_ROUNDS 14 + +/* .. c:macro:: CF_AES_MAXROUNDS + * + * You can reduce the maximum number of rounds this implementation + * supports. This reduces the storage needed by :c:type:`cf_aes_context`. + * + * The default is :c:macro:`AES256_ROUNDS` and is good for all key + * sizes. + */ +#ifndef CF_AES_MAXROUNDS +# define CF_AES_MAXROUNDS AES256_ROUNDS +#endif + +/* .. c:macro:: CF_AES_ENCRYPT_ONLY + * + * Define this to 1 if you don't need to decrypt anything. + * This saves space. :c:func:`cf_aes_decrypt` calls `abort(3)`. + */ +#ifndef CF_AES_ENCRYPT_ONLY +# define CF_AES_ENCRYPT_ONLY 0 +#endif + +/* .. c:type:: cf_aes_context + * This type represents an expanded AES key. Create one + * using :c:func:`cf_aes_init`, make use of one using + * :c:func:`cf_aes_encrypt` or :c:func:`cf_aes_decrypt`. + * + * The contents of this structure are equivalent to the + * original key material. You should clean the + * contents of this structure with :c:func:`cf_aes_finish` + * when you're done. + * + * .. c:member:: cf_aes_context.rounds + * + * Number of rounds to use, set by :c:func:`cf_aes_init`. + * + * This depends on the original key size, and will be + * :c:macro:`AES128_ROUNDS`, :c:macro:`AES192_ROUNDS` or + * :c:macro:`AES256_ROUNDS`. + * + * .. c:member:: cf_aes_context.ks + * + * Expanded key material. Filled in by :c:func:`cf_aes_init`. + */ +typedef struct +{ + uint32_t rounds; + uint32_t ks[AES_BLOCKSZ / 4 * (CF_AES_MAXROUNDS + 1)]; +} cf_aes_context; + +/* .. c:function:: $DECL + * This function does AES key expansion. It destroys + * existing contents of :c:data:`ctx`. + * + * :param ctx: expanded key context, filled in by this function. + * :param key: pointer to key material, of :c:data:`nkey` bytes. + * :param nkey: length of key material. Must be `16`, `24` or `32`. + */ +extern void cf_aes_init(cf_aes_context *ctx, + const uint8_t *key, + size_t nkey); + +/* .. c:function:: $DECL + * Encrypts the given block, from :c:data:`in` to :c:data:`out`. + * These may alias. + * + * Fails at runtime if :c:data:`ctx` is invalid. + * + * :param ctx: expanded key context + * :param in: input block (read) + * :param out: output block (written) + */ +extern void cf_aes_encrypt(const cf_aes_context *ctx, + const uint8_t in[AES_BLOCKSZ], + uint8_t out[AES_BLOCKSZ]); + +/* .. c:function:: $DECL + * Decrypts the given block, from :c:data:`in` to :c:data:`out`. + * These may alias. + * + * Fails at runtime if :c:data:`ctx` is invalid. + * + * :param ctx: expanded key context + * :param in: input block (read) + * :param out: output block (written) + */ +extern void cf_aes_decrypt(const cf_aes_context *ctx, + const uint8_t in[AES_BLOCKSZ], + uint8_t out[AES_BLOCKSZ]); + +/* .. c:function:: $DECL + * Erase scheduled key material. + * + * Call this when you're done to erase the round keys. */ +extern void cf_aes_finish(cf_aes_context *ctx); + +/* .. c:var:: const cf_prp cf_aes + * Abstract interface to AES. See :c:type:`cf_prp` for + * more information. */ +extern const cf_prp cf_aes; + +#endif diff --git a/deps/picoquic/picotls/deps/cifra/src/arm/.gitignore b/deps/picoquic/picotls/deps/cifra/src/arm/.gitignore new file mode 100644 index 0000000..5841cb8 --- /dev/null +++ b/deps/picoquic/picotls/deps/cifra/src/arm/.gitignore @@ -0,0 +1,3 @@ +*.log +*.elf +*.bin diff --git a/deps/picoquic/picotls/deps/cifra/src/arm/Makefile b/deps/picoquic/picotls/deps/cifra/src/arm/Makefile new file mode 100644 index 0000000..7be9a53 --- /dev/null +++ b/deps/picoquic/picotls/deps/cifra/src/arm/Makefile @@ -0,0 +1,184 @@ +FUNCS = do_nothing stack_8w stack_64w \ + hashtest_sha256 hashtest_sha512 \ + hashtest_sha3_256 hashtest_sha3_512 \ + aes128block_test aes128sched_test \ + aes256block_test aes256sched_test \ + aes128gcm_test aes128eax_test \ + aes128ccm_test \ + salsa20_test chacha20_test \ + poly1305_test hmacsha256_test \ + curve25519_test \ + norx_test + +AEADS = aeadperf_aes128gcm \ + aeadperf_aes128ccm \ + aeadperf_aes128eax \ + aeadperf_aes256gcm \ + aeadperf_aes256ccm \ + aeadperf_aes256eax \ + aeadperf_norx \ + aeadperf_chacha20poly1305 +TESTS = testcurve25519 testaes testmodes testsalsa20 testsha1 testsha2 \ + testsha3 testpoly1305 testnorx testchacha20poly1305 testdrbg +ARCHS = stm32f0 stm32f1 stm32f3 efm32 qemucm3 + +all: $(patsubst %,%.stm32f0.bin,$(FUNCS) $(AEADS) $(TESTS)) \ + $(patsubst %,%.stm32f1.bin,$(FUNCS) $(AEADS) $(TESTS)) \ + $(patsubst %,%.stm32f3.bin,$(FUNCS) $(AEADS) $(TESTS)) \ + $(patsubst %,%.efm32.bin,$(FUNCS) $(AEADS) $(TESTS)) \ + $(patsubst %,%.qemucm3.bin,$(FUNCS) $(AEADS) $(TESTS)) + +%.stm32f0.elf: + arm-none-eabi-gcc $(CFLAGS) $(CFLAGS_$*) $(LDFLAGS) -T linkscript.stm32f0.ld -mcpu=cortex-m0 -DCORTEX_M0 -o $@ $^ -DTEST=$* -lgcc + +%.stm32f1.elf: + arm-none-eabi-gcc $(CFLAGS) $(CFLAGS_$*) $(LDFLAGS) -T linkscript.stm32f1.ld -mcpu=cortex-m3 -DCORTEX_M3 -o $@ $^ -DTEST=$* -lgcc + +%.stm32f3.elf: + arm-none-eabi-gcc $(CFLAGS) $(CFLAGS_$*) $(LDFLAGS) -T linkscript.stm32f3.ld -mcpu=cortex-m4 -DCORTEX_M4 -o $@ $^ -DTEST=$* -lgcc + +%.efm32.elf: + arm-none-eabi-gcc $(CFLAGS) $(CFLAGS_$*) $(LDFLAGS) -T linkscript.efm32.ld -mcpu=cortex-m0 -DCORTEX_M0 -o $@ $^ -DTEST=$* -lgcc + +%.qemucm3.elf: + arm-none-eabi-gcc $(CFLAGS) $(CFLAGS_$*) $(LDFLAGS) -T linkscript.qemucm3.ld -mcpu=cortex-m3 -DCORTEX_M3 -o $@ $^ -DTEST=$* -lgcc + +%.bin: %.elf + arm-none-eabi-objcopy -O binary $< $@ +.PRECIOUS: %.bin + +AES_OPTIONS = -DCF_AES_ENCRYPT_ONLY=1 -DCF_SIDE_CHANNEL_PROTECTION=0 +AES128_OPTIONS = -DCF_AES_MAXROUNDS=AES128_ROUNDS +AES256_OPTIONS = -DCF_AES_MAXROUNDS=AES256_ROUNDS + +AEADPERF_BRACKET = -DBRACKET_MODE=1 -DBRACKET_START=0 -DBRACKET_END=256 -DBRACKET_STEP=4 + +CFLAGS_aes128block_test = $(AES_OPTIONS) $(AES128_OPTIONS) +CFLAGS_aes128sched_test = $(AES_OPTIONS) $(AES128_OPTIONS) +CFLAGS_aes128gcm_test = $(AES_OPTIONS) $(AES128_OPTIONS) +CFLAGS_aes128eax_test = $(AES_OPTIONS) $(AES128_OPTIONS) +CFLAGS_aes128ccm_test = $(AES_OPTIONS) $(AES128_OPTIONS) +CFLAGS_poly1305_test = $(AES_OPTIONS) $(AES128_OPTIONS) + +CFLAGS_aeadperf_aes128gcm = $(AES_OPTIONS) $(AES128_OPTIONS) $(AEADPERF_BRACKET) +CFLAGS_aeadperf_aes128eax = $(AES_OPTIONS) $(AES128_OPTIONS) $(AEADPERF_BRACKET) +CFLAGS_aeadperf_aes128ccm = $(AES_OPTIONS) $(AES128_OPTIONS) $(AEADPERF_BRACKET) +CFLAGS_aeadperf_aes256gcm = $(AES_OPTIONS) $(AES256_OPTIONS) $(AEADPERF_BRACKET) +CFLAGS_aeadperf_aes256eax = $(AES_OPTIONS) $(AES256_OPTIONS) $(AEADPERF_BRACKET) +CFLAGS_aeadperf_aes256ccm = $(AES_OPTIONS) $(AES256_OPTIONS) $(AEADPERF_BRACKET) +CFLAGS_aeadperf_norx = $(AEADPERF_BRACKET) +CFLAGS_aeadperf_chacha20poly1305 = $(AEADPERF_BRACKET) + +CFLAGS_aes256block_test = $(AES_OPTIONS) $(AES256_OPTIONS) +CFLAGS_aes256sched_test = $(AES_OPTIONS) $(AES256_OPTIONS) + +CFLAGS_testaes = -DCF_SIDE_CHANNEL_PROTECTION=0 + +CFLAGS = -I./ext -I../ext -I.. -Os -ffunction-sections -g \ + -Wall -Werror -std=gnu99 -mthumb +LDFLAGS = -nostartfiles -nostdlib -Wl,-gc-sections +CURVESRCS = unacl/cortex_m0_mpy121666.s unacl/cortex_m0_reduce25519.s unacl/mul.s unacl/sqr.s +SRCS = boot.c memcpy.s memset.s semihost.c semihost.s \ + ../sha1.c ../sha256.c ../sha512.c ../sha3.c ../blockwise.c ../chash.c \ + ../curve25519.c ../poly1305.c \ + ../aes.c ../eax.c ../gcm.c ../cbcmac.c ../ccm.c \ + ../modes.c ../cmac.c ../gf128.c \ + ../hmac.c ../pbkdf2.c ../salsa20.c ../chacha20.c \ + ../norx.c ../chacha20poly1305.c ../drbg.c + +$(patsubst %,%.stm32f0.elf, $(FUNCS) $(AEADS)): $(SRCS) main.c $(CURVESRCS) +$(patsubst %,%.stm32f1.elf, $(FUNCS) $(AEADS)): $(SRCS) main.c $(CURVESRCS) +$(patsubst %,%.stm32f3.elf, $(FUNCS) $(AEADS)): $(SRCS) main.c $(CURVESRCS) +$(patsubst %,%.efm32.elf, $(FUNCS) $(AEADS)): $(SRCS) main.c $(CURVESRCS) +$(patsubst %,%.qemucm3.elf, $(FUNCS) $(AEADS)): $(SRCS) main.c $(CURVESRCS) + +$(patsubst %,testcurve25519.%.elf, $(ARCHS)): $(SRCS) $(CURVESRCS) ../testcurve25519.c +$(patsubst %,testaes.%.elf, $(ARCHS)): $(SRCS) ../testaes.c +$(patsubst %,testmodes.%.elf, $(ARCHS)): $(SRCS) ../testmodes.c +$(patsubst %,testsalsa20.%.elf, $(ARCHS)): $(SRCS) ../testsalsa20.c +$(patsubst %,testsha1.%.elf, $(ARCHS)): $(SRCS) ../testsha1.c +$(patsubst %,testsha2.%.elf, $(ARCHS)): $(SRCS) ../testsha2.c +$(patsubst %,testsha3.%.elf, $(ARCHS)): $(SRCS) ../testsha3.c +$(patsubst %,testpoly1305.%.elf, $(ARCHS)): $(SRCS) ../testpoly1305.c +$(patsubst %,testnorx.%.elf, $(ARCHS)): $(SRCS) ../testnorx.c +$(patsubst %,testchacha20poly1305.%.elf, $(ARCHS)): $(SRCS) ../testchacha20poly1305.c +$(patsubst %,testdrbg.%.elf, $(ARCHS)): $(SRCS) ../testdrbg.c + +run.%.qemucm3: %.qemucm3.bin + arm-none-eabi-readelf -l $(patsubst %.bin,%.elf,$^) > $@.log + qemu-system-gnuarmeclipse -verbose -verbose -M STM32-P103 -kernel $^ -semihosting -nographic -monitor null -serial null 2>> $@.log + cat $@.log + +run.%.efm32: %.efm32.elf + arm-none-eabi-readelf -l $^ > $@.log + echo '-----' >> $@.log + openocd -f openocd.efm32.cfg >> $@.log & + arm-none-eabi-gdb --quiet --batch-silent \ + $^ \ + -ex 'target remote :3333' \ + -ex 'monitor reset halt' \ + -ex 'load' \ + -ex 'monitor arm semihosting enable' \ + -ex 'monitor reset run' \ + -ex 'monitor wait_halt 720000' \ + -ex 'monitor shutdown' + +run.%.stm32f0: %.stm32f0.elf + arm-none-eabi-readelf -l $^ > $@.log + echo '-----' >> $@.log + openocd -f openocd.stm32f0.cfg >> $@.log & + arm-none-eabi-gdb --quiet --batch-silent \ + $^ \ + -ex 'target remote :3333' \ + -ex 'monitor reset halt' \ + -ex 'load' \ + -ex 'monitor arm semihosting enable' \ + -ex 'monitor reset run' \ + -ex 'monitor wait_halt 720000' \ + -ex 'monitor shutdown' + +run.%.stm32f1: %.stm32f1.elf + arm-none-eabi-readelf -l $^ > $@.log + echo '-----' >> $@.log + openocd -f openocd.stm32f1.cfg >> $@.log & + arm-none-eabi-gdb --quiet --batch-silent \ + $^ \ + -ex 'target remote :3333' \ + -ex 'monitor reset halt' \ + -ex 'load' \ + -ex 'monitor arm semihosting enable' \ + -ex 'monitor reset run' \ + -ex 'monitor wait_halt 720000' \ + -ex 'monitor shutdown' + +run.%.stm32f3: %.stm32f3.elf + arm-none-eabi-readelf -l $^ > $@.log + echo '-----' >> $@.log + openocd -f openocd.stm32f3.cfg >> $@.log & + arm-none-eabi-gdb --quiet --batch-silent \ + $^ \ + -ex 'target remote :3333' \ + -ex 'monitor reset halt' \ + -ex 'load' \ + -ex 'monitor arm semihosting enable' \ + -ex 'monitor reset run' \ + -ex 'monitor wait_halt 720000' \ + -ex 'monitor shutdown' + +test: $(patsubst %,run.%.qemucm3,$(FUNCS) $(TESTS)) +.PHONY: test + +perf.stm32f0: $(patsubst %,run.%.stm32f0,$(FUNCS)) +.PHONY: perf.stm32f0 + +test.stm32f0: $(patsubst %,run.%.stm32f0,$(FUNCS) $(TESTS)) +.PHONY: test.stm32f0 + +test.stm32f1: $(patsubst %,run.%.stm32f1,$(FUNCS) $(TESTS)) +.PHONY: test.stm32f1 + +test.stm32f3: $(patsubst %,run.%.stm32f3,$(FUNCS) $(TESTS)) +.PHONY: test.stm32f3 + +clean: + rm -rf *.log *.elf *.bin diff --git a/deps/picoquic/picotls/deps/cifra/src/arm/analyse.py b/deps/picoquic/picotls/deps/cifra/src/arm/analyse.py new file mode 100644 index 0000000..d2c456f --- /dev/null +++ b/deps/picoquic/picotls/deps/cifra/src/arm/analyse.py @@ -0,0 +1,207 @@ +import subprocess +import sys +import re + +function_intro_re = re.compile(r'^(?P[0-9a-fA-F]{8}) <(?P[a-zA-Z0-9\._]+)>:$') +insn_re = re.compile(r'^\s+(?P[0-9a-fA-F]+):\s+(?P[0-9a-fA-F ]+)\s+\t(?P.*)$') + +class Instruction: + def __init__(self, addr, insn, op): + self.addr = long(addr, 16) + self.insn = insn + + args = op.split('\t', 1) + + self.op = args[0].strip() + if len(args) == 2: + comment = args[1].strip().split(';', 1) + else: + comment = args + + self.args = comment[0].strip() + + if len(comment) == 2: + self.comment = comment[1].strip() + else: + self.comment = '' + + def __repr__(self): + return '' % (self.__dict__) + + +def literal_branch_target(t): + return ' <' in t + +class Function: + def __init__(self, addr, name): + self.name = name + self.addr = long(addr, 16) + self.insns = [] + self.calls = [] + + def __repr__(self): + return '<%s %d instructions>' % (self.name, len(self.insns)) + + def add_insn(self, insn): + self.insns.append(Instruction(**insn)) + + def contains_addr(self, addr): + if self.insns: + return addr >= self.addr and addr <= self.insns[-1].addr + else: + return addr == self.addr + + def dump(self): + print self.name + ':' + for insn in self.insns: + print ' ', '%04x' % insn.addr + ':', insn.op, insn.args, '\t;', insn.comment + + def get_literal_word(self, addr): + for insn in self.insns: + if insn.addr == addr and insn.op == '.word': + w = int(insn.args, 16) + if w & 0x80000000: + w = -(w ^ 0xffffffff) + 1 + return w + return None + + def analyse(self, prog): + self.stack_guess = None + regs = {} + + for insn in self.insns: + # stack adjustment with literal + if insn.op == 'sub' and insn.args.startswith('sp, ') and self.stack_guess is None: + sz = int(insn.args.split('#', 1)[1]) + self.stack_guess = sz + + # literal pool loads + if insn.op == 'ldr' and ', [pc, #' in insn.args: + reg, offset = insn.args.split(', [pc, #') + offset = int(offset.replace(']', '')) + word = self.get_literal_word(insn.addr + offset + 2) + if word is not None: + regs[reg] = word + + if insn.op == 'add' and insn.args.startswith('sp, r') and self.stack_guess is None: + reg = insn.args.split(', ')[1] + if reg in regs: + self.stack_guess = regs[reg] + + # static branches + if insn.op[0] == 'b' and literal_branch_target(insn.args): + target = long(insn.args.split(' <', 1)[0], 16) + + targetf = prog.function_at_addr(target) + + if targetf and targetf != self: + self.calls.append(targetf) + + if self.stack_guess is None: + self.stack_guess = 0 + + def stack_usage(self, hints, warns, prog, depth = 0): + hinted_calls = [] + if self.stack_guess: + print ' ' * depth, 'stack:', self.name, self.stack_guess, 'bytes' + + our_hints = [h for h in hints if h and h[0] == self.name] + if our_hints: + hints = [h[1:] for h in our_hints] + hinted_calls = [prog.function_by_name(h[0]) for h in hints if h] + else: + if self.name in warns: + print ' WARN: no calls hints for fn-ptr caller', self.name + + if self.calls + hinted_calls: + call_usage = max([f.stack_usage(hints, warns, prog, depth + 1) for f in self.calls + hinted_calls]) + else: + call_usage = 0 + return self.stack_guess + call_usage + +class Program: + def __init__(self): + self.functions = [] + + # sequence of tuples naming a call sequence known to occur + # this allows working out calls through pointers + self.call_hints = [] + + # function names to warn on if we don't have callees + self.call_warns = set() + + def read_elf(self, elf): + current_fn = None + + for x in subprocess.Popen(['arm-none-eabi-objdump', '-d', elf], + stdout = subprocess.PIPE).stdout: + x = x.rstrip('\n') + m = function_intro_re.match(x) + if m: + fn = Function(**m.groupdict()) + current_fn = fn + self.functions.append(fn) + + m = insn_re.match(x) + if m: + assert current_fn + current_fn.add_insn(m.groupdict()) + + def analyse(self): + for f in self.functions: + f.analyse(self) + + def function_by_name(self, name): + fns = [fn for fn in self.functions if fn.name == name] + if len(fns) == 0: + return None + elif len(fns) == 1: + return fns[0] + else: + print 'warn: more than one function named', name + return None + + def function_at_addr(self, addr): + for f in self.functions: + if f.addr == addr: + return f + return None + + def add_call_hint(self, *seq): + self.call_hints.append(seq) + + def add_call_warn(self, fn): + self.call_warns.add(fn) + + def measure_stack(self, name): + fn = self.function_by_name(name) + if fn is None: + return 0 + + return fn.stack_usage(self.call_hints, self.call_warns, self) + +_, exe, fn = sys.argv + +p = Program() +p.read_elf(exe) + +p.analyse() + +# calls which indirect through fn ptrs +p.add_call_warn('cf_blockwise_accumulate') +p.add_call_warn('cf_blockwise_accumulate_final') + +# hints to resolve those +p.add_call_hint('cf_sha224_update', 'cf_blockwise_accumulate', 'cf_blockwise_accumulate_final', 'sha256_update_block') +p.add_call_hint('cf_sha256_update', 'cf_blockwise_accumulate', 'cf_blockwise_accumulate_final', 'sha256_update_block') +p.add_call_hint('cf_sha384_update', 'cf_blockwise_accumulate', 'cf_blockwise_accumulate_final', 'sha512_update_block') +p.add_call_hint('cf_sha512_update', 'cf_blockwise_accumulate', 'cf_blockwise_accumulate_final', 'sha512_update_block') +p.add_call_hint('cf_norx32_encrypt', 'input', 'cf_blockwise_accumulate', 'cf_blockwise_accumulate_final', 'input_block') +p.add_call_hint('cf_norx32_decrypt', 'input', 'cf_blockwise_accumulate', 'cf_blockwise_accumulate_final', 'input_block') +p.add_call_hint('cf_cbcmac_stream_update', 'cf_blockwise_accumulate', 'cf_blockwise_accumulate_final', 'cbcmac_process') +p.add_call_hint('cf_cmac_stream_update', 'cf_blockwise_accumulate', 'cf_blockwise_accumulate_final', 'cmac_process_final_pad') +p.add_call_hint('cf_cmac_stream_update', 'cf_blockwise_accumulate_final', 'cmac_process') +p.add_call_hint('cf_cmac_stream_update', 'cf_blockwise_accumulate_final', 'cmac_process_final_nopad') + + +print 'stack', fn, '=', p.measure_stack(fn) diff --git a/deps/picoquic/picotls/deps/cifra/src/arm/boot.c b/deps/picoquic/picotls/deps/cifra/src/arm/boot.c new file mode 100644 index 0000000..d2a8e40 --- /dev/null +++ b/deps/picoquic/picotls/deps/cifra/src/arm/boot.c @@ -0,0 +1,144 @@ +#include +#include +#include + +extern int main(void); + +/* --- Defined by link script --- */ +extern uint32_t __etext; /* End of text/start of data. */ +extern uint32_t __data_start__, __data_end__; /* Data addresses in RAM */ +extern uint32_t __bss_start__, __bss_end__; /* BSS addresses in RAM */ +extern uint32_t __StackTop; /* End of stack in RAM */ + +#define ATTR_SECTION(sec) __attribute__ ((section (sec))) + +/* --- Interrupt vector table. --- */ +void Reset_Handler(void); +void SysTick_Handler(void); +void infinite_loop(void); +void do_nothing(void); + +typedef void (*vector_fn)(void); + +typedef struct { + uint32_t *stack_top; + vector_fn reset, nmi, hard_fault, mmu_fault, bus_fault, usage_fault; + vector_fn reserved0[4]; + vector_fn svc, debug_monitor; + vector_fn reserved1; + vector_fn pendsv, systick; + vector_fn irq[128]; +} vectors_t; + +#define COPY2(v) v, v +#define COPY4(v) COPY2(v), COPY2(v) +#define COPY8(v) COPY4(v), COPY4(v) +#define COPY16(v) COPY8(v), COPY8(v) +#define COPY32(v) COPY16(v), COPY16(v) +#define COPY64(v) COPY32(v), COPY32(v) +#define COPY128(v) COPY64(v), COPY64(v) + +vectors_t vectors ATTR_SECTION(".isr_vector") = { + .stack_top = &__StackTop, + .reset = Reset_Handler, + .nmi = do_nothing, + .hard_fault = infinite_loop, + .mmu_fault = infinite_loop, + .bus_fault = infinite_loop, + .usage_fault = infinite_loop, + .svc = do_nothing, + .debug_monitor = do_nothing, + .pendsv = do_nothing, + .systick = SysTick_Handler, + .irq = { COPY128(do_nothing) } +}; + +/* --- ISRs --- */ +void Reset_Handler(void) +{ + /* Copy data segment contents from flash to RAM. */ + uint32_t data_bytes = (&__data_end__ - &__data_start__) * 4; + memcpy(&__etext, &__data_start__, data_bytes); + + /* Zero BSS. */ + uint32_t bss_bytes = (&__bss_end__ - &__bss_start__) * 4; + memset(&__bss_start__, 0, bss_bytes); + + main(); + while (1) + ; +} + +void __assert_func(const char *file, int line, const char *func, const char *expr) +{ + while (1) + ; +} + +void infinite_loop(void) +{ + while (1) + ; +} + +void do_nothing(void) +{ +} + +uint32_t ticks = 0; + +void SysTick_Handler(void) +{ + ticks++; +} + +uint32_t get_ticks(void) +{ + return ticks; +} + +void reset_ticks(void) +{ + ticks = 0; +} + +void *memmove(void *vtarg, const void *vsrc, size_t len) +{ + if (vsrc > vtarg) + return memcpy(vtarg, vsrc, len); + else if (vsrc == vtarg) + return vtarg; + + uint8_t *targ = vtarg; + const uint8_t *src = vsrc; + + for (size_t i = len; i != 0; i++) + targ[i - 1] = src[i - 1]; + return vtarg; +} + +int memcmp(const void *va, const void *vb, size_t len) +{ + const uint8_t *a = va, *b = vb; + + for (size_t i = 0; i < len; i++) + { + if (a[i] != b[i]) + return a[i] < b[i] ? -1 : 1; + } + + return 0; +} + +size_t strlen(const char *c) +{ + size_t r = 0; + while (*c++) r++; + return r; +} + +void abort(void) +{ + while (1) + ; +} diff --git a/deps/picoquic/picotls/deps/cifra/src/arm/curve25519-results.txt b/deps/picoquic/picotls/deps/cifra/src/arm/curve25519-results.txt new file mode 100644 index 0000000..968e40e --- /dev/null +++ b/deps/picoquic/picotls/deps/cifra/src/arm/curve25519-results.txt @@ -0,0 +1,22 @@ +STM32F0 +donna-before-opt: 12907000c +donna-after-opt: 17294000c +donna-reset-opt: 12947000c +~20k + +donna -O2 -Os: 15268000c +donna -O2 -Os noasm: 20453000c +donna -Os: 15748000c +7.4k + +donna -O3: 12907000c 16KB 3380b +donna -Os: 15748000c 7.4KB 3148b +donna -O2: 15218000c 7.9KB 3148b + +tweetnacl -O2: 68876000c 3.0KB 2268b +tweetnacl -Os: 75979000c 2.8KB 2244b +tweetnacl -O3: 69622000c 8.9KB 2900b + +naclref -Os: 47813000c 3.2KB 4012b +naclref -O3: 35059000c 4.1KB 4044b +naclref -O2: 34309000c 3.5KB 4036b diff --git a/deps/picoquic/picotls/deps/cifra/src/arm/ext/cutest.h b/deps/picoquic/picotls/deps/cifra/src/arm/ext/cutest.h new file mode 100644 index 0000000..fa3c5d8 --- /dev/null +++ b/deps/picoquic/picotls/deps/cifra/src/arm/ext/cutest.h @@ -0,0 +1,55 @@ +/* cutest, for embedded targets. */ + +#ifndef CUTEST_H +#define CUTEST_H + +/* Main interface. */ +#define TEST_LIST const struct test__ test_list__[] +#define TEST_CHECK(cond) test_check__((cond), __FILE__, __LINE__, #cond) +/* no TEST_CHECK_ -- we don't have a good enough printf */ + +/* Implementation */ +#include "../semihost.h" + +struct test__ +{ + const char *name; + void (*func)(void); +}; + +extern const struct test__ test_list__[]; + +static void test_check__(int cond, const char *file, int line, const char *expr) +{ + if (cond) + return; /* pass */ + + emit("Failed!\n"); + emit("File: "); emit(file); emit("\n"); + emit("Line: "); emit_uint32(line); emit("\n"); + emit("Expr: "); emit(expr); emit("\n"); + quit_failure(); +} + +static void run_test__(const struct test__ *t) +{ + emit(" "); emit(t->name); emit(": "); + t->func(); + emit("OK\n"); +} + +int main(void) +{ + emit("Running tests:\n"); + + for (const struct test__ *t = test_list__; + t->name; + t++) + { + run_test__(t); + } + emit("Success\n"); + quit_success(); +} + +#endif diff --git a/deps/picoquic/picotls/deps/cifra/src/arm/linkscript.efm32.ld b/deps/picoquic/picotls/deps/cifra/src/arm/linkscript.efm32.ld new file mode 100644 index 0000000..8b9a6bf --- /dev/null +++ b/deps/picoquic/picotls/deps/cifra/src/arm/linkscript.efm32.ld @@ -0,0 +1,8 @@ +MEMORY +{ + FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 64K + RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 8K +} + +INCLUDE linkscript.std.ld + diff --git a/deps/picoquic/picotls/deps/cifra/src/arm/linkscript.lm3s6965evb.ld b/deps/picoquic/picotls/deps/cifra/src/arm/linkscript.lm3s6965evb.ld new file mode 100644 index 0000000..14fdac4 --- /dev/null +++ b/deps/picoquic/picotls/deps/cifra/src/arm/linkscript.lm3s6965evb.ld @@ -0,0 +1,7 @@ +MEMORY +{ + FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 256K + RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 64K +} + +INCLUDE linkscript.std.ld diff --git a/deps/picoquic/picotls/deps/cifra/src/arm/linkscript.qemucm3.ld b/deps/picoquic/picotls/deps/cifra/src/arm/linkscript.qemucm3.ld new file mode 100644 index 0000000..2826467 --- /dev/null +++ b/deps/picoquic/picotls/deps/cifra/src/arm/linkscript.qemucm3.ld @@ -0,0 +1,8 @@ +MEMORY +{ + FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 128K + RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 20K +} + +INCLUDE linkscript.std.ld + diff --git a/deps/picoquic/picotls/deps/cifra/src/arm/linkscript.std.ld b/deps/picoquic/picotls/deps/cifra/src/arm/linkscript.std.ld new file mode 100644 index 0000000..c08d7be --- /dev/null +++ b/deps/picoquic/picotls/deps/cifra/src/arm/linkscript.std.ld @@ -0,0 +1,172 @@ + +/* Linker script to place sections and symbol values. Should be used together + * with other linker script that defines memory regions FLASH and RAM. + * It references following symbols, which must be defined in code: + * Reset_Handler : Entry of reset handler + * + * It defines following symbols, which code can use without definition: + * __exidx_start + * __exidx_end + * __copy_table_start__ + * __copy_table_end__ + * __zero_table_start__ + * __zero_table_end__ + * __etext + * __data_start__ + * __preinit_array_start + * __preinit_array_end + * __init_array_start + * __init_array_end + * __fini_array_start + * __fini_array_end + * __data_end__ + * __bss_start__ + * __bss_end__ + * __end__ + * end + * __HeapLimit + * __StackLimit + * __StackTop + * __stack + */ +ENTRY(Reset_Handler) + +SECTIONS +{ + .text : + { + KEEP(*(.isr_vector)) + *(.text*) + + KEEP(*(.init)) + KEEP(*(.fini)) + + /* .ctors */ + *crtbegin.o(.ctors) + *crtbegin?.o(.ctors) + *(EXCLUDE_FILE(*crtend?.o *crtend.o) .ctors) + *(SORT(.ctors.*)) + *(.ctors) + + /* .dtors */ + *crtbegin.o(.dtors) + *crtbegin?.o(.dtors) + *(EXCLUDE_FILE(*crtend?.o *crtend.o) .dtors) + *(SORT(.dtors.*)) + *(.dtors) + + *(.rodata*) + + KEEP(*(.eh_frame*)) + } > FLASH + + .ARM.extab : + { + *(.ARM.extab* .gnu.linkonce.armextab.*) + } > FLASH + + __exidx_start = .; + .ARM.exidx : + { + *(.ARM.exidx* .gnu.linkonce.armexidx.*) + } > FLASH + __exidx_end = .; + + /* To copy multiple ROM to RAM sections, + * uncomment .copy.table section and, + * define __STARTUP_COPY_MULTIPLE in startup_ARMCMx.S */ + + .copy.table : + { + . = ALIGN(4); + __copy_table_start__ = .; + LONG (__etext) + LONG (__data_start__) + LONG (__data_end__ - __data_start__) + __copy_table_end__ = .; + } > FLASH + + + /* To clear multiple BSS sections, + * uncomment .zero.table section and, + * define __STARTUP_CLEAR_BSS_MULTIPLE in startup_ARMCMx.S */ + .zero.table : + { + . = ALIGN(4); + __zero_table_start__ = .; + LONG (__bss_start__) + LONG (__bss_end__ - __bss_start__) + __zero_table_end__ = .; + } > FLASH + + __etext = .; + + .data : AT (__etext) + { + __data_start__ = .; + *(vtable) + *(.data*) + + . = ALIGN(4); + /* preinit data */ + PROVIDE_HIDDEN (__preinit_array_start = .); + KEEP(*(.preinit_array)) + PROVIDE_HIDDEN (__preinit_array_end = .); + + . = ALIGN(4); + /* init data */ + PROVIDE_HIDDEN (__init_array_start = .); + KEEP(*(SORT(.init_array.*))) + KEEP(*(.init_array)) + PROVIDE_HIDDEN (__init_array_end = .); + + + . = ALIGN(4); + /* finit data */ + PROVIDE_HIDDEN (__fini_array_start = .); + KEEP(*(SORT(.fini_array.*))) + KEEP(*(.fini_array)) + PROVIDE_HIDDEN (__fini_array_end = .); + + KEEP(*(.jcr*)) + . = ALIGN(4); + /* All data end */ + __data_end__ = .; + + } > RAM + + .bss : + { + . = ALIGN(4); + __bss_start__ = .; + *(.bss*) + *(COMMON) + . = ALIGN(4); + __bss_end__ = .; + } > RAM + + .heap (COPY): + { + __end__ = .; + PROVIDE(end = .); + *(.heap*) + __HeapLimit = .; + } > RAM + + /* .stack_dummy section doesn't contains any symbols. It is only + * used for linker to calculate size of stack sections, and assign + * values to stack symbols later */ + .stack_dummy (COPY): + { + *(.stack*) + } > RAM + + /* Set stack top to end of RAM, and stack limit move down by + * size of stack_dummy section */ + __StackTop = ORIGIN(RAM) + LENGTH(RAM); + __StackLimit = __StackTop - SIZEOF(.stack_dummy); + PROVIDE(__stack = __StackTop); + + /* Check if data + heap + stack exceeds RAM limit */ + ASSERT(__StackLimit >= __HeapLimit, "region RAM overflowed with stack") +} diff --git a/deps/picoquic/picotls/deps/cifra/src/arm/linkscript.stm32f0.ld b/deps/picoquic/picotls/deps/cifra/src/arm/linkscript.stm32f0.ld new file mode 100644 index 0000000..c7a3bd8 --- /dev/null +++ b/deps/picoquic/picotls/deps/cifra/src/arm/linkscript.stm32f0.ld @@ -0,0 +1,8 @@ +MEMORY +{ + FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 128K + RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 16K +} + +INCLUDE linkscript.std.ld + diff --git a/deps/picoquic/picotls/deps/cifra/src/arm/linkscript.stm32f1.ld b/deps/picoquic/picotls/deps/cifra/src/arm/linkscript.stm32f1.ld new file mode 100644 index 0000000..d13f58d --- /dev/null +++ b/deps/picoquic/picotls/deps/cifra/src/arm/linkscript.stm32f1.ld @@ -0,0 +1,8 @@ +MEMORY +{ + FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 64K + RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 8K +} + +INCLUDE linkscript.std.ld + diff --git a/deps/picoquic/picotls/deps/cifra/src/arm/linkscript.stm32f3.ld b/deps/picoquic/picotls/deps/cifra/src/arm/linkscript.stm32f3.ld new file mode 100644 index 0000000..92eee46 --- /dev/null +++ b/deps/picoquic/picotls/deps/cifra/src/arm/linkscript.stm32f3.ld @@ -0,0 +1,8 @@ +MEMORY +{ + FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 64K + RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 12K +} + +INCLUDE linkscript.std.ld + diff --git a/deps/picoquic/picotls/deps/cifra/src/arm/main.c b/deps/picoquic/picotls/deps/cifra/src/arm/main.c new file mode 100644 index 0000000..5b7cbf2 --- /dev/null +++ b/deps/picoquic/picotls/deps/cifra/src/arm/main.c @@ -0,0 +1,447 @@ +#ifndef TEST +# error You must select a function to test. +#endif + +#include "semihost.h" +#include "aes.h" +#include "hmac.h" +#include "sha2.h" +#include "sha3.h" +#include "modes.h" +#include "salsa20.h" +#include "curve25519.h" +#include "poly1305.h" +#include "norx.h" +#include "chacha20poly1305.h" + +#include + +typedef void (*measure_fn)(void); +static uint32_t bracket; /* bracket mode parameter */ + +static void do_nothing(void) +{ +} + +static void stack_64w(void) +{ + volatile uint32_t words[64]; + words[0] = 0; + words[63] = 0; + (void) words[63]; +} + +static void stack_8w(void) +{ + volatile uint32_t words[8]; + words[0] = 0; + words[7] = 0; + (void) words[7]; +} + +static void hashtest_sha256(void) +{ + uint8_t hash[CF_SHA256_HASHSZ]; + cf_sha256_context ctx; + cf_sha256_init(&ctx); + cf_sha256_update(&ctx, "", 0); + cf_sha256_digest_final(&ctx, hash); +} + +static void hashtest_sha512(void) +{ + uint8_t hash[CF_SHA512_HASHSZ]; + cf_sha512_context ctx; + cf_sha512_init(&ctx); + cf_sha512_update(&ctx, "", 0); + cf_sha512_digest_final(&ctx, hash); +} + +static void hashtest_sha3_256(void) +{ + uint8_t hash[CF_SHA3_256_HASHSZ]; + cf_sha3_context ctx; + cf_sha3_256_init(&ctx); + cf_sha3_256_update(&ctx, "", 0); + cf_sha3_256_digest_final(&ctx, hash); +} + +static void hashtest_sha3_512(void) +{ + uint8_t hash[CF_SHA3_512_HASHSZ]; + cf_sha3_context ctx; + cf_sha3_512_init(&ctx); + cf_sha3_512_update(&ctx, "", 0); + cf_sha3_512_digest_final(&ctx, hash); +} + +static void aes128block_test(void) +{ + uint8_t key[16] = { 0 }, block[16] = { 0 }; + cf_aes_context ctx; + cf_aes_init(&ctx, key, sizeof key); + cf_aes_encrypt(&ctx, block, block); +} + +static void aes128sched_test(void) +{ + uint8_t key[16] = { 0 }; + cf_aes_context ctx; + cf_aes_init(&ctx, key, sizeof key); +} + +static void aes256block_test(void) +{ + uint8_t key[32] = { 0 }, block[16] = { 0 }; + cf_aes_context ctx; + cf_aes_init(&ctx, key, sizeof key); + cf_aes_encrypt(&ctx, block, block); +} + +static void aes256sched_test(void) +{ + uint8_t key[32] = { 0 }; + cf_aes_context ctx; + cf_aes_init(&ctx, key, sizeof key); +} + +static void aes128gcm_test(void) +{ + uint8_t key[16] = { 0 }; + cf_aes_context ctx; + cf_aes_init(&ctx, key, sizeof key); + + uint8_t msg[16] = { 0 }; + uint8_t aad[16] = { 0 }; + uint8_t nonce[12] = { 0 }; + uint8_t cipher[16] = { 0 }; + uint8_t tag[16] = { 0 }; + + cf_gcm_encrypt(&cf_aes, &ctx, + msg, sizeof msg, + aad, sizeof aad, + nonce, sizeof nonce, + cipher, + tag, sizeof tag); +} + +static void aes128eax_test(void) +{ + uint8_t key[16] = { 0 }; + cf_aes_context ctx; + cf_aes_init(&ctx, key, sizeof key); + + uint8_t msg[16] = { 0 }; + uint8_t aad[16] = { 0 }; + uint8_t nonce[12] = { 0 }; + uint8_t cipher[16] = { 0 }; + uint8_t tag[16] = { 0 }; + + cf_eax_encrypt(&cf_aes, &ctx, + msg, sizeof msg, + aad, sizeof aad, + nonce, sizeof nonce, + cipher, + tag, sizeof tag); +} + +static void aes128ccm_test(void) +{ + uint8_t key[16] = { 0 }; + cf_aes_context ctx; + cf_aes_init(&ctx, key, sizeof key); + + uint8_t msg[16] = { 0 }; + uint8_t aad[16] = { 0 }; + uint8_t nonce[11] = { 0 }; + uint8_t cipher[16] = { 0 }; + uint8_t tag[16] = { 0 }; + + cf_ccm_encrypt(&cf_aes, &ctx, + msg, sizeof msg, 4, + aad, sizeof aad, + nonce, sizeof nonce, + cipher, + tag, sizeof tag); +} + +static void salsa20_test(void) +{ + uint8_t key[32] = { 0 }; + uint8_t nonce[8] = { 0 }; + uint8_t msg[64] = { 0 }; + uint8_t cipher[64] = { 0 }; + + cf_salsa20_ctx ctx; + cf_salsa20_init(&ctx, key, sizeof key, nonce); + cf_salsa20_cipher(&ctx, msg, cipher, sizeof msg); +} + +static void chacha20_test(void) +{ + uint8_t key[32] = { 0 }; + uint8_t nonce[8] = { 0 }; + uint8_t msg[64] = { 0 }; + uint8_t cipher[64] = { 0 }; + + cf_chacha20_ctx ctx; + cf_chacha20_init(&ctx, key, sizeof key, nonce); + cf_chacha20_cipher(&ctx, msg, cipher, sizeof msg); +} + +static void curve25519_test(void) +{ + uint8_t secret[32] = { 1 }; + uint8_t pubkey[32]; + cf_curve25519_mul_base(pubkey, secret); +} + +static const uint8_t *mac_message = (const uint8_t *) "hello world"; +static const size_t mac_message_len = 11; + +static void poly1305_test(void) +{ + uint8_t key[32] = { 0 }, + nonce[16] = { 0 }, + encnonce[16], + mac[16]; + + cf_aes_context aes; + cf_aes_init(&aes, key, 16); + cf_aes_encrypt(&aes, nonce, encnonce); + + cf_poly1305 poly; + cf_poly1305_init(&poly, key + 16, encnonce); + cf_poly1305_update(&poly, mac_message, mac_message_len); + cf_poly1305_finish(&poly, mac); +} + +static void hmacsha256_test(void) +{ + uint8_t key[32] = { 0 }, + mac[32] = { 0 }; + + cf_hmac_ctx ctx; + cf_hmac_init(&ctx, &cf_sha256, key, sizeof key); + cf_hmac_update(&ctx, mac_message, mac_message_len); + cf_hmac_finish(&ctx, mac); +} + +static void norx_test(void) +{ + uint8_t key[16] = { 0 }; + uint8_t msg[16] = { 0 }; + uint8_t aad[16] = { 0 }; + uint8_t nonce[8] = { 0 }; + uint8_t cipher[16] = { 0 }; + uint8_t tag[16] = { 0 }; + + cf_norx32_encrypt(key, + nonce, + aad, sizeof aad, + msg, sizeof msg, + NULL, 0, + cipher, + tag); +} + +#ifndef BRACKET_MODE +# define AEADPERF_LEN 1 +#else +# define AEADPERF_LEN BRACKET_END +#endif + +static uint8_t aead_msg[AEADPERF_LEN] = { 0 }; +static uint8_t aead_cipher[AEADPERF_LEN] = { 0 }; +static uint8_t aead_aad[16] = { 0 }; +static uint8_t aead_key[32] = { 0 }; +static uint8_t aead_nonce[16] = { 0 }; +static uint8_t aead_tag[16] = { 0 }; + +static void aeadperf_norx(void) +{ + cf_norx32_encrypt(aead_key, aead_nonce, + aead_aad, sizeof aead_aad, + aead_msg, bracket, + NULL, 0, + aead_cipher, aead_tag); +} + +static void aeadperf_chacha20poly1305(void) +{ + cf_chacha20poly1305_encrypt(aead_key, aead_nonce, + aead_aad, sizeof aead_aad, + aead_msg, bracket, + aead_cipher, aead_tag); +} +static void aeadperf_aes128gcm(void) +{ + cf_aes_context ctx; + cf_aes_init(&ctx, aead_key, 16); + + cf_gcm_encrypt(&cf_aes, &ctx, + aead_msg, bracket, + aead_aad, sizeof aead_aad, + aead_nonce, 12, + aead_cipher, + aead_tag, 16); +} + +static void aeadperf_aes128ccm(void) +{ + cf_aes_context ctx; + cf_aes_init(&ctx, aead_key, 16); + + cf_ccm_encrypt(&cf_aes, &ctx, + aead_msg, bracket, + 4, + aead_aad, sizeof aead_aad, + aead_nonce, 11, + aead_cipher, + aead_tag, 16); +} + +static void aeadperf_aes128eax(void) +{ + cf_aes_context ctx; + cf_aes_init(&ctx, aead_key, 16); + + cf_eax_encrypt(&cf_aes, &ctx, + aead_msg, bracket, + aead_aad, sizeof aead_aad, + aead_nonce, 12, + aead_cipher, + aead_tag, 16); +} + +static void aeadperf_aes256gcm(void) +{ + cf_aes_context ctx; + cf_aes_init(&ctx, aead_key, 32); + + cf_gcm_encrypt(&cf_aes, &ctx, + aead_msg, bracket, + aead_aad, sizeof aead_aad, + aead_nonce, 12, + aead_cipher, + aead_tag, 16); +} + +static void aeadperf_aes256ccm(void) +{ + cf_aes_context ctx; + cf_aes_init(&ctx, aead_key, 32); + + cf_ccm_encrypt(&cf_aes, &ctx, + aead_msg, bracket, + 4, + aead_aad, sizeof aead_aad, + aead_nonce, 11, + aead_cipher, + aead_tag, 16); +} + +static void aeadperf_aes256eax(void) +{ + cf_aes_context ctx; + cf_aes_init(&ctx, aead_key, 32); + + cf_eax_encrypt(&cf_aes, &ctx, + aead_msg, bracket, + aead_aad, sizeof aead_aad, + aead_nonce, 12, + aead_cipher, + aead_tag, 16); +} + +/* Provided by linkscript */ +extern uint32_t __HeapLimit; + +#define STACK_MAGIC 0x57ac34df + +static __attribute__((noinline)) void clear_stack(void) +{ + uint32_t *stack_start = &__HeapLimit; + uint32_t ss = 0, *stack_stop = &ss; + size_t words = stack_stop - stack_start; + for (size_t i = 0; i < words; i++) + stack_start[i] = STACK_MAGIC; +} + +static __attribute__((noinline)) uint32_t measure_stack(void) +{ + uint32_t *stack_start = &__HeapLimit; + uint32_t ss, *stack_stop = &ss; + size_t words = stack_stop - stack_start; + for (size_t i = 0; i < words; i++) + if (stack_start[i] != STACK_MAGIC) + return words - i + 4; /* we used 4 words for ourselves, roughly */ + + return 0; +} + +static void measure(measure_fn fn) +{ + clear_stack(); + uint32_t start_cycles = reset_cycles(); + fn(); + uint32_t end_cycles = get_cycles(); + uint32_t stack_words = measure_stack(); + + emit("cycles = "); + emit_uint32(end_cycles - start_cycles); + emit("\n"); + emit("stack = "); + emit_uint32(stack_words << 2); + emit("\n"); +} + +#define STRING_(x) #x +#define STRING(x) STRING_(x) + +int main(void) +{ + emit(STRING(TEST) "\n"); +#ifdef BRACKET_MODE + for (bracket = BRACKET_START; bracket <= BRACKET_END; bracket += BRACKET_STEP) + { + emit("bracket = "); + emit_uint32(bracket); + emit("\n"); + measure(TEST); + } +#else + measure(TEST); +#endif + quit_success(); + + (void) bracket; + (void) do_nothing; + (void) stack_8w; + (void) stack_64w; + (void) hashtest_sha256; + (void) hashtest_sha512; + (void) hashtest_sha3_256; + (void) hashtest_sha3_512; + (void) aes128block_test; + (void) aes128sched_test; + (void) aes256block_test; + (void) aes256sched_test; + (void) aes128gcm_test; + (void) aes128eax_test; + (void) aes128ccm_test; + (void) salsa20_test; + (void) chacha20_test; + (void) curve25519_test; + (void) poly1305_test; + (void) hmacsha256_test; + (void) norx_test; + (void) aeadperf_norx; + (void) aeadperf_chacha20poly1305; + (void) aeadperf_aes128gcm; + (void) aeadperf_aes128ccm; + (void) aeadperf_aes128eax; + (void) aeadperf_aes256gcm; + (void) aeadperf_aes256ccm; + (void) aeadperf_aes256eax; +} diff --git a/deps/picoquic/picotls/deps/cifra/src/arm/memcpy.s b/deps/picoquic/picotls/deps/cifra/src/arm/memcpy.s new file mode 100644 index 0000000..63406fe --- /dev/null +++ b/deps/picoquic/picotls/deps/cifra/src/arm/memcpy.s @@ -0,0 +1,49 @@ + .text + .syntax unified + .global memcpy + .func memcpy + .thumb_func + +memcpy: + /* on entry + * r0 = targ + * r1 = src + * r2 = len (bytes) + * on exit + * r0 = targ (unchanged) + */ + push {r0, r4, lr} + + /* If targ or src are unaligned, drop to byte + * processing. */ + mov r3, r0 + movs r4, #3 + orrs r3, r1 + ands r3, r4 + bne L_bytewise + + /* Process words */ +L_wordwise: + cmp r2, #4 + blo L_bytewise + ldr r4, [r1] + adds r1, #4 + str r4, [r0] + adds r0, #4 + subs r2, #4 + b L_wordwise + + /* Process bytes */ +L_bytewise: + cmp r2, #0 + beq L_fin + ldrb r4, [r1] + adds r1, #1 + strb r4, [r0] + adds r0, #1 + subs r2, #1 + b L_bytewise + +L_fin: + pop {r0, r4, pc} + .endfunc diff --git a/deps/picoquic/picotls/deps/cifra/src/arm/memset.s b/deps/picoquic/picotls/deps/cifra/src/arm/memset.s new file mode 100644 index 0000000..a501966 --- /dev/null +++ b/deps/picoquic/picotls/deps/cifra/src/arm/memset.s @@ -0,0 +1,50 @@ + .text + .syntax unified + .global memset + .func memset + .thumb_func + +memset: + /* on entry + * r0 = targ + * r1 = value + * r2 = len (bytes) + * on exit + * r0 = targ (unchanged) + */ + push {r0, r4, lr} + + /* If targ is unaligned, drop to byte + * processing. */ + movs r3, #3 + ands r3, r0 + bne L_bytewise + + /* Process words */ + /* Build r4 by repeating r1. */ + uxtb r4, r1 + lsls r3, r4, #8 + orrs r4, r3 + lsls r3, r4, #16 + orrs r4, r3 + +L_wordwise: + cmp r2, #4 + blo L_bytewise + str r4, [r0] + adds r0, #4 + subs r2, #4 + b L_wordwise + + /* Process bytes */ +L_bytewise: + cmp r2, #0 + beq L_fin + strb r1, [r0] + adds r0, #1 + subs r2, #1 + b L_bytewise + +L_fin: + pop {r0, r4, pc} + .endfunc diff --git a/deps/picoquic/picotls/deps/cifra/src/arm/merge.py b/deps/picoquic/picotls/deps/cifra/src/arm/merge.py new file mode 100644 index 0000000..71d5089 --- /dev/null +++ b/deps/picoquic/picotls/deps/cifra/src/arm/merge.py @@ -0,0 +1,26 @@ +import sys + +def extract_results(results): + index = 0 + while index < len(results): + if results[index].startswith('## '): + end = results.index('\n', index) + yield results[index:end] + index += 1 + +def merge(readme, res): + title, table = res[0], res[1:] + assert title in readme, 'Section ' + title + ' missing from README.md' + secindex = readme.index(title) + hdrindex = [i for i in range(secindex, len(readme)) if readme[i].startswith('---------- | ')][0] + start = hdrindex - 1 + end = readme.index('\n', start) + table = [t.rstrip() + '\n' for t in table] + return readme[:start] + table + readme[end:] + +results = sys.stdin.readlines() +readme = open('../../README.md').readlines() + +for res in extract_results(results): + readme = merge(readme, res) +print ''.join(readme).rstrip() diff --git a/deps/picoquic/picotls/deps/cifra/src/arm/openocd.efm32.cfg b/deps/picoquic/picotls/deps/cifra/src/arm/openocd.efm32.cfg new file mode 100644 index 0000000..85af473 --- /dev/null +++ b/deps/picoquic/picotls/deps/cifra/src/arm/openocd.efm32.cfg @@ -0,0 +1,3 @@ +source [find interface/jlink.cfg] +transport select swd +source [find target/efm32.cfg] diff --git a/deps/picoquic/picotls/deps/cifra/src/arm/openocd.stm32f0.cfg b/deps/picoquic/picotls/deps/cifra/src/arm/openocd.stm32f0.cfg new file mode 100644 index 0000000..e9356f7 --- /dev/null +++ b/deps/picoquic/picotls/deps/cifra/src/arm/openocd.stm32f0.cfg @@ -0,0 +1,3 @@ +source [find interface/stlink-v2.cfg] +transport select hla_swd +source [find target/stm32f0x.cfg] diff --git a/deps/picoquic/picotls/deps/cifra/src/arm/openocd.stm32f1.cfg b/deps/picoquic/picotls/deps/cifra/src/arm/openocd.stm32f1.cfg new file mode 100644 index 0000000..1108ea0 --- /dev/null +++ b/deps/picoquic/picotls/deps/cifra/src/arm/openocd.stm32f1.cfg @@ -0,0 +1,3 @@ +source [find interface/stlink-v2.cfg] +transport select hla_swd +source [find target/stm32f1x.cfg] diff --git a/deps/picoquic/picotls/deps/cifra/src/arm/openocd.stm32f3.cfg b/deps/picoquic/picotls/deps/cifra/src/arm/openocd.stm32f3.cfg new file mode 100644 index 0000000..de023b8 --- /dev/null +++ b/deps/picoquic/picotls/deps/cifra/src/arm/openocd.stm32f3.cfg @@ -0,0 +1,3 @@ +source [find interface/stlink-v2.cfg] +transport select hla_swd +source [find target/stm32f3x.cfg] diff --git a/deps/picoquic/picotls/deps/cifra/src/arm/report.py b/deps/picoquic/picotls/deps/cifra/src/arm/report.py new file mode 100644 index 0000000..718ab24 --- /dev/null +++ b/deps/picoquic/picotls/deps/cifra/src/arm/report.py @@ -0,0 +1,276 @@ +""" +Interprets logs from test runs. Outputs ASCII +tables containing results, json data, etc. +""" + +import json +import sys + +archs = 'stm32f0 stm32f1 stm32f3'.split() +tests = """ +aes128block_test +aes256block_test +aes128sched_test +aes256sched_test +hashtest_sha256 +hashtest_sha512 +hashtest_sha3_256 +hashtest_sha3_512 +aes128gcm_test +aes128eax_test +aes128ccm_test +norx_test +salsa20_test +chacha20_test +poly1305_test +hmacsha256_test +curve25519_test +aeadperf_norx +aeadperf_aes128gcm +aeadperf_aes128eax +aeadperf_aes128ccm +aeadperf_aes256gcm +aeadperf_aes256eax +aeadperf_aes256ccm +aeadperf_chacha20poly1305 +do_nothing +""".split() + +arch_names = dict( + stm32f0 = 'Cortex-M0', + stm32f1 = 'Cortex-M3', + stm32f3 = 'Cortex-M4F' + ) + +base_test = 'do_nothing' + +def extract(arch, test): + fn = 'run.%s.%s.log' % (test, arch) + + code_size = 0 + data_size = 0 + cycle_count = None + stack_usage = None + brackets = None + current_bracket = None + + try: + lines = open(fn).readlines() + except IOError: + return None + + for l in lines: + if 'LOAD' in l: + parts = l.split() + assert len(parts) >= 8 + assert 'LOAD' == parts[0] + if parts[6] == 'RWE': + code_size += long(parts[5], 16) + if parts[6] == 'RW': + data_size += long(parts[5], 16) + + if l.startswith('bracket = '): + bracket = long(l.split(' = ')[1].strip(), 16) + current_bracket = bracket + if brackets is None: + brackets = {} + brackets[current_bracket] = dict() + + if l.startswith('cycles = '): + cycle_count = long(l.split(' = ')[1].strip(), 16) + if current_bracket is not None: + brackets[current_bracket]['cycle_count'] = cycle_count + + if l.startswith('stack = '): + stack_usage = long(l.split(' = ')[1].strip(), 16) + if current_bracket is not None: + brackets[current_bracket]['stack_usage'] = stack_usage + + return dict( + code_size = code_size, + data_size = data_size, + cycle_count = cycle_count, + stack_usage = stack_usage, + brackets = brackets + ) + +def print_table(rows): + header, rows = rows[0], rows[1:] + assert not [True for r in rows if len(r) != len(header)] + widths = [] + for i, h in enumerate(header): + widths.append(max([len(h)] + [len(r[i]) for r in rows])) + + def print_row(row): + print ' | '.join(c + (' ' * (widths[i] - len(c))) for i, c in enumerate(row)) + + print_row(header) + print_row(['-' * w for w in widths]) + for r in rows: + print_row(r) + +results = {} + +for arch in archs: + for test in tests: + inf = extract(arch, test) + if inf: + results.setdefault(arch, {})[test] = inf + +for arch in results.keys(): + if base_test not in results[arch]: + print 'need', base_test, 'results to report for', arch + continue + + base_result = results[arch][base_test] + + for test in results[arch].keys(): + if test == base_test: + continue + + results[arch][test]['code_size'] -= base_result['code_size'] + +def tabulate_aes(arch, block_result, sched_result, table = None): + if table is None: + table = [] + table.append(( + 'Core', + 'Cycles (key schedule + block)', + 'Cycles (key schedule)', + 'Cycles (block)', + 'Stack', + 'Code size' + )) + + table.append( + ( + arch_names[arch], + '%d' % block_result['cycle_count'], + '%d' % sched_result['cycle_count'], + '%d' % (block_result['cycle_count'] - sched_result['cycle_count']), + '%dB' % block_result['stack_usage'], + '%dB' % block_result['code_size'] + )) + + return table + +def print_std(result): + print """* **Cycles**: %(cycle_count)d +* **Stack**: %(stack_usage)dB +* **Code size**: %(code_size)dB +""" % result + +def tabulate_std(arch, result, table = None): + if table is None: + table = [] + table.append(('Core', 'Cycles', 'Stack', 'Code size')) + + table.append( + ( + arch_names[arch], + '%d' % result['cycle_count'], + '%dB' % result['stack_usage'], + '%dB' % result['code_size'] + )) + + return table + +def tabulate(mktab): + table = None + for arch in archs: + if arch not in results: + continue + table = mktab(arch, table) + print_table(table) + +def convert_brackets(metric, tests): + for arch in archs: + arch_result = {} + + # collect results for each test + for t in tests: + if arch not in results or t not in results[arch]: + print 'missing', arch, t + continue + data = results[arch][t]['brackets'] + arch_result[t] = [[b, data[b][metric]] for b in sorted(data.keys())] + + # convert into list of [bracket, test-1, test-2, ...] lists + out = [] + if len(arch_result) == 0: + continue + first_row = arch_result.values()[0] + + for i in range(len(first_row)): + row = [ first_row[i][0] ] + + for k in sorted(arch_result.keys()): + if len(arch_result[k]) != len(first_row): + print 'warn:', 'test', k, 'did not complete?' + rr = arch_result[k][i] + row.append(rr[1]) + + out.append(row) + + print json.dumps(out) + +convert_brackets('cycle_count', + [ + 'aeadperf_norx', + 'aeadperf_aes128gcm', + 'aeadperf_aes128eax', + 'aeadperf_aes128ccm', + 'aeadperf_aes256gcm', + 'aeadperf_aes256eax', + 'aeadperf_aes256ccm', + 'aeadperf_chacha20poly1305' + ]) +convert_brackets('stack_usage', + [ + 'aeadperf_norx', + 'aeadperf_aes128gcm', + 'aeadperf_aes128eax', + 'aeadperf_aes128ccm', + 'aeadperf_aes256gcm', + 'aeadperf_aes256eax', + 'aeadperf_aes256ccm', + 'aeadperf_chacha20poly1305' + ]) + +# screwed if we need other block ciphers +print '###', '128-bit key' +tabulate(lambda arch, table: tabulate_aes(arch, results[arch]['aes128block_test'], results[arch]['aes128sched_test'], table)) +print + +print '###', '256-bit key' +tabulate(lambda arch, table: tabulate_aes(arch, results[arch]['aes256block_test'], results[arch]['aes256sched_test'], table)) +print + +def do_table(title, test): + print '##', title + tabulate(lambda arch, table: tabulate_std(arch, results[arch][test], table)) + print + +do_table('AES128-GCM', 'aes128gcm_test') +do_table('AES128-EAX', 'aes128eax_test') +do_table('AES128-CCM', 'aes128ccm_test') +do_table('NORX32', 'norx_test') +do_table('ChaCha20', 'chacha20_test') +do_table('Salsa20', 'salsa20_test') +do_table('SHA256', 'hashtest_sha256') +do_table('SHA512', 'hashtest_sha512') +do_table('SHA3-256', 'hashtest_sha3_256') +do_table('SHA3-512', 'hashtest_sha3_512') +do_table('HMAC-SHA256', 'hmacsha256_test') +do_table('Poly1305-AES', 'poly1305_test') +do_table('Curve25519', 'curve25519_test') + +if '--aead' in sys.argv: + do_table('AEAD-Shootout: NORX', 'aeadperf_norx') + do_table('AEAD-Shootout: AES-128-GCM', 'aeadperf_aes128gcm') + do_table('AEAD-Shootout: AES-128-EAX', 'aeadperf_aes128eax') + do_table('AEAD-Shootout: AES-128-CCM', 'aeadperf_aes128ccm') + do_table('AEAD-Shootout: AES-256-GCM', 'aeadperf_aes256gcm') + do_table('AEAD-Shootout: AES-256-EAX', 'aeadperf_aes256eax') + do_table('AEAD-Shootout: AES-256-CCM', 'aeadperf_aes256ccm') + do_table('AEAD-Shootout: ChaCha20-Poly1305', 'aeadperf_chacha20poly1305') diff --git a/deps/picoquic/picotls/deps/cifra/src/arm/semihost.c b/deps/picoquic/picotls/deps/cifra/src/arm/semihost.c new file mode 100644 index 0000000..cbe5aa2 --- /dev/null +++ b/deps/picoquic/picotls/deps/cifra/src/arm/semihost.c @@ -0,0 +1,170 @@ +#include +#include +#include +#include + +#include "semihost.h" + +#define OP_WRITE0 0x04 +#define OP_EXIT 0x18 +#define OP_EXIT_ARG_FAILURE 0x0 +#define OP_EXIT_ARG_SUCCESS 0x20026 + +extern uint32_t semihost(uint32_t, volatile void *); + +__attribute__((noreturn)) +void quit_success(void) +{ + semihost(OP_EXIT, (void *) OP_EXIT_ARG_SUCCESS); + while (1) + ; +} + +__attribute__((noreturn)) +void quit_failure(void) +{ + semihost(OP_EXIT, (void *) OP_EXIT_ARG_FAILURE); + while (1) + ; +} + +void emit(const char *buf) +{ + semihost(OP_WRITE0, (volatile void *) buf); +} + +static void emit_extent(const char *start, const char *end) +{ + char buf[32+1]; + size_t bufmax = sizeof(buf) - 1; + buf[32] = 0; + + size_t bytes = end - start + 1; + + while (bytes >= bufmax) + { + memcpy(buf, start, bufmax); + emit(buf); + bytes -= bufmax; + start += bufmax; + } + + if (bytes == 0) + return; + + memcpy(buf, start, bytes); + buf[bytes] = 0; + emit(buf); +} + +void emitf(const char *fmt, ...) +{ + const char *start = fmt, *end = fmt; + + va_list args; + va_start(args, fmt); + + while (*fmt) + { + switch (*fmt) + { + case '%': + emit_extent(start, end); + + switch (fmt[1]) + { + case '%': + emit("%"); + break; + + case 'u': + emit_uint32(va_arg(args, uint32_t)); + break; + + case 's': + emit(va_arg(args, const char *)); + break; + } + start = end = fmt + 2; + break; + + default: + end = fmt; + break; + } + + fmt++; + } + + va_end(args); + emit_extent(start, end); +} + +static const char *hex_chars = "0123456789abcdef"; + +void emit_hex(const void *ptr, size_t len) +{ + const uint8_t *bb = ptr; + char byte[3]; + + byte[2] = 0; + + for (size_t i = 0; i < len; i++) + { + byte[0] = hex_chars[(bb[i] >> 4) & 0xf]; + byte[1] = hex_chars[bb[i] & 0xf]; + emit(byte); + } +} + +void emit_uint32(uint32_t x) +{ + char buf[sizeof "0x11223344"]; + buf[0] = '0'; + buf[1] = 'x'; + buf[2] = hex_chars[(x >> 28) & 0xf]; + buf[3] = hex_chars[(x >> 24) & 0xf]; + buf[4] = hex_chars[(x >> 20) & 0xf]; + buf[5] = hex_chars[(x >> 16) & 0xf]; + buf[6] = hex_chars[(x >> 12) & 0xf]; + buf[7] = hex_chars[(x >> 8) & 0xf]; + buf[8] = hex_chars[(x >> 4) & 0xf]; + buf[9] = hex_chars[x & 0xf]; + buf[10] = 0; + + emit(buf); +} + +typedef struct +{ + volatile uint32_t ctrl; + volatile uint32_t reload; + volatile uint32_t current; +} systick; + +#define SysTick ((systick *)0xe000e010) + +#define STCTRL_SYSCLOCK 0x04 +#define STCTRL_TICKINT 0x02 +#define STCTRL_ENABLE 0x01 + +#define STCTRL_MAX 0xffffff +#define STCTRL_SHIFT 24 + +extern uint32_t get_ticks(void); +extern void reset_ticks(void); + +uint32_t reset_cycles(void) +{ + SysTick->reload = STCTRL_MAX; + SysTick->ctrl = STCTRL_SYSCLOCK | STCTRL_TICKINT | STCTRL_ENABLE; + SysTick->current = 0; + reset_ticks(); + return get_ticks(); +} + +uint32_t get_cycles(void) +{ + return (get_ticks() << STCTRL_SHIFT) + (STCTRL_MAX - SysTick->current); +} + diff --git a/deps/picoquic/picotls/deps/cifra/src/arm/semihost.h b/deps/picoquic/picotls/deps/cifra/src/arm/semihost.h new file mode 100644 index 0000000..cf6f01a --- /dev/null +++ b/deps/picoquic/picotls/deps/cifra/src/arm/semihost.h @@ -0,0 +1,40 @@ +#ifndef SEMIHOST_H +#define SEMIHOST_H + +#include +#include + +/* Exits emulator with success (or merely hangs). */ +__attribute__((noreturn)) +void quit_success(void); + +/* Exits emulator with failure (or merely hangs). */ +__attribute__((noreturn)) +void quit_failure(void); + +/* Writes zero terminated string to debug output */ +void emit(const char *buf); + +/* Writes a formatting string to debug output. + * + * Supported: + * %u - uint32_t argument, same as emit_uint32 + * %s - const char * argument, same as emit + */ +void emitf(const char *fmt, ...); + +/* Writes hex dump of len bytes at ptr to debug output. */ +void emit_hex(const void *ptr, size_t len); + +/* Writes value v in hex to debug output, in format: + * 0xHHHHHHHH (equivalent to printf 0x%08x). */ +void emit_uint32(uint32_t v); + +/* Reset cycle counter to 0. Returns the current value + * (just after resetting it). */ +uint32_t reset_cycles(void); + +/* Return the value of the cycle counter. */ +uint32_t get_cycles(void); + +#endif diff --git a/deps/picoquic/picotls/deps/cifra/src/arm/semihost.s b/deps/picoquic/picotls/deps/cifra/src/arm/semihost.s new file mode 100644 index 0000000..0fddf04 --- /dev/null +++ b/deps/picoquic/picotls/deps/cifra/src/arm/semihost.s @@ -0,0 +1,15 @@ + .text + .syntax unified + .global semihost + .func semihost + .thumb_func + +semihost: + /* on entry + * r0 = op + * r1 = arg */ + push {r7, lr} + bkpt 0xab + pop {r7, pc} + + .endfunc diff --git a/deps/picoquic/picotls/deps/cifra/src/arm/unacl/cortex_m0_mpy121666.s b/deps/picoquic/picotls/deps/cifra/src/arm/unacl/cortex_m0_mpy121666.s new file mode 100644 index 0000000..49e3b5d --- /dev/null +++ b/deps/picoquic/picotls/deps/cifra/src/arm/unacl/cortex_m0_mpy121666.s @@ -0,0 +1,199 @@ +// Implementation of multiplication of an fe25519 bit value with the curve constant 121666. +// +// B. Haase, Endress + Hauser Conducta GmbH & Ko. KG +// public domain. +// +// gnu assembler format. +// +// Generated and tested with C++ functions in the test subdirectory. +// +// ATTENTION: +// Not yet tested on target hardware. + + + .cpu cortex-m0 + .fpu softvfp + .eabi_attribute 20, 1 + .eabi_attribute 21, 1 + .eabi_attribute 23, 3 + .eabi_attribute 24, 1 + .eabi_attribute 25, 1 + .eabi_attribute 26, 1 + .eabi_attribute 30, 2 + .eabi_attribute 34, 0 + .eabi_attribute 18, 4 + .code 16 + + .file "cortex_m0_reduce25519.s" + + .text + .align 2 + + .global fe25519_mpyWith121666_asm + .code 16 + .thumb_func + .type fe25519_mpyWith121666_asm, %function + +fe25519_mpyWith121666_asm: + push {r4,r5,r6,r7,r14} + ldr r7,__label_for_immediate_56130 + ldr r2,[r1,#28] + lsl r5,r2,#16 + lsr r6,r2,#16 + lsr r3,r2,#16 + uxth r2,r2 + mul r2,r7 + mul r3,r7 + add r5,r2 + mov r2,#0 + adc r6,r2 + lsl r2,r3,#16 + lsr r3,r3,#16 + add r5,r2 + adc r6,r3 + lsl r2,r5,#1 + lsr r2,r2,#1 + str r2,[r0,#28] + lsr r5,r5,#31 + lsl r6,r6,#1 + orr r5,r6 + mov r6,#19 + mul r5,r6 + mov r6,#0 + ldr r2,[r1,#0] + lsl r3,r2,#16 + lsr r4,r2,#16 + add r5,r3 + adc r6,r4 + lsr r3,r2,#16 + uxth r2,r2 + mul r2,r7 + mul r3,r7 + add r5,r2 + mov r2,#0 + adc r6,r2 + lsl r2,r3,#16 + lsr r3,r3,#16 + add r5,r2 + adc r6,r3 + str r5,[r0,#0] + mov r5,#0 + ldr r2,[r1,#4] + lsl r3,r2,#16 + lsr r4,r2,#16 + add r6,r3 + adc r5,r4 + lsr r3,r2,#16 + uxth r2,r2 + mul r2,r7 + mul r3,r7 + add r6,r2 + mov r2,#0 + adc r5,r2 + lsl r2,r3,#16 + lsr r3,r3,#16 + add r6,r2 + adc r5,r3 + str r6,[r0,#4] + mov r6,#0 + ldr r2,[r1,#8] + lsl r3,r2,#16 + lsr r4,r2,#16 + add r5,r3 + adc r6,r4 + lsr r3,r2,#16 + uxth r2,r2 + mul r2,r7 + mul r3,r7 + add r5,r2 + mov r2,#0 + adc r6,r2 + lsl r2,r3,#16 + lsr r3,r3,#16 + add r5,r2 + adc r6,r3 + str r5,[r0,#8] + mov r5,#0 + ldr r2,[r1,#12] + lsl r3,r2,#16 + lsr r4,r2,#16 + add r6,r3 + adc r5,r4 + lsr r3,r2,#16 + uxth r2,r2 + mul r2,r7 + mul r3,r7 + add r6,r2 + mov r2,#0 + adc r5,r2 + lsl r2,r3,#16 + lsr r3,r3,#16 + add r6,r2 + adc r5,r3 + str r6,[r0,#12] + mov r6,#0 + ldr r2,[r1,#16] + lsl r3,r2,#16 + lsr r4,r2,#16 + add r5,r3 + adc r6,r4 + lsr r3,r2,#16 + uxth r2,r2 + mul r2,r7 + mul r3,r7 + add r5,r2 + mov r2,#0 + adc r6,r2 + lsl r2,r3,#16 + lsr r3,r3,#16 + add r5,r2 + adc r6,r3 + str r5,[r0,#16] + mov r5,#0 + ldr r2,[r1,#20] + lsl r3,r2,#16 + lsr r4,r2,#16 + add r6,r3 + adc r5,r4 + lsr r3,r2,#16 + uxth r2,r2 + mul r2,r7 + mul r3,r7 + add r6,r2 + mov r2,#0 + adc r5,r2 + lsl r2,r3,#16 + lsr r3,r3,#16 + add r6,r2 + adc r5,r3 + str r6,[r0,#20] + mov r6,#0 + ldr r2,[r1,#24] + lsl r3,r2,#16 + lsr r4,r2,#16 + add r5,r3 + adc r6,r4 + lsr r3,r2,#16 + uxth r2,r2 + mul r2,r7 + mul r3,r7 + add r5,r2 + mov r2,#0 + adc r6,r2 + lsl r2,r3,#16 + lsr r3,r3,#16 + add r5,r2 + adc r6,r3 + str r5,[r0,#24] + mov r5,#0 + ldr r2,[r0,#28] + add r6,r2 + str r6,[r0,#28] + pop {r4,r5,r6,r7,r15} + + .align 2 +__label_for_immediate_56130: + .word 56130 + + .size fe25519_mpyWith121666_asm, .-fe25519_mpyWith121666_asm + diff --git a/deps/picoquic/picotls/deps/cifra/src/arm/unacl/cortex_m0_reduce25519.s b/deps/picoquic/picotls/deps/cifra/src/arm/unacl/cortex_m0_reduce25519.s new file mode 100644 index 0000000..4c09f5e --- /dev/null +++ b/deps/picoquic/picotls/deps/cifra/src/arm/unacl/cortex_m0_reduce25519.s @@ -0,0 +1,176 @@ +// Implementation of a partial reduction modulo 2^255 - 38. +// +// B. Haase, Endress + Hauser Conducta GmbH & Ko. KG +// public domain. +// +// gnu assembler format. +// +// Generated and tested with C++ functions in the test subdirectory and on the target. +// + + .cpu cortex-m0 + .fpu softvfp + .eabi_attribute 20, 1 + .eabi_attribute 21, 1 + .eabi_attribute 23, 3 + .eabi_attribute 24, 1 + .eabi_attribute 25, 1 + .eabi_attribute 26, 1 + .eabi_attribute 30, 2 + .eabi_attribute 34, 0 + .eabi_attribute 18, 4 + .code 16 + + .file "cortex_m0_reduce25519.s" + + .text + .align 2 + + .global fe25519_reduceTo256Bits_asm + .code 16 + .thumb_func + .type fe25519_reduceTo256Bits_asm, %function + +fe25519_reduceTo256Bits_asm: + push {r4,r5,r6,r7,r14} + ldr r2,[r1,#60] + lsr r3,r2,#16 + uxth r2,r2 + mov r7,#38 + mul r2,r7 + mul r3,r7 + ldr r4,[r1,#28] + lsr r5,r3,#16 + lsl r3,r3,#16 + mov r6,#0 + add r4,r2 + adc r5,r6 + add r4,r3 + adc r5,r6 + lsl r2,r4,#1 + lsr r2,r2,#1 + str r2,[r0,#28] + lsr r4,r4,#31 + lsl r5,r5,#1 + orr r4,r5 + mov r2,#19 + mul r2,r4 + ldr r4,[r1,#0] + add r2,r4 + mov r3,#0 + adc r3,r6 + ldr r4,[r1,#32] + lsr r5,r4,#16 + uxth r4,r4 + mul r5,r7 + mul r4,r7 + add r2,r4 + adc r3,r6 + lsl r4,r5,#16 + lsr r5,r5,#16 + add r2,r4 + adc r3,r5 + str r2,[r0,#0] + ldr r4,[r1,#4] + add r3,r4 + mov r2,#0 + adc r2,r6 + ldr r4,[r1,#36] + lsr r5,r4,#16 + uxth r4,r4 + mul r5,r7 + mul r4,r7 + add r3,r4 + adc r2,r6 + lsl r4,r5,#16 + lsr r5,r5,#16 + add r3,r4 + adc r2,r5 + str r3,[r0,#4] + ldr r4,[r1,#8] + add r2,r4 + mov r3,#0 + adc r3,r6 + ldr r4,[r1,#40] + lsr r5,r4,#16 + uxth r4,r4 + mul r5,r7 + mul r4,r7 + add r2,r4 + adc r3,r6 + lsl r4,r5,#16 + lsr r5,r5,#16 + add r2,r4 + adc r3,r5 + str r2,[r0,#8] + ldr r4,[r1,#12] + add r3,r4 + mov r2,#0 + adc r2,r6 + ldr r4,[r1,#44] + lsr r5,r4,#16 + uxth r4,r4 + mul r5,r7 + mul r4,r7 + add r3,r4 + adc r2,r6 + lsl r4,r5,#16 + lsr r5,r5,#16 + add r3,r4 + adc r2,r5 + str r3,[r0,#12] + ldr r4,[r1,#16] + add r2,r4 + mov r3,#0 + adc r3,r6 + ldr r4,[r1,#48] + lsr r5,r4,#16 + uxth r4,r4 + mul r5,r7 + mul r4,r7 + add r2,r4 + adc r3,r6 + lsl r4,r5,#16 + lsr r5,r5,#16 + add r2,r4 + adc r3,r5 + str r2,[r0,#16] + ldr r4,[r1,#20] + add r3,r4 + mov r2,#0 + adc r2,r6 + ldr r4,[r1,#52] + lsr r5,r4,#16 + uxth r4,r4 + mul r5,r7 + mul r4,r7 + add r3,r4 + adc r2,r6 + lsl r4,r5,#16 + lsr r5,r5,#16 + add r3,r4 + adc r2,r5 + str r3,[r0,#20] + ldr r4,[r1,#24] + add r2,r4 + mov r3,#0 + adc r3,r6 + ldr r4,[r1,#56] + lsr r5,r4,#16 + uxth r4,r4 + mul r5,r7 + mul r4,r7 + add r2,r4 + adc r3,r6 + lsl r4,r5,#16 + lsr r5,r5,#16 + add r2,r4 + adc r3,r5 + str r2,[r0,#24] + ldr r4,[r0,#28] + add r4,r3 + str r4,[r0,#28] + pop {r4,r5,r6,r7,r15} + + .size fe25519_reduceTo256Bits_asm, .-fe25519_reduceTo256Bits_asm + diff --git a/deps/picoquic/picotls/deps/cifra/src/arm/unacl/mul.s b/deps/picoquic/picotls/deps/cifra/src/arm/unacl/mul.s new file mode 100644 index 0000000..155674c --- /dev/null +++ b/deps/picoquic/picotls/deps/cifra/src/arm/unacl/mul.s @@ -0,0 +1,1109 @@ + .align 2 + .global multiply256x256_asm + .type multiply256x256_asm, %function +multiply256x256_asm: + push {r4-r7,lr} + mov r3, r8 + mov r4, r9 + mov r5, r10 + mov r6, r11 + push {r0-r6} + mov r12, r0 + mov r10, r2 + mov r11, r1 + mov r0,r2 + ldm r0!, {r4,r5,r6,r7} + ldm r1!, {r2,r3,r6,r7} + push {r0,r1} + /////////BEGIN LOW PART ////////////////////// + /////////MUL128///////////// + //MUL64 + mov r6, r5 + mov r1, r2 + sub r5, r4 + sbc r0, r0 + eor r5, r0 + sub r5, r0 + sub r1, r3 + sbc r7, r7 + eor r1, r7 + sub r1, r7 + eor r7, r0 + mov r9, r1 + mov r8, r5 + lsr r1,r4,#16 + uxth r4,r4 + mov r0,r4 + uxth r5,r2 + lsr r2,#16 + mul r0,r5//00 + mul r5,r1//10 + mul r4,r2//01 + mul r1,r2//11 + lsl r2,r4,#16 + lsr r4,r4,#16 + add r0,r2 + adc r1,r4 + lsl r2,r5,#16 + lsr r4,r5,#16 + add r0,r2 + adc r1,r4 + lsr r4, r6,#16 + uxth r6, r6 + uxth r5, r3 + lsr r3, r3, #16 + mov r2, r6 + mul r2, r5 + mul r5, r4 + mul r6, r3 + mul r3, r4 + lsl r4,r5,#16 + lsr r5,r5,#16 + add r2,r4 + adc r3,r5 + lsl r4,r6,#16 + lsr r5,r6,#16 + add r2,r4 + adc r3,r5 + eor r6, r6 + add r2, r1 + adc r3, r6 + mov r1, r9 + mov r5, r8 + mov r8, r0 + lsr r0, r1,#16 + uxth r1,r1 + mov r4,r1 + lsr r6,r5,#16 + uxth r5,r5 + mul r1,r5 + mul r4,r6 + mul r5,r0 + mul r0,r6 + lsl r6,r4,#16 + lsr r4,#16 + add r1,r6 + adc r0,r4 + lsl r6,r5,#16 + lsr r5,#16 + add r1,r6 + adc r0,r5 + eor r1,r7 + eor r0,r7 + eor r4, r4 + asr r7, r7, #1 + adc r1, r2 + adc r2, r0 + adc r7, r4 + mov r0, r8 + add r1, r0 + adc r2, r3 + adc r3, r7 + ////////////////////////// + mov r4, r12 + stm r4!, {r0,r1} + push {r4} + push {r0,r1} + mov r1, r10 + mov r10, r2 + ldm r1, {r0, r1, r4, r5} + mov r2, r4 + mov r7, r5 + sub r2, r0 + sbc r7, r1 + sbc r6, r6 + eor r2, r6 + eor r7, r6 + sub r2, r6 + sbc r7, r6 + push {r2, r7} + mov r2, r11 + mov r11, r3 + ldm r2, {r0, r1, r2, r3} + sub r0, r2 + sbc r1, r3 + sbc r7, r7 + eor r0, r7 + eor r1, r7 + sub r0, r7 + sbc r1, r7 + eor r7, r6 + mov r12, r7 + push {r0, r1} + //MUL64 + mov r6, r5 + mov r1, r2 + sub r5, r4 + sbc r0, r0 + eor r5, r0 + sub r5, r0 + sub r1, r3 + sbc r7, r7 + eor r1, r7 + sub r1, r7 + eor r7, r0 + mov r9, r1 + mov r8, r5 + lsr r1,r4,#16 + uxth r4,r4 + mov r0,r4 + uxth r5,r2 + lsr r2,#16 + mul r0,r5//00 + mul r5,r1//10 + mul r4,r2//01 + mul r1,r2//11 + lsl r2,r4,#16 + lsr r4,r4,#16 + add r0,r2 + adc r1,r4 + lsl r2,r5,#16 + lsr r4,r5,#16 + add r0,r2 + adc r1,r4 + lsr r4, r6,#16 + uxth r6, r6 + uxth r5, r3 + lsr r3, r3, #16 + mov r2, r6 + mul r2, r5 + mul r5, r4 + mul r6, r3 + mul r3, r4 + lsl r4,r5,#16 + lsr r5,r5,#16 + add r2,r4 + adc r3,r5 + lsl r4,r6,#16 + lsr r5,r6,#16 + add r2,r4 + adc r3,r5 + eor r6, r6 + add r2, r1 + adc r3, r6 + mov r1, r9 + mov r5, r8 + mov r8, r0 + lsr r0, r1,#16 + uxth r1,r1 + mov r4,r1 + lsr r6,r5,#16 + uxth r5,r5 + mul r1,r5 + mul r4,r6 + mul r5,r0 + mul r0,r6 + lsl r6,r4,#16 + lsr r4,#16 + add r1,r6 + adc r0,r4 + lsl r6,r5,#16 + lsr r5,#16 + add r1,r6 + adc r0,r5 + eor r1,r7 + eor r0,r7 + eor r4, r4 + asr r7, r7, #1 + adc r1, r2 + adc r2, r0 + adc r7, r4 + mov r0, r8 + add r1, r0 + adc r2, r3 + adc r3, r7 + mov r4, r10 + mov r5, r11 + eor r6, r6 + add r0, r4 + adc r1, r5 + adc r2, r6 + adc r3, r6 + mov r10, r2 + mov r11, r3 + pop {r2-r5} + push {r0, r1} + mov r6, r5 + mov r1, r2 + sub r5, r4 + sbc r0, r0 + eor r5, r0 + sub r5, r0 + sub r1, r3 + sbc r7, r7 + eor r1, r7 + sub r1, r7 + eor r7, r0 + mov r9, r1 + mov r8, r5 + lsr r1,r4,#16 + uxth r4,r4 + mov r0,r4 + uxth r5,r2 + lsr r2,#16 + mul r0,r5//00 + mul r5,r1//10 + mul r4,r2//01 + mul r1,r2//11 + lsl r2,r4,#16 + lsr r4,r4,#16 + add r0,r2 + adc r1,r4 + lsl r2,r5,#16 + lsr r4,r5,#16 + add r0,r2 + adc r1,r4 + lsr r4, r6,#16 + uxth r6, r6 + uxth r5, r3 + lsr r3, r3, #16 + mov r2, r6 + mul r2, r5 + mul r5, r4 + mul r6, r3 + mul r3, r4 + lsl r4,r5,#16 + lsr r5,r5,#16 + add r2,r4 + adc r3,r5 + lsl r4,r6,#16 + lsr r5,r6,#16 + add r2,r4 + adc r3,r5 + eor r6, r6 + add r2, r1 + adc r3, r6 + mov r1, r9 + mov r5, r8 + mov r8, r0 + lsr r0, r1,#16 + uxth r1,r1 + mov r4,r1 + lsr r6,r5,#16 + uxth r5,r5 + mul r1,r5 + mul r4,r6 + mul r5,r0 + mul r0,r6 + lsl r6,r4,#16 + lsr r4,#16 + add r1,r6 + adc r0,r4 + lsl r6,r5,#16 + lsr r5,#16 + add r1,r6 + adc r0,r5 + eor r1,r7 + eor r0,r7 + eor r4, r4 + asr r7, r7, #1 + adc r1, r2 + adc r2, r0 + adc r7, r4 + mov r0, r8 + add r1, r0 + adc r2, r3 + adc r3, r7 + pop {r4, r5} + mov r6, r12 + mov r7, r12 + eor r0, r6 + eor r1, r6 + eor r2, r6 + eor r3, r6 + asr r6, r6, #1 + adc r0, r4 + adc r1, r5 + adc r4, r2 + adc r5, r3 + eor r2, r2 + adc r6,r2 + adc r7,r2 + pop {r2, r3} + mov r8, r2 + mov r9, r3 + add r2, r0 + adc r3, r1 + mov r0, r10 + mov r1, r11 + adc r4, r0 + adc r5, r1 + adc r6, r0 + adc r7, r1 + ////////END LOW PART///////////////////// + pop {r0} + stm r0!, {r2,r3} + pop {r1,r2} + push {r0} + push {r4-r7} + mov r10, r1 + mov r11, r2 + ldm r1!, {r4, r5} + ldm r2, {r2, r3} + /////////BEGIN HIGH PART//////////////// + /////////MUL128///////////// + //MUL64 + mov r6, r5 + mov r1, r2 + sub r5, r4 + sbc r0, r0 + eor r5, r0 + sub r5, r0 + sub r1, r3 + sbc r7, r7 + eor r1, r7 + sub r1, r7 + eor r7, r0 + mov r9, r1 + mov r8, r5 + lsr r1,r4,#16 + uxth r4,r4 + mov r0,r4 + uxth r5,r2 + lsr r2,#16 + mul r0,r5//00 + mul r5,r1//10 + mul r4,r2//01 + mul r1,r2//11 + lsl r2,r4,#16 + lsr r4,r4,#16 + add r0,r2 + adc r1,r4 + lsl r2,r5,#16 + lsr r4,r5,#16 + add r0,r2 + adc r1,r4 + lsr r4, r6,#16 + uxth r6, r6 + uxth r5, r3 + lsr r3, r3, #16 + mov r2, r6 + mul r2, r5 + mul r5, r4 + mul r6, r3 + mul r3, r4 + lsl r4,r5,#16 + lsr r5,r5,#16 + add r2,r4 + adc r3,r5 + lsl r4,r6,#16 + lsr r5,r6,#16 + add r2,r4 + adc r3,r5 + eor r6, r6 + add r2, r1 + adc r3, r6 + mov r1, r9 + mov r5, r8 + mov r8, r0 + lsr r0, r1,#16 + uxth r1,r1 + mov r4,r1 + lsr r6,r5,#16 + uxth r5,r5 + mul r1,r5 + mul r4,r6 + mul r5,r0 + mul r0,r6 + lsl r6,r4,#16 + lsr r4,#16 + add r1,r6 + adc r0,r4 + lsl r6,r5,#16 + lsr r5,#16 + add r1,r6 + adc r0,r5 + eor r1,r7 + eor r0,r7 + eor r4, r4 + asr r7, r7, #1 + adc r1, r2 + adc r2, r0 + adc r7, r4 + mov r0, r8 + add r1, r0 + adc r2, r3 + adc r3, r7 + push {r0,r1} + mov r1, r10 + mov r10, r2 + ldm r1, {r0, r1, r4, r5} + mov r2, r4 + mov r7, r5 + sub r2, r0 + sbc r7, r1 + sbc r6, r6 + eor r2, r6 + eor r7, r6 + sub r2, r6 + sbc r7, r6 + push {r2, r7} + mov r2, r11 + mov r11, r3 + ldm r2, {r0, r1, r2, r3} + sub r0, r2 + sbc r1, r3 + sbc r7, r7 + eor r0, r7 + eor r1, r7 + sub r0, r7 + sbc r1, r7 + eor r7, r6 + mov r12, r7 + push {r0, r1} + //MUL64 + mov r6, r5 + mov r1, r2 + sub r5, r4 + sbc r0, r0 + eor r5, r0 + sub r5, r0 + sub r1, r3 + sbc r7, r7 + eor r1, r7 + sub r1, r7 + eor r7, r0 + mov r9, r1 + mov r8, r5 + lsr r1,r4,#16 + uxth r4,r4 + mov r0,r4 + uxth r5,r2 + lsr r2,#16 + mul r0,r5//00 + mul r5,r1//10 + mul r4,r2//01 + mul r1,r2//11 + lsl r2,r4,#16 + lsr r4,r4,#16 + add r0,r2 + adc r1,r4 + lsl r2,r5,#16 + lsr r4,r5,#16 + add r0,r2 + adc r1,r4 + lsr r4, r6,#16 + uxth r6, r6 + uxth r5, r3 + lsr r3, r3, #16 + mov r2, r6 + mul r2, r5 + mul r5, r4 + mul r6, r3 + mul r3, r4 + lsl r4,r5,#16 + lsr r5,r5,#16 + add r2,r4 + adc r3,r5 + lsl r4,r6,#16 + lsr r5,r6,#16 + add r2,r4 + adc r3,r5 + eor r6, r6 + add r2, r1 + adc r3, r6 + mov r1, r9 + mov r5, r8 + mov r8, r0 + lsr r0, r1,#16 + uxth r1,r1 + mov r4,r1 + lsr r6,r5,#16 + uxth r5,r5 + mul r1,r5 + mul r4,r6 + mul r5,r0 + mul r0,r6 + lsl r6,r4,#16 + lsr r4,#16 + add r1,r6 + adc r0,r4 + lsl r6,r5,#16 + lsr r5,#16 + add r1,r6 + adc r0,r5 + eor r1,r7 + eor r0,r7 + eor r4, r4 + asr r7, r7, #1 + adc r1, r2 + adc r2, r0 + adc r7, r4 + mov r0, r8 + add r1, r0 + adc r2, r3 + adc r3, r7 + mov r4, r10 + mov r5, r11 + eor r6, r6 + add r0, r4 + adc r1, r5 + adc r2, r6 + adc r3, r6 + mov r10, r2 + mov r11, r3 + pop {r2-r5} + push {r0, r1} + mov r6, r5 + mov r1, r2 + sub r5, r4 + sbc r0, r0 + eor r5, r0 + sub r5, r0 + sub r1, r3 + sbc r7, r7 + eor r1, r7 + sub r1, r7 + eor r7, r0 + mov r9, r1 + mov r8, r5 + lsr r1,r4,#16 + uxth r4,r4 + mov r0,r4 + uxth r5,r2 + lsr r2,#16 + mul r0,r5//00 + mul r5,r1//10 + mul r4,r2//01 + mul r1,r2//11 + lsl r2,r4,#16 + lsr r4,r4,#16 + add r0,r2 + adc r1,r4 + lsl r2,r5,#16 + lsr r4,r5,#16 + add r0,r2 + adc r1,r4 + lsr r4, r6,#16 + uxth r6, r6 + uxth r5, r3 + lsr r3, r3, #16 + mov r2, r6 + mul r2, r5 + mul r5, r4 + mul r6, r3 + mul r3, r4 + lsl r4,r5,#16 + lsr r5,r5,#16 + add r2,r4 + adc r3,r5 + lsl r4,r6,#16 + lsr r5,r6,#16 + add r2,r4 + adc r3,r5 + eor r6, r6 + add r2, r1 + adc r3, r6 + mov r1, r9 + mov r5, r8 + mov r8, r0 + lsr r0, r1,#16 + uxth r1,r1 + mov r4,r1 + lsr r6,r5,#16 + uxth r5,r5 + mul r1,r5 + mul r4,r6 + mul r5,r0 + mul r0,r6 + lsl r6,r4,#16 + lsr r4,#16 + add r1,r6 + adc r0,r4 + lsl r6,r5,#16 + lsr r5,#16 + add r1,r6 + adc r0,r5 + eor r1,r7 + eor r0,r7 + eor r4, r4 + asr r7, r7, #1 + adc r1, r2 + adc r2, r0 + adc r7, r4 + mov r0, r8 + add r1, r0 + adc r2, r3 + adc r3, r7 + pop {r4, r5} + mov r6, r12 + mov r7, r12 + eor r0, r6 + eor r1, r6 + eor r2, r6 + eor r3, r6 + asr r6, r6, #1 + adc r0, r4 + adc r1, r5 + adc r4, r2 + adc r5, r3 + eor r2, r2 + adc r6,r2 //0,1 + adc r7,r2 + pop {r2, r3} + mov r8, r2 + mov r9, r3 + add r2, r0 + adc r3, r1 + mov r0, r10 + mov r1, r11 + adc r4, r0 + adc r5, r1 + adc r6, r0 + adc r7, r1 + ////////END HIGH PART///////////////////// + mov r0, r8 + mov r1, r9 + mov r8, r6 + mov r9, r7 + pop {r6, r7} + add r0, r6 + adc r1, r7 + pop {r6, r7} + adc r2, r6 + adc r3, r7 + pop {r7} + stm r7!, {r0-r3} + mov r10, r7 + eor r0,r0 + mov r6, r8 + mov r7, r9 + adc r4, r0 + adc r5, r0 + adc r6, r0 + adc r7, r0 + pop {r0,r1,r2} + mov r12, r2 + push {r0, r4-r7} + ldm r1, {r0-r7} + sub r0, r4 + sbc r1, r5 + sbc r2, r6 + sbc r3, r7 + eor r4, r4 + sbc r4, r4 + eor r0, r4 + eor r1, r4 + eor r2, r4 + eor r3, r4 + sub r0, r4 + sbc r1, r4 + sbc r2, r4 + sbc r3, r4 + mov r6, r12 + mov r12, r4 //carry + mov r5, r10 + stm r5!, {r0-r3} + mov r11, r5 + mov r8, r0 + mov r9, r1 + ldm r6, {r0-r7} + sub r4, r0 + sbc r5, r1 + sbc r6, r2 + sbc r7, r3 + eor r0, r0 + sbc r0, r0 + eor r4, r0 + eor r5, r0 + eor r6, r0 + eor r7, r0 + sub r4, r0 + sbc r5, r0 + sbc r6, r0 + sbc r7, r0 + mov r1, r12 + eor r0, r1 + mov r1, r11 + stm r1!, {r4-r7} + push {r0} + mov r2, r8 + mov r3, r9 + /////////BEGIN MIDDLE PART//////////////// + /////////MUL128///////////// + //MUL64 + mov r6, r5 + mov r1, r2 + sub r5, r4 + sbc r0, r0 + eor r5, r0 + sub r5, r0 + sub r1, r3 + sbc r7, r7 + eor r1, r7 + sub r1, r7 + eor r7, r0 + mov r9, r1 + mov r8, r5 + lsr r1,r4,#16 + uxth r4,r4 + mov r0,r4 + uxth r5,r2 + lsr r2,#16 + mul r0,r5//00 + mul r5,r1//10 + mul r4,r2//01 + mul r1,r2//11 + lsl r2,r4,#16 + lsr r4,r4,#16 + add r0,r2 + adc r1,r4 + lsl r2,r5,#16 + lsr r4,r5,#16 + add r0,r2 + adc r1,r4 + lsr r4, r6,#16 + uxth r6, r6 + uxth r5, r3 + lsr r3, r3, #16 + mov r2, r6 + mul r2, r5 + mul r5, r4 + mul r6, r3 + mul r3, r4 + lsl r4,r5,#16 + lsr r5,r5,#16 + add r2,r4 + adc r3,r5 + lsl r4,r6,#16 + lsr r5,r6,#16 + add r2,r4 + adc r3,r5 + eor r6, r6 + add r2, r1 + adc r3, r6 + mov r1, r9 + mov r5, r8 + mov r8, r0 + lsr r0, r1,#16 + uxth r1,r1 + mov r4,r1 + lsr r6,r5,#16 + uxth r5,r5 + mul r1,r5 + mul r4,r6 + mul r5,r0 + mul r0,r6 + lsl r6,r4,#16 + lsr r4,#16 + add r1,r6 + adc r0,r4 + lsl r6,r5,#16 + lsr r5,#16 + add r1,r6 + adc r0,r5 + eor r1,r7 + eor r0,r7 + eor r4, r4 + asr r7, r7, #1 + adc r1, r2 + adc r2, r0 + adc r7, r4 + mov r0, r8 + add r1, r0 + adc r2, r3 + adc r3, r7 + push {r0,r1} + mov r1, r10 + mov r10, r2 + ldm r1, {r0, r1, r4, r5} + mov r2, r4 + mov r7, r5 + sub r2, r0 + sbc r7, r1 + sbc r6, r6 + eor r2, r6 + eor r7, r6 + sub r2, r6 + sbc r7, r6 + push {r2, r7} + mov r2, r11 + mov r11, r3 + ldm r2, {r0, r1, r2, r3} + sub r0, r2 + sbc r1, r3 + sbc r7, r7 + eor r0, r7 + eor r1, r7 + sub r0, r7 + sbc r1, r7 + eor r7, r6 + mov r12, r7 + push {r0, r1} + //MUL64 + mov r6, r5 + mov r1, r2 + sub r5, r4 + sbc r0, r0 + eor r5, r0 + sub r5, r0 + sub r1, r3 + sbc r7, r7 + eor r1, r7 + sub r1, r7 + eor r7, r0 + mov r9, r1 + mov r8, r5 + lsr r1,r4,#16 + uxth r4,r4 + mov r0,r4 + uxth r5,r2 + lsr r2,#16 + mul r0,r5//00 + mul r5,r1//10 + mul r4,r2//01 + mul r1,r2//11 + lsl r2,r4,#16 + lsr r4,r4,#16 + add r0,r2 + adc r1,r4 + lsl r2,r5,#16 + lsr r4,r5,#16 + add r0,r2 + adc r1,r4 + lsr r4, r6,#16 + uxth r6, r6 + uxth r5, r3 + lsr r3, r3, #16 + mov r2, r6 + mul r2, r5 + mul r5, r4 + mul r6, r3 + mul r3, r4 + lsl r4,r5,#16 + lsr r5,r5,#16 + add r2,r4 + adc r3,r5 + lsl r4,r6,#16 + lsr r5,r6,#16 + add r2,r4 + adc r3,r5 + eor r6, r6 + add r2, r1 + adc r3, r6 + mov r1, r9 + mov r5, r8 + mov r8, r0 + lsr r0, r1,#16 + uxth r1,r1 + mov r4,r1 + lsr r6,r5,#16 + uxth r5,r5 + mul r1,r5 + mul r4,r6 + mul r5,r0 + mul r0,r6 + lsl r6,r4,#16 + lsr r4,#16 + add r1,r6 + adc r0,r4 + lsl r6,r5,#16 + lsr r5,#16 + add r1,r6 + adc r0,r5 + eor r1,r7 + eor r0,r7 + eor r4, r4 + asr r7, r7, #1 + adc r1, r2 + adc r2, r0 + adc r7, r4 + mov r0, r8 + add r1, r0 + adc r2, r3 + adc r3, r7 + mov r4, r10 + mov r5, r11 + eor r6, r6 + add r0, r4 + adc r1, r5 + adc r2, r6 + adc r3, r6 + mov r10, r2 + mov r11, r3 + pop {r2-r5} + push {r0, r1} + mov r6, r5 + mov r1, r2 + sub r5, r4 + sbc r0, r0 + eor r5, r0 + sub r5, r0 + sub r1, r3 + sbc r7, r7 + eor r1, r7 + sub r1, r7 + eor r7, r0 + mov r9, r1 + mov r8, r5 + lsr r1,r4,#16 + uxth r4,r4 + mov r0,r4 + uxth r5,r2 + lsr r2,#16 + mul r0,r5//00 + mul r5,r1//10 + mul r4,r2//01 + mul r1,r2//11 + lsl r2,r4,#16 + lsr r4,r4,#16 + add r0,r2 + adc r1,r4 + lsl r2,r5,#16 + lsr r4,r5,#16 + add r0,r2 + adc r1,r4 + lsr r4, r6,#16 + uxth r6, r6 + uxth r5, r3 + lsr r3, r3, #16 + mov r2, r6 + mul r2, r5 + mul r5, r4 + mul r6, r3 + mul r3, r4 + lsl r4,r5,#16 + lsr r5,r5,#16 + add r2,r4 + adc r3,r5 + lsl r4,r6,#16 + lsr r5,r6,#16 + add r2,r4 + adc r3,r5 + eor r6, r6 + add r2, r1 + adc r3, r6 + mov r1, r9 + mov r5, r8 + mov r8, r0 + lsr r0, r1,#16 + uxth r1,r1 + mov r4,r1 + lsr r6,r5,#16 + uxth r5,r5 + mul r1,r5 + mul r4,r6 + mul r5,r0 + mul r0,r6 + lsl r6,r4,#16 + lsr r4,#16 + add r1,r6 + adc r0,r4 + lsl r6,r5,#16 + lsr r5,#16 + add r1,r6 + adc r0,r5 + eor r1,r7 + eor r0,r7 + eor r4, r4 + asr r7, r7, #1 + adc r1, r2 + adc r2, r0 + adc r7, r4 + mov r0, r8 + add r1, r0 + adc r2, r3 + adc r3, r7 + pop {r4, r5} + mov r6, r12 + mov r7, r12 + eor r0, r6 + eor r1, r6 + eor r2, r6 + eor r3, r6 + asr r6, r6, #1 + adc r0, r4 + adc r1, r5 + adc r4, r2 + adc r5, r3 + eor r2, r2 + adc r6,r2 //0,1 + adc r7,r2 + pop {r2, r3} + mov r8, r2 + mov r9, r3 + add r2, r0 + adc r3, r1 + mov r0, r10 + mov r1, r11 + adc r4, r0 + adc r5, r1 + adc r6, r0 + adc r7, r1 + //////////END MIDDLE PART//////////////// + pop {r0,r1} //r0,r1 + mov r12, r0 //negative + eor r2, r0 + eor r3, r0 + eor r4, r0 + eor r5, r0 + eor r6, r0 + eor r7, r0 + push {r4-r7} + ldm r1!, {r4-r7} + mov r11, r1 //reference + mov r1, r9 + eor r1, r0 + mov r10, r4 + mov r4, r8 + asr r0, #1 + eor r0, r4 + mov r4, r10 + adc r0, r4 + adc r1, r5 + adc r2, r6 + adc r3, r7 + eor r4, r4 + adc r4, r4 + mov r10, r4 //carry + mov r4, r11 + ldm r4, {r4-r7} + add r0, r4 + adc r1, r5 + adc r2, r6 + adc r3, r7 + mov r9, r4 + mov r4, r11 + stm r4!, {r0-r3} + mov r11, r4 + pop {r0-r3} + mov r4, r9 + adc r4, r0 + adc r5, r1 + adc r6, r2 + adc r7, r3 + mov r1, #0 + adc r1, r1 + mov r0, r10 + mov r10, r1 //carry + asr r0, #1 + pop {r0-r3} + adc r4, r0 + adc r5, r1 + adc r6, r2 + adc r7, r3 + mov r8, r0 + mov r0, r11 + stm r0!, {r4-r7} + mov r11, r0 + mov r0, r8 + mov r6, r12 + mov r5, r10 + eor r4, r4 + adc r5, r6 + adc r6, r4 + add r0, r5 + adc r1, r6 + adc r2, r6 + adc r3, r6 + mov r7, r11 + stm r7!, {r0-r3} + pop {r3-r6} + mov r8, r3 + mov r9, r4 + mov r10, r5 + mov r11, r6 + pop {r4-r7,pc} + bx lr +.size multiply256x256_asm, .-multiply256x256_asm + diff --git a/deps/picoquic/picotls/deps/cifra/src/arm/unacl/scalarmult.c b/deps/picoquic/picotls/deps/cifra/src/arm/unacl/scalarmult.c new file mode 100644 index 0000000..488aac7 --- /dev/null +++ b/deps/picoquic/picotls/deps/cifra/src/arm/unacl/scalarmult.c @@ -0,0 +1,761 @@ +/* ======================= + ============================ C/C++ HEADER FILE ============================= + ======================= + + Collection of all required submodules from naclM0 required for curve25519 + scalar multiplication (not including randomization, etc.) alone. + + Library naclM0 largely bases on work avrNacl of M. Hutter and P. Schwabe. + + Will compile to the two functions + + int + crypto_scalarmult_base_curve25519( + unsigned char* q, + const unsigned char* n + ); + + int + crypto_scalarmult_curve25519 ( + unsigned char* r, + const unsigned char* s, + const unsigned char* p + ); + + Requires inttypes.h header and the four external assembly functions + + extern void + fe25519_reduceTo256Bits_asm ( + fe25519 *res, + const UN_512bitValue *in + ); + + extern void + fe25519_mpyWith121666_asm ( + fe25519* out, + const fe25519* in + ); + + extern void + multiply256x256_asm ( + UN_512bitValue* result, + const UN_256bitValue* x, + const UN_256bitValue* y + ); + + extern void + square256_asm ( + UN_512bitValue* result, + const UN_256bitValue* x + ); + + \file scalarmult.c + + \Author B. Haase, Endress + Hauser Conducta GmbH & Co. KG + + License: CC Common Creative license Attribution 4.0 International (CC BY 4.0) + http://creativecommons.org/licenses/by/4.0/ + ============================================================================*/ + +#include + +// comment out this line if implementing conditional swaps by data moves +//#define DH_SWAP_BY_POINTERS + +// Define the symbol to 0 in order to only use ladder steps +//#define DH_REPLACE_LAST_THREE_LADDERSTEPS_WITH_DOUBLINGS 1 + +typedef uint8_t uint8; +typedef uint16_t uint16; +typedef uint32_t uint32; +typedef uint64_t uint64; +typedef uintptr_t uintptr; + +typedef int8_t int8; +typedef int16_t int16; +typedef int32_t int32; +typedef int64_t int64; +typedef intptr_t intptr; + +// Note that it's important to define the unit8 as first union member, so that +// an array of uint8 may be used as initializer. +typedef union UN_256bitValue_ +{ + uint8 as_uint8[32]; + uint16 as_uint16[16]; + uint32 as_uint32[8]; + uint64 as_uint64[4]; +} UN_256bitValue; + +// Note that it's important to define the unit8 as first union member, so that +// an array of uint8 may be used as initializer. +typedef union UN_512bitValue_ +{ + uint8 as_uint8[64]; + uint16 as_uint16[32]; + uint32 as_uint32[16]; + uint64 as_uint64[8]; + UN_256bitValue as_256_bitValue[2]; +} UN_512bitValue; + +typedef UN_256bitValue fe25519; + +// **************************************************** +// Assembly functions. +// **************************************************** + +extern void +fe25519_reduceTo256Bits_asm( + fe25519 *res, + const UN_512bitValue *in +); + +#define fe25519_mpyWith121666 fe25519_mpyWith121666_asm +extern void +fe25519_mpyWith121666_asm ( + fe25519* out, + const fe25519* in +); + +#define multiply256x256 multiply256x256_asm +extern void +multiply256x256( + UN_512bitValue* result, + const UN_256bitValue* x, + const UN_256bitValue* y +); + +#define square256 square256_asm +extern void +square256( + UN_512bitValue* result, + const UN_256bitValue* x +); + +// **************************************************** +// C functions for fe25519 +// **************************************************** + +static void +fe25519_cpy( + fe25519* dest, + const fe25519* source +) +{ + uint32 ctr; + + for (ctr = 0; ctr < 8; ctr++) + { + dest->as_uint32[ctr] = source->as_uint32[ctr]; + } +} + +static void +fe25519_unpack( + volatile fe25519* out, + const unsigned char in[32] +) +{ + uint8 ctr; + + for (ctr = 0; ctr < 32; ctr++) + { + out->as_uint8[ctr] = in[ctr]; + } + out->as_uint8[31] &= 0x7f; // make sure that the last bit is cleared. +} + +static void +fe25519_sub( + fe25519* out, + const fe25519* baseValue, + const fe25519* valueToSubstract +) +{ + uint16 ctr; + int64 accu = 0; + + // First subtract the most significant word, so that we may + // reduce the result "on the fly". + accu = baseValue->as_uint32[7]; + accu -= valueToSubstract->as_uint32[7]; + + // We always set bit #31, and compensate this by subtracting 1 from the reduction + // value. + out->as_uint32[7] = ((uint32)accu) | 0x80000000ul; + + accu = 19 * ((int32)(accu >> 31) - 1); + // ^ "-1" is the compensation for the "| 0x80000000ul" above. + // This choice makes sure, that the result will be positive! + + for (ctr = 0; ctr < 7; ctr += 1) + { + accu += baseValue->as_uint32[ctr]; + accu -= valueToSubstract->as_uint32[ctr]; + + out->as_uint32[ctr] = (uint32)accu; + accu >>= 32; + } + accu += out->as_uint32[7]; + out->as_uint32[7] = (uint32)accu; +} + +static void +fe25519_add( + fe25519* out, + const fe25519* baseValue, + const fe25519* valueToAdd +) +{ + uint16 ctr = 0; + uint64 accu = 0; + + // We first add the most significant word, so that we may reduce + // "on the fly". + accu = baseValue->as_uint32[7]; + accu += valueToAdd->as_uint32[7]; + out->as_uint32[7] = ((uint32)accu) & 0x7ffffffful; + + accu = ((uint32)(accu >> 31)) * 19; + + for (ctr = 0; ctr < 7; ctr += 1) + { + accu += baseValue->as_uint32[ctr]; + accu += valueToAdd->as_uint32[ctr]; + + out->as_uint32[ctr] = (uint32)accu; + accu >>= 32; + } + accu += out->as_uint32[7]; + out->as_uint32[7] = (uint32)accu; +} + +static void +fe25519_mul( + fe25519* result, + const fe25519* in1, + const fe25519* in2 +) +{ + UN_512bitValue tmp; + + multiply256x256(&tmp, in1, in2); + fe25519_reduceTo256Bits_asm(result,&tmp); +} + +static void +fe25519_square( + fe25519* result, + const fe25519* in +) +{ + UN_512bitValue tmp; + + square256(&tmp, in); + fe25519_reduceTo256Bits_asm(result,&tmp); +} + +static void +fe25519_reduceCompletely( + volatile fe25519* inout +) +{ + uint32 numberOfTimesToSubstractPrime; + uint32 initialGuessForNumberOfTimesToSubstractPrime = inout->as_uint32[7] >> + 31; + uint64 accu; + uint8 ctr; + + // add one additional 19 to the estimated number of reductions. + // Do the calculation without writing back the results to memory. + // + // The initial guess of required numbers of reductions is based + // on bit #32 of the most significant word. + // This initial guess may be wrong, since we might have a value + // v in the range + // 2^255 - 19 <= v < 2^255 + // . After adding 19 to the value, we will be having the correct + // Number of required subtractions. + accu = initialGuessForNumberOfTimesToSubstractPrime * 19 + 19; + + for (ctr = 0; ctr < 7; ctr++) + { + accu += inout->as_uint32[ctr]; + accu >>= 32; + } + accu += inout->as_uint32[7]; + + numberOfTimesToSubstractPrime = (uint32)(accu >> 31); + + // Do the reduction. + accu = numberOfTimesToSubstractPrime * 19; + + for (ctr = 0; ctr < 7; ctr++) + { + accu += inout->as_uint32[ctr]; + inout->as_uint32[ctr] = (uint32)accu; + accu >>= 32; + } + accu += inout->as_uint32[7]; + inout->as_uint32[7] = accu & 0x7ffffffful; +} + +/// We are already using a packed radix 16 representation for fe25519. The real use for this function +/// is for architectures that use more bits for storing a fe25519 in a representation where multiplication +/// may be calculated more efficiently. +/// Here we simply copy the data. +static void +fe25519_pack( + unsigned char out[32], + volatile fe25519* in +) +{ + uint8 ctr; + + fe25519_reduceCompletely(in); + + for (ctr = 0; ctr < 32; ctr++) + { + out[ctr] = in->as_uint8[ctr]; + } +} + +// Note, that r and x are allowed to overlap! +static void +fe25519_invert_useProvidedScratchBuffers( + fe25519* r, + const fe25519* x, + fe25519* t0, + fe25519* t1, + fe25519* t2 +) +{ + fe25519 *z11 = r; // store z11 in r (in order to save one temporary). + fe25519 *z2_10_0 = t1; + fe25519 *z2_50_0 = t2; + fe25519 *z2_100_0 = z2_10_0; + + uint8 i; + + { + fe25519 *z2 = z2_50_0; + + /* 2 */ fe25519_square(z2, x); + /* 4 */ fe25519_square(t0, z2); + /* 8 */ fe25519_square(t0, t0); + /* 9 */ fe25519_mul(z2_10_0, t0, x); + /* 11 */ fe25519_mul(z11, z2_10_0, z2); + + // z2 is dead. + } + + /* 22 */ fe25519_square(t0, z11); + /* 2^5 - 2^0 = 31 */ fe25519_mul(z2_10_0, t0, z2_10_0); + + /* 2^6 - 2^1 */ fe25519_square(t0, z2_10_0); + /* 2^7 - 2^2 */ fe25519_square(t0, t0); + /* 2^8 - 2^3 */ fe25519_square(t0, t0); + /* 2^9 - 2^4 */ fe25519_square(t0, t0); + /* 2^10 - 2^5 */ fe25519_square(t0, t0); + /* 2^10 - 2^0 */ fe25519_mul(z2_10_0, t0, z2_10_0); + + /* 2^11 - 2^1 */ fe25519_square(t0, z2_10_0); + + /* 2^20 - 2^10 */ for (i = 1; i < 10; i ++) + { + fe25519_square(t0, t0); + } + /* 2^20 - 2^0 */ fe25519_mul(z2_50_0, t0, z2_10_0); + + /* 2^21 - 2^1 */ fe25519_square(t0, z2_50_0); + + /* 2^40 - 2^20 */ for (i = 1; i < 20; i ++) + { + fe25519_square(t0, t0); + } + /* 2^40 - 2^0 */ fe25519_mul(t0, t0, z2_50_0); + + /* 2^41 - 2^1 */ fe25519_square(t0, t0); + + /* 2^50 - 2^10 */ for (i = 1; i < 10; i ++) + { + fe25519_square(t0, t0); + } + /* 2^50 - 2^0 */ fe25519_mul(z2_50_0, t0, z2_10_0); + + /* 2^51 - 2^1 */ fe25519_square(t0, z2_50_0); + + /* 2^100 - 2^50 */ for (i = 1; i < 50; i ++) + { + fe25519_square(t0, t0); + } + /* 2^100 - 2^0 */ fe25519_mul(z2_100_0, t0, z2_50_0); + + /* 2^101 - 2^1 */ fe25519_square(t0, z2_100_0); + + /* 2^200 - 2^100 */ for (i = 1; i < 100; i ++) + { + fe25519_square(t0, t0); + } + /* 2^200 - 2^0 */ fe25519_mul(t0, t0, z2_100_0); + + /* 2^250 - 2^50 */ for (i = 0; i < 50; i ++) + { + fe25519_square(t0, t0); + } + /* 2^250 - 2^0 */ fe25519_mul(t0, t0, z2_50_0); + + /* 2^255 - 2^5 */ for (i = 0; i < 5; i ++) + { + fe25519_square(t0, t0); + } + /* 2^255 - 21 */ fe25519_mul(r, t0, z11); +} + +static void +fe25519_setzero( + fe25519* out +) +{ + uint8 ctr; + + for (ctr = 0; ctr < 8; ctr++) + { + out->as_uint32[ctr] = 0; + } +} + +static void +fe25519_setone( + fe25519* out +) +{ + uint8 ctr; + + out->as_uint32[0] = 1; + + for (ctr = 1; ctr < 8; ctr++) + { + out->as_uint32[ctr] = 0; + } +} + +/* +static void +swapPointersConditionally (void **p1, void **p2, uint8 condition) +{ + // Secure version of this code: + // + // if (condition) + // { + // void *temp; + // temp = *p2; + // *p2 = *p1; + // *p1 = temp; + // } + + uintptr mask = condition; + uintptr val1 = (uintptr) *p1; + uintptr val2 = (uintptr) *p2; + uintptr temp = val2 ^ val1; + + mask = (uintptr)( - (intptr) mask ); + temp ^= mask & (temp ^ val1); + val1 ^= mask & (val1 ^ val2); + val2 ^= mask & (val2 ^ temp); + + *p1 = (void *) val1; + *p2 = (void *) val2; +} +*/ + +static void +fe25519_cswap( + fe25519* in1, + fe25519* in2, + int condition +) +{ + int32 mask = condition; + uint32 ctr; + + mask = -mask; + + for (ctr = 0; ctr < 8; ctr++) + { + uint32 val1 = in1->as_uint32[ctr]; + uint32 val2 = in2->as_uint32[ctr]; + uint32 temp = val1; + + val1 ^= mask & (val2 ^ val1); + val2 ^= mask & (val2 ^ temp); + + + in1->as_uint32[ctr] = val1; + in2->as_uint32[ctr] = val2; + } +} + +// **************************************************** +// Scalarmultiplication implementation. +// **************************************************** + +typedef struct _ST_curve25519ladderstepWorkingState +{ + // The base point in affine coordinates + fe25519 x0; + + // The two working points p, q, in projective coordinates. Possibly randomized. + fe25519 xp; + fe25519 zp; + fe25519 xq; + fe25519 zq; + + volatile UN_256bitValue s; + + int nextScalarBitToProcess; + uint8 previousProcessedBit; + +#ifdef DH_SWAP_BY_POINTERS + fe25519 *pXp; + fe25519 *pZp; + fe25519 *pXq; + fe25519 *pZq; +#endif + +} ST_curve25519ladderstepWorkingState; + +static void +curve25519_ladderstep( + ST_curve25519ladderstepWorkingState* pState +) +{ + // Implements the "ladd-1987-m-3" differential-addition-and-doubling formulas + // Source: 1987 Montgomery "Speeding the Pollard and elliptic curve methods of factorization", page 261, + // fifth and sixth displays, plus common-subexpression elimination. + // + // Notation from the explicit formulas database: + // (X2,Z2) corresponds to (xp,zp), + // (X3,Z3) corresponds to (xq,zq) + // Result (X4,Z4) (X5,Z5) expected in (xp,zp) and (xq,zq) + // + // A = X2+Z2; AA = A^2; B = X2-Z2; BB = B^2; E = AA-BB; C = X3+Z3; D = X3-Z3; + // DA = D*A; CB = C*B; t0 = DA+CB; t1 = t0^2; X5 = Z1*t1; t2 = DA-CB; + // t3 = t2^2; Z5 = X1*t3; X4 = AA*BB; t4 = a24*E; t5 = BB+t4; Z4 = E*t5 ; + // + // Re-Ordered for using less temporaries. + + fe25519 t1, t2; + + #ifdef DH_SWAP_BY_POINTERS + fe25519 *b1=pState->pXp; fe25519 *b2=pState->pZp; + fe25519 *b3=pState->pXq; fe25519 *b4=pState->pZq; + #else + fe25519 *b1=&pState->xp; fe25519 *b2=&pState->zp; + fe25519 *b3=&pState->xq; fe25519 *b4=&pState->zq; + #endif + + fe25519 *b5= &t1; fe25519 *b6=&t2; + + fe25519_add(b5,b1,b2); // A = X2+Z2 + fe25519_sub(b6,b1,b2); // B = X2-Z2 + fe25519_add(b1,b3,b4); // C = X3+Z3 + fe25519_sub(b2,b3,b4); // D = X3-Z3 + fe25519_mul(b3,b2,b5); // DA= D*A + fe25519_mul(b2,b1,b6); // CB= C*B + fe25519_add(b1,b2,b3); // T0= DA+CB + fe25519_sub(b4,b3,b2); // T2= DA-CB + fe25519_square(b3,b1); // X5==T1= T0^2 + fe25519_square(b1,b4); // T3= t2^2 + fe25519_mul(b4,b1,&pState->x0); // Z5=X1*t3 + fe25519_square(b1,b5); // AA=A^2 + fe25519_square(b5,b6); // BB=B^2 + fe25519_sub(b2,b1,b5); // E=AA-BB + fe25519_mul(b1,b5,b1); // X4= AA*BB + fe25519_mpyWith121666 (b6,b2); // T4 = a24*E + fe25519_add(b6,b6,b5); // T5 = BB + t4 + fe25519_mul(b2,b6,b2); // Z4 = E*t5 +} + +static void +curve25519_cswap( + ST_curve25519ladderstepWorkingState* state, + uint8 b +) +{ + #ifdef DH_SWAP_BY_POINTERS + swapPointersConditionally ((void **) &state->pXp,(void **) &state->pXq,b); + swapPointersConditionally ((void **) &state->pZp,(void **) &state->pZq,b); + #else + fe25519_cswap (&state->xp, &state->xq,b); + fe25519_cswap (&state->zp, &state->zq,b); + #endif +} + +#if DH_REPLACE_LAST_THREE_LADDERSTEPS_WITH_DOUBLINGS + +static void +curve25519_doublePointP (ST_curve25519ladderstepWorkingState* pState) +{ + // Implement the doubling formula "dbl-1987-m-3" + // from 1987 Montgomery "Speeding the Pollard and elliptic curve methods of factorization", + // page 261, sixth display, plus common-subexpression elimination. + // + // Three operand code: + // A = X1+Z1 + // AA = A^2 + // B = X1-Z1 + // BB = B^2 + // C = AA-BB + // X3 = AA*BB + // t0 = a24*C + // t1 = BB+t0 + // Z3 = C*t1 + + // Double the point input in the state variable "P". Use the State variable "Q" as temporary + // for storing A, AA and B, BB. Use the same temporary variable for A and AA respectively and + // B, BB respectively. + #ifdef DH_SWAP_BY_POINTERS + fe25519 *pA = pState->pXq; + fe25519 *pB = pState->pZq; + fe25519 *pX = pState->pXp; + fe25519 *pZ = pState->pZp; + #else + fe25519 *pA = &pState->xq; + fe25519 *pB = &pState->zq; + fe25519 *pX = &pState->xp; + fe25519 *pZ = &pState->zp; + #endif + + // A = X1+Z1 + fe25519_add(pA, pX, pZ); + // AA = A^2 + fe25519_square (pA,pA); + // B = X1-Z1 + fe25519_sub(pB, pX, pZ); + // BB = B^2 + fe25519_square (pB,pB); + // X3 = AA*BB + fe25519_mul (pX,pA,pB); + // C = AA-BB + fe25519_sub (pZ,pA,pB); + // t0 = a24*C + fe25519_mpyWith121666 (pA,pZ); + // t1 = BB+t0 + fe25519_add (pB,pA,pB); + // Z3 = C*t1 + fe25519_mul (pZ,pZ,pB); +} + +#endif // #ifdef DH_REPLACE_LAST_THREE_LADDERSTEPS_WITH_DOUBLINGS + +int +crypto_scalarmult_curve25519( + unsigned char* r, + const unsigned char* s, + const unsigned char* p +) +{ + ST_curve25519ladderstepWorkingState state; + unsigned char i; + + + // Prepare the scalar within the working state buffer. + for (i = 0; i < 32; i++) + { + state.s.as_uint8 [i] = s[i]; + } +#if DH_REPLACE_LAST_THREE_LADDERSTEPS_WITH_DOUBLINGS + // Due to explicit final doubling for the last three bits instead of a full ladderstep, + // the following line is no longer necessary. +#else + state.s.as_uint8 [0] &= 248; +#endif + state.s.as_uint8 [31] &= 127; + state.s.as_uint8 [31] |= 64; + + // Copy the affine x-axis of the base point to the state. + fe25519_unpack (&state.x0, p); + + // Prepare the working points within the working state struct. + + fe25519_setone (&state.zq); + fe25519_cpy (&state.xq, &state.x0); + + fe25519_setone(&state.xp); + fe25519_setzero(&state.zp); + + state.nextScalarBitToProcess = 254; + +#ifdef DH_SWAP_BY_POINTERS + // we need to initially assign the pointers correctly. + state.pXp = &state.xp; + state.pZp = &state.zp; + state.pXq = &state.xq; + state.pZq = &state.zq; +#endif + + state.previousProcessedBit = 0; + +#if DH_REPLACE_LAST_THREE_LADDERSTEPS_WITH_DOUBLINGS + // Process all the bits except for the last three where we explicitly double the result. + while (state.nextScalarBitToProcess >= 3) +#else + // Process all the bits except for the last three where we explicitly double the result. + while (state.nextScalarBitToProcess >= 0) +#endif + { + uint8 byteNo = state.nextScalarBitToProcess >> 3; + uint8 bitNo = state.nextScalarBitToProcess & 7; + uint8 bit; + uint8 swap; + + bit = 1 & (state.s.as_uint8 [byteNo] >> bitNo); + swap = bit ^ state.previousProcessedBit; + state.previousProcessedBit = bit; + curve25519_cswap(&state, swap); + curve25519_ladderstep(&state); + state.nextScalarBitToProcess --; + } + + curve25519_cswap(&state,state.previousProcessedBit); + +#if DH_REPLACE_LAST_THREE_LADDERSTEPS_WITH_DOUBLINGS + curve25519_doublePointP (&state); + curve25519_doublePointP (&state); + curve25519_doublePointP (&state); +#endif + +#ifdef DH_SWAP_BY_POINTERS + // optimize for stack usage. + fe25519_invert_useProvidedScratchBuffers (state.pZp, state.pZp, state.pXq,state.pZq,&state.x0); + fe25519_mul(state.pXp, state.pXp, state.pZp); + fe25519_reduceCompletely(state.pXp); + + fe25519_pack (r, state.pXp); +#else + // optimize for stack usage. + fe25519_invert_useProvidedScratchBuffers (&state.zp, &state.zp, &state.xq, &state.zq, &state.x0); + fe25519_mul(&state.xp, &state.xp, &state.zp); + fe25519_reduceCompletely(&state.xp); + + fe25519_pack (r, &state.xp); +#endif + + return 0; +} + +int +crypto_scalarmult_curve25519_base( + unsigned char* q, + const unsigned char* n +) +{ + static const uint8 base[32] = + { + 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + + return crypto_scalarmult_curve25519(q, n, base); +} diff --git a/deps/picoquic/picotls/deps/cifra/src/arm/unacl/sqr.s b/deps/picoquic/picotls/deps/cifra/src/arm/unacl/sqr.s new file mode 100644 index 0000000..3b190c9 --- /dev/null +++ b/deps/picoquic/picotls/deps/cifra/src/arm/unacl/sqr.s @@ -0,0 +1,777 @@ + .align 2 + .global square256_asm + .type square256_asm, %function +square256_asm: + push {r4-r7,lr} + mov r2, r8 + mov r3, r9 + mov r4, r10 + mov r5, r11 + push {r0-r5} + + mov r12, r0 + mov r4, r1 + ldm r4!, {r0-r3} + push {r4} + /////////BEGIN LOW PART ////////////////////// + ///SQR 128, in r0-r3 + mov r8, r2 + mov r9, r3 + eor r4, r4 + sub r2, r0 + sbc r3, r1 + sbc r4, r4 + eor r2, r4 + eor r3, r4 + sub r2, r4 + sbc r3, r4 + mov r10, r2 + mov r11, r3 + //SQR64, in: r0, r1, out: r0-r3, used: r0-r6 + mov r2, r0 + eor r3, r3 + sub r2, r1 + sbc r3, r3 + eor r2, r3 + sub r2, r3 + lsr r3, r0, #16 + uxth r0, r0 + mov r4, r0 + mul r4, r3 + mul r0, r0 + mul r3, r3 + lsr r5, r4, #16 + lsl r4, #16 + add r0, r4 + adc r3, r5 + add r0, r4 + adc r3, r5 + lsr r4, r1, #16 + uxth r1, r1 + mov r5, r1 + mul r5, r4 + mul r1, r1 + mul r4, r4 + eor r6, r6 + add r1, r3 + adc r4, r6 + lsr r3, r5, #16 + lsl r5, r5, #16 + add r1, r5 + adc r4, r3 + add r1, r5 + adc r3, r4 + lsr r4, r2, #16 + uxth r2, r2 + mov r5, r2 + mul r5, r4 + mul r2, r2 + mul r4, r4 + lsr r6, r5, #16 + lsl r5, #16 + add r2, r5 + adc r4, r6 + add r5, r2 + adc r6, r4 + eor r7, r7 + mov r2, r1 + sub r1, r5 + sbc r2, r6 + sbc r7, r7 + add r1, r0 + adc r2, r3 + adc r7, r3 + mov r3, r12 + stm r3!, {r0-r1} + push {r3} + + mov r12, r0 + mov r0, r8 + mov r8, r1 + mov r1, r9 + mov r9, r2 + //SQR64, in: r0, r1, out: r0-r3, used: r0-r6 + mov r2, r0 + eor r3, r3 + sub r2, r1 + sbc r3, r3 + eor r2, r3 + sub r2, r3 + lsr r3, r0, #16 + uxth r0, r0 + mov r4, r0 + mul r4, r3 + mul r0, r0 + mul r3, r3 + lsr r5, r4, #16 + lsl r4, #16 + add r0, r4 + adc r3, r5 + add r0, r4 + adc r3, r5 + lsr r4, r1, #16 + uxth r1, r1 + mov r5, r1 + mul r5, r4 + mul r1, r1 + mul r4, r4 + eor r6, r6 + add r1, r3 + adc r4, r6 + lsr r3, r5, #16 + lsl r5, r5, #16 + add r1, r5 + adc r4, r3 + add r1, r5 + adc r3, r4 + lsr r4, r2, #16 + uxth r2, r2 + mov r5, r2 + mul r5, r4 + mul r2, r2 + mul r4, r4 + lsr r6, r5, #16 + lsl r5, #16 + add r2, r5 + adc r4, r6 + add r5, r2 + adc r6, r4 + eor r4, r4 + mov r2, r1 + sub r1, r5 + sbc r2, r6 + sbc r4, r4 + add r1, r0 + adc r2, r3 + adc r3, r4 + eor r4, r4 + mov r6, r9 + add r0, r6 + adc r7, r1 + adc r2, r4 + adc r3, r4 + mov r1, r11 + mov r11, r0 + mov r0, r10 + mov r9, r2 + mov r10,r3 + //SQR64, in: r0, r1, out: r0-r3, used: r0-r6 + mov r2, r0 + eor r3, r3 + sub r2, r1 + sbc r3, r3 + eor r2, r3 + sub r2, r3 + lsr r3, r0, #16 + uxth r0, r0 + mov r4, r0 + mul r4, r3 + mul r0, r0 + mul r3, r3 + lsr r5, r4, #16 + lsl r4, #16 + add r0, r4 + adc r3, r5 + add r0, r4 + adc r3, r5 + lsr r4, r1, #16 + uxth r1, r1 + mov r5, r1 + mul r5, r4 + mul r1, r1 + mul r4, r4 + eor r6, r6 + add r1, r3 + adc r4, r6 + lsr r3, r5, #16 + lsl r5, r5, #16 + add r1, r5 + adc r4, r3 + add r1, r5 + adc r3, r4 + lsr r4, r2, #16 + uxth r2, r2 + mov r5, r2 + mul r5, r4 + mul r2, r2 + mul r4, r4 + lsr r6, r5, #16 + lsl r5, #16 + add r2, r5 + adc r4, r6 + add r5, r2 + adc r6, r4 + eor r4, r4 + mov r2, r1 + sub r1, r5 + sbc r2, r6 + sbc r4, r4 + add r1, r0 + adc r2, r3 + adc r3, r4 + mov r6, r11 + mov r4, r11 + mov r5, r7 + sub r6, r0 + sbc r7, r1 + sbc r4, r2 + sbc r5, r3 + eor r1, r1 + sbc r1, r1 + mov r2, r12 + mov r3, r8 + add r2, r6 + adc r3, r7 + mov r6, r9 + mov r7, r10 + adc r4, r6 + adc r5, r7 + adc r6, r1 + adc r7, r1 + //results r12, r8, r2-r7 + /////////END LOW PART //////////////////////// + pop {r0,r1} + stm r0!, {r2, r3} + push {r0, r4-r7} + ldm r1, {r0-r3} + /////////BEGIN HIGH PART ////////////////////// + ///SQR 128, in r0-r3 + mov r8, r2 + mov r9, r3 + eor r4, r4 + sub r2, r0 + sbc r3, r1 + sbc r4, r4 + eor r2, r4 + eor r3, r4 + sub r2, r4 + sbc r3, r4 + mov r10, r2 + mov r11, r3 + //SQR64, in: r0, r1, out: r0-r3, used: r0-r6 + mov r2, r0 + eor r3, r3 + sub r2, r1 + sbc r3, r3 + eor r2, r3 + sub r2, r3 + lsr r3, r0, #16 + uxth r0, r0 + mov r4, r0 + mul r4, r3 + mul r0, r0 + mul r3, r3 + lsr r5, r4, #16 + lsl r4, #16 + add r0, r4 + adc r3, r5 + add r0, r4 + adc r3, r5 + lsr r4, r1, #16 + uxth r1, r1 + mov r5, r1 + mul r5, r4 + mul r1, r1 + mul r4, r4 + eor r6, r6 + add r1, r3 + adc r4, r6 + lsr r3, r5, #16 + lsl r5, r5, #16 + add r1, r5 + adc r4, r3 + add r1, r5 + adc r3, r4 + lsr r4, r2, #16 + uxth r2, r2 + mov r5, r2 + mul r5, r4 + mul r2, r2 + mul r4, r4 + lsr r6, r5, #16 + lsl r5, #16 + add r2, r5 + adc r4, r6 + add r5, r2 + adc r6, r4 + eor r7, r7 + mov r2, r1 + sub r1, r5 + sbc r2, r6 + sbc r7, r7 + add r1, r0 + adc r2, r3 + adc r7, r3 + mov r12, r0 + mov r0, r8 + mov r8, r1 + mov r1, r9 + mov r9, r2 + //SQR64, in: r0, r1, out: r0-r3, used: r0-r6 + mov r2, r0 + eor r3, r3 + sub r2, r1 + sbc r3, r3 + eor r2, r3 + sub r2, r3 + lsr r3, r0, #16 + uxth r0, r0 + mov r4, r0 + mul r4, r3 + mul r0, r0 + mul r3, r3 + lsr r5, r4, #16 + lsl r4, #16 + add r0, r4 + adc r3, r5 + add r0, r4 + adc r3, r5 + lsr r4, r1, #16 + uxth r1, r1 + mov r5, r1 + mul r5, r4 + mul r1, r1 + mul r4, r4 + eor r6, r6 + add r1, r3 + adc r4, r6 + lsr r3, r5, #16 + lsl r5, r5, #16 + add r1, r5 + adc r4, r3 + add r1, r5 + adc r3, r4 + lsr r4, r2, #16 + uxth r2, r2 + mov r5, r2 + mul r5, r4 + mul r2, r2 + mul r4, r4 + lsr r6, r5, #16 + lsl r5, #16 + add r2, r5 + adc r4, r6 + add r5, r2 + adc r6, r4 + eor r4, r4 + mov r2, r1 + sub r1, r5 + sbc r2, r6 + sbc r4, r4 + add r1, r0 + adc r2, r3 + adc r3, r4 + eor r4, r4 + mov r6, r9 + add r0, r6 + adc r7, r1 + adc r2, r4 + adc r3, r4 + mov r1, r11 + mov r11, r0 + mov r0, r10 + mov r9, r2 + mov r10,r3 + //SQR64, in: r0, r1, out: r0-r3, used: r0-r6 + mov r2, r0 + eor r3, r3 + sub r2, r1 + sbc r3, r3 + eor r2, r3 + sub r2, r3 + lsr r3, r0, #16 + uxth r0, r0 + mov r4, r0 + mul r4, r3 + mul r0, r0 + mul r3, r3 + lsr r5, r4, #16 + lsl r4, #16 + add r0, r4 + adc r3, r5 + add r0, r4 + adc r3, r5 + lsr r4, r1, #16 + uxth r1, r1 + mov r5, r1 + mul r5, r4 + mul r1, r1 + mul r4, r4 + eor r6, r6 + add r1, r3 + adc r4, r6 + lsr r3, r5, #16 + lsl r5, r5, #16 + add r1, r5 + adc r4, r3 + add r1, r5 + adc r3, r4 + lsr r4, r2, #16 + uxth r2, r2 + mov r5, r2 + mul r5, r4 + mul r2, r2 + mul r4, r4 + lsr r6, r5, #16 + lsl r5, #16 + add r2, r5 + adc r4, r6 + add r5, r2 + adc r6, r4 + eor r4, r4 + mov r2, r1 + sub r1, r5 + sbc r2, r6 + sbc r4, r4 + add r1, r0 + adc r2, r3 + adc r3, r4 + mov r6, r11 + mov r4, r11 + mov r5, r7 + sub r6, r0 + sbc r7, r1 + sbc r4, r2 + sbc r5, r3 + eor r1, r1 + sbc r1, r1 + mov r2, r12 + mov r3, r8 + add r2, r6 + adc r3, r7 + mov r6, r9 + mov r7, r10 + adc r4, r6 + adc r5, r7 + adc r6, r1 + adc r7, r1 + //results r12, r8, r2-r7 + /////////END HIGH PART //////////////////////// + mov r0, r12 + mov r1, r8 + mov r8, r4 + mov r9, r5 + mov r10, r6 + mov r11, r7 + pop {r4} + mov r12, r4//str + pop {r4-r7} + add r0, r4 + adc r1, r5 + adc r2, r6 + adc r3, r7 + mov r4, r12 + stm r4!, {r0-r3}//low part + mov r4, r8 + mov r5, r9 + mov r6, r10 + mov r7, r11 + eor r0, r0 + adc r4, r0 + adc r5, r0 + adc r6, r0 + adc r7, r0 + pop {r0, r1} //r0->out, r1, in + push {r0,r4-r7} + ldm r1, {r0-r7} + sub r0, r4 + sbc r1, r5 + sbc r2, r6 + sbc r3, r7 + sbc r4, r4 + eor r0, r4 + eor r1, r4 + eor r2, r4 + eor r3, r4 + sub r0, r4 + sbc r1, r4 + sbc r2, r4 + sbc r3, r4 + //////////BEGIN MIDDLE PART//////////////// + ///SQR 128, in r0-r3 + mov r8, r2 + mov r9, r3 + eor r4, r4 + sub r2, r0 + sbc r3, r1 + sbc r4, r4 + eor r2, r4 + eor r3, r4 + sub r2, r4 + sbc r3, r4 + mov r10, r2 + mov r11, r3 + //SQR64, in: r0, r1, out: r0-r3, used: r0-r6 + mov r2, r0 + eor r3, r3 + sub r2, r1 + sbc r3, r3 + eor r2, r3 + sub r2, r3 + lsr r3, r0, #16 + uxth r0, r0 + mov r4, r0 + mul r4, r3 + mul r0, r0 + mul r3, r3 + lsr r5, r4, #16 + lsl r4, #16 + add r0, r4 + adc r3, r5 + add r0, r4 + adc r3, r5 + lsr r4, r1, #16 + uxth r1, r1 + mov r5, r1 + mul r5, r4 + mul r1, r1 + mul r4, r4 + eor r6, r6 + add r1, r3 + adc r4, r6 + lsr r3, r5, #16 + lsl r5, r5, #16 + add r1, r5 + adc r4, r3 + add r1, r5 + adc r3, r4 + lsr r4, r2, #16 + uxth r2, r2 + mov r5, r2 + mul r5, r4 + mul r2, r2 + mul r4, r4 + lsr r6, r5, #16 + lsl r5, #16 + add r2, r5 + adc r4, r6 + add r5, r2 + adc r6, r4 + eor r7, r7 + mov r2, r1 + sub r1, r5 + sbc r2, r6 + sbc r7, r7 + add r1, r0 + adc r2, r3 + adc r7, r3 + mov r12, r0 + mov r0, r8 + mov r8, r1 + mov r1, r9 + mov r9, r2 + //SQR64, in: r0, r1, out: r0-r3, used: r0-r6 + mov r2, r0 + eor r3, r3 + sub r2, r1 + sbc r3, r3 + eor r2, r3 + sub r2, r3 + lsr r3, r0, #16 + uxth r0, r0 + mov r4, r0 + mul r4, r3 + mul r0, r0 + mul r3, r3 + lsr r5, r4, #16 + lsl r4, #16 + add r0, r4 + adc r3, r5 + add r0, r4 + adc r3, r5 + lsr r4, r1, #16 + uxth r1, r1 + mov r5, r1 + mul r5, r4 + mul r1, r1 + mul r4, r4 + eor r6, r6 + add r1, r3 + adc r4, r6 + lsr r3, r5, #16 + lsl r5, r5, #16 + add r1, r5 + adc r4, r3 + add r1, r5 + adc r3, r4 + lsr r4, r2, #16 + uxth r2, r2 + mov r5, r2 + mul r5, r4 + mul r2, r2 + mul r4, r4 + lsr r6, r5, #16 + lsl r5, #16 + add r2, r5 + adc r4, r6 + add r5, r2 + adc r6, r4 + eor r4, r4 + mov r2, r1 + sub r1, r5 + sbc r2, r6 + sbc r4, r4 + add r1, r0 + adc r2, r3 + adc r3, r4 + eor r4, r4 + mov r6, r9 + add r0, r6 + adc r7, r1 + adc r2, r4 + adc r3, r4 + mov r1, r11 + mov r11, r0 + mov r0, r10 + mov r9, r2 + mov r10,r3 + //SQR64, in: r0, r1, out: r0-r3, used: r0-r6 + mov r2, r0 + eor r3, r3 + sub r2, r1 + sbc r3, r3 + eor r2, r3 + sub r2, r3 + lsr r3, r0, #16 + uxth r0, r0 + mov r4, r0 + mul r4, r3 + mul r0, r0 + mul r3, r3 + lsr r5, r4, #16 + lsl r4, #16 + add r0, r4 + adc r3, r5 + add r0, r4 + adc r3, r5 + lsr r4, r1, #16 + uxth r1, r1 + mov r5, r1 + mul r5, r4 + mul r1, r1 + mul r4, r4 + eor r6, r6 + add r1, r3 + adc r4, r6 + lsr r3, r5, #16 + lsl r5, r5, #16 + add r1, r5 + adc r4, r3 + add r1, r5 + adc r3, r4 + lsr r4, r2, #16 + uxth r2, r2 + mov r5, r2 + mul r5, r4 + mul r2, r2 + mul r4, r4 + lsr r6, r5, #16 + lsl r5, #16 + add r2, r5 + adc r4, r6 + add r5, r2 + adc r6, r4 + eor r4, r4 + mov r2, r1 + sub r1, r5 + sbc r2, r6 + sbc r4, r4 + add r1, r0 + adc r2, r3 + adc r3, r4 + mov r6, r11 + mov r4, r11 + mov r5, r7 + sub r6, r0 + sbc r7, r1 + sbc r4, r2 + sbc r5, r3 + eor r1, r1 + sbc r1, r1 + mov r2, r12 + mov r3, r8 + add r2, r6 + adc r3, r7 + mov r6, r9 + mov r7, r10 + adc r4, r6 + adc r5, r7 + adc r6, r1 + adc r7, r1 + //results r12, r8, r2-r7 + //////////END MIDDLE PART////////////////// + mvn r2, r2 + mvn r3, r3 + mvn r4, r4 + mvn r5, r5 + mvn r6, r6 + mvn r7, r7 + pop {r1} + push {r4-r7} + mov r4, #1 + asr r4, #1 + ldm r1!, {r4-r7} + mov r0, r12 + mov r12, r1 ////////ref + mov r1, r8 + mvn r0, r0 + mvn r1, r1 + adc r0, r4 + adc r1, r5 + adc r2, r6 + adc r3, r7 + eor r4, r4 + adc r4, r4 + mov r8, r4 //carry A --ini + mov r4, r12 + ldm r4, {r4-r7} + add r0, r4 + adc r1, r5 + adc r2, r6 + adc r3, r7 + mov r9, r4 + mov r4, r12 + stm r4!, {r0-r3} + mov r12, r4 + mov r4, r9 + pop {r0-r3} + adc r4, r0 + adc r5, r1 + adc r6, r2 + adc r7, r3 + eor r0, r0 + adc r0, r0 + mov r9, r0 //carry B --ini + mov r0, r8 + asr r0, #1 //carry A --end + pop {r0-r3} + adc r4, r0 + adc r5, r1 + adc r6, r2 + adc r7, r3 + mov r8, r0 + mov r0, r12 + stm r0!, {r4-r7} + mov r11, r0 + mov r0, r8 + eor r4, r4 + mov r5, r9 + adc r5, r4 //carry B --end + mvn r6, r4 + add r5, r6 + adc r6, r4 + add r0, r5 + adc r1, r6 + adc r2, r6 + adc r3, r6 + mov r7, r11 + stm r7!, {r0-r3} + + pop {r3-r6} + mov r8, r3 + mov r9, r4 + mov r10, r5 + mov r11, r6 + pop {r4-r7,pc} + bx lr + .size square256_asm, .-square256_asm diff --git a/deps/picoquic/picotls/deps/cifra/src/bitops.h b/deps/picoquic/picotls/deps/cifra/src/bitops.h new file mode 100644 index 0000000..f2ca308 --- /dev/null +++ b/deps/picoquic/picotls/deps/cifra/src/bitops.h @@ -0,0 +1,310 @@ +/* + * cifra - embedded cryptography library + * Written in 2014 by Joseph Birr-Pixton + * + * To the extent possible under law, the author(s) have dedicated all + * copyright and related and neighboring rights to this software to the + * public domain worldwide. This software is distributed without any + * warranty. + * + * You should have received a copy of the CC0 Public Domain Dedication + * along with this software. If not, see + * . + */ + +#ifndef BITOPS_H +#define BITOPS_H + +#include +#include + +#ifdef _WINDOWS +#include +#endif + +/* Assorted bitwise and common operations used in ciphers. */ + +/** Circularly rotate right x by n bits. + * 0 > n > 32. */ +static inline uint32_t rotr32(uint32_t x, unsigned n) +{ + return (x >> n) | (x << (32 - n)); +} + +/** Circularly rotate left x by n bits. + * 0 > n > 32. */ +static inline uint32_t rotl32(uint32_t x, unsigned n) +{ + return (x << n) | (x >> (32 - n)); +} + +/** Circularly rotate right x by n bits. + * 0 > n > 64. */ +static inline uint64_t rotr64(uint64_t x, unsigned n) +{ + return (x >> n) | (x << (64 - n)); +} + +/** Circularly rotate left x by n bits. + * 0 > n > 64. */ +static inline uint64_t rotl64(uint64_t x, unsigned n) +{ + return (x << n) | (x >> (64 - n)); +} + +/** Read 4 bytes from buf, as a 32-bit big endian quantity. */ +static inline uint32_t read32_be(const uint8_t buf[4]) +{ + return (buf[0] << 24) | + (buf[1] << 16) | + (buf[2] << 8) | + (buf[3]); +} + +/** Read 4 bytes from buf, as a 32-bit little endian quantity. */ +static inline uint32_t read32_le(const uint8_t buf[4]) +{ + return (buf[3] << 24) | + (buf[2] << 16) | + (buf[1] << 8) | + (buf[0]); +} + +/** Read 8 bytes from buf, as a 64-bit big endian quantity. */ +static inline uint64_t read64_be(const uint8_t buf[8]) +{ + uint32_t hi = read32_be(buf), + lo = read32_be(buf + 4); + return ((uint64_t)hi) << 32 | + lo; +} + +/** Read 8 bytes from buf, as a 64-bit little endian quantity. */ +static inline uint64_t read64_le(const uint8_t buf[8]) +{ + uint32_t hi = read32_le(buf + 4), + lo = read32_le(buf); + return ((uint64_t)hi) << 32 | + lo; +} + +/** Encode v as a 32-bit big endian quantity into buf. */ +static inline void write32_be(uint32_t v, uint8_t buf[4]) +{ + *buf++ = (v >> 24) & 0xff; + *buf++ = (v >> 16) & 0xff; + *buf++ = (v >> 8) & 0xff; + *buf = v & 0xff; +} + +/** Encode v as a 32-bit little endian quantity into buf. */ +static inline void write32_le(uint32_t v, uint8_t buf[4]) +{ + *buf++ = v & 0xff; + *buf++ = (v >> 8) & 0xff; + *buf++ = (v >> 16) & 0xff; + *buf = (v >> 24) & 0xff; +} + +/** Encode v as a 64-bit big endian quantity into buf. */ +static inline void write64_be(uint64_t v, uint8_t buf[8]) +{ + *buf++ = (v >> 56) & 0xff; + *buf++ = (v >> 48) & 0xff; + *buf++ = (v >> 40) & 0xff; + *buf++ = (v >> 32) & 0xff; + *buf++ = (v >> 24) & 0xff; + *buf++ = (v >> 16) & 0xff; + *buf++ = (v >> 8) & 0xff; + *buf = v & 0xff; +} + +/** Encode v as a 64-bit little endian quantity into buf. */ +static inline void write64_le(uint64_t v, uint8_t buf[8]) +{ + *buf++ = v & 0xff; + *buf++ = (v >> 8) & 0xff; + *buf++ = (v >> 16) & 0xff; + *buf++ = (v >> 24) & 0xff; + *buf++ = (v >> 32) & 0xff; + *buf++ = (v >> 40) & 0xff; + *buf++ = (v >> 48) & 0xff; + *buf = (v >> 56) & 0xff; +} + +/** out = in ^ b8. + * out and in may alias. */ +static inline void xor_b8(uint8_t *out, const uint8_t *in, uint8_t b8, size_t len) +{ + size_t i; + for (i = 0; i < len; i++) + out[i] = in[i] ^ b8; +} + +/** out = x ^ y. + * out, x and y may alias. */ +static inline void xor_bb(uint8_t *out, const uint8_t *x, const uint8_t *y, size_t len) +{ + size_t i; + for (i = 0; i < len; i++) + out[i] = x[i] ^ y[i]; +} + +/* out ^= x + * out and x may alias. */ +static inline void xor_words(uint32_t *out, const uint32_t *x, size_t nwords) +{ + size_t i; + for (i = 0; i < nwords; i++) + out[i] ^= x[i]; +} + +/** Produce 0xffffffff if x == y, zero otherwise, without branching. */ +static inline uint32_t mask_u32(uint32_t x, uint32_t y) +{ + uint32_t diff = x ^ y; + uint32_t diff_is_zero = ~diff & (diff - 1); + return (uint32_t)(-(int32_t)(diff_is_zero >> 31)); +} + +/** Product 0xff if x == y, zero otherwise, without branching. */ +static inline uint8_t mask_u8(uint32_t x, uint32_t y) +{ + uint32_t diff = x ^ y; + uint8_t diff_is_zero = ~diff & (diff - 1); + return - (diff_is_zero >> 7); +} + +/** Select the ith entry from the given table of n values, in a side channel-silent + * way. */ +static inline uint32_t select_u32(uint32_t i, volatile const uint32_t *tab, uint32_t n) +{ + uint32_t r = 0, ii; + + for (ii = 0; ii < n; ii++) + { + uint32_t mask = mask_u32(i, ii); + r = (r & ~mask) | (tab[ii] & mask); + } + + return r; +} + +/** Select the ith entry from the given table of n values, in a side channel-silent + * way. */ +static inline uint8_t select_u8(uint32_t i, volatile const uint8_t *tab, uint32_t n) +{ + uint8_t r = 0; + uint32_t ii; + + for (ii = 0; ii < n; ii++) + { + uint8_t mask = mask_u8(i, ii); + r = (r & ~mask) | (tab[ii] & mask); + } + + return r; +} + +/** Select the ath, bth, cth and dth entries from the given table of n values, + * placing the results into a, b, c and d. */ +static inline void select_u8x4(uint8_t *a, uint8_t *b, uint8_t *c, uint8_t *d, + volatile const uint8_t *tab, uint32_t n) +{ + uint8_t ra = 0, + rb = 0, + rc = 0, + rd = 0; + uint8_t mask; + uint32_t i; + + for (i = 0; i < n; i++) + { + uint8_t item = tab[i]; + + mask = mask_u8(*a, i); ra = (ra & ~mask) | (item & mask); + mask = mask_u8(*b, i); rb = (rb & ~mask) | (item & mask); + mask = mask_u8(*c, i); rc = (rc & ~mask) | (item & mask); + mask = mask_u8(*d, i); rd = (rd & ~mask) | (item & mask); + } + + *a = ra; + *b = rb; + *c = rc; + *d = rd; +} + +/** out ^= if0 or if1, depending on the value of bit. */ +static inline void select_xor128(uint32_t out[4], + const uint32_t if0[4], + const uint32_t if1[4], + uint8_t bit) +{ + uint32_t mask1 = mask_u32(bit, 1); + uint32_t mask0 = ~mask1; + + out[0] ^= (if0[0] & mask0) | (if1[0] & mask1); + out[1] ^= (if0[1] & mask0) | (if1[1] & mask1); + out[2] ^= (if0[2] & mask0) | (if1[2] & mask1); + out[3] ^= (if0[3] & mask0) | (if1[3] & mask1); +} + +/** Increments the integer stored at v (of non-zero length len) + * with the least significant byte first. */ +static inline void incr_le(uint8_t *v, size_t len) +{ + size_t i = 0; + while (1) + { + if (++v[i] != 0) + return; + i++; + if (i == len) + return; + } +} + +/** Increments the integer stored at v (of non-zero length len) + * with the most significant byte last. */ +static inline void incr_be(uint8_t *v, size_t len) +{ + len--; + while (1) + { + if (++v[len] != 0) + return; + if (len == 0) + return; + len--; + } +} + +/** Copies len bytes from in to out, with in shifted left by offset bits + * to the right. */ +static inline void copy_bytes_unaligned(uint8_t *out, const uint8_t *in, size_t len, uint8_t offset) +{ + uint8_t byte_off = offset / 8; + uint8_t bit_off = offset & 7; + uint8_t rmask = (1 << bit_off) - 1; + uint8_t lmask = ~rmask; + size_t i; + + for (i = 0; i < len; i++) + { + out[i] = (in[i + byte_off] << bit_off) & lmask; + out[i] |= (in[i + byte_off + 1] >> (8 - bit_off)) & rmask; + } +} + +static inline uint32_t count_trailing_zeroes(uint32_t x) +{ +#ifdef _WINDOWS + uint32_t r = 0; + _BitScanReverse(&r, x); + return (31 - r); +#else + return (uint32_t) __builtin_ctzl(x); +#endif +} + +#endif diff --git a/deps/picoquic/picotls/deps/cifra/src/blockwise.c b/deps/picoquic/picotls/deps/cifra/src/blockwise.c new file mode 100644 index 0000000..182c8c5 --- /dev/null +++ b/deps/picoquic/picotls/deps/cifra/src/blockwise.c @@ -0,0 +1,195 @@ +/* + * cifra - embedded cryptography library + * Written in 2014 by Joseph Birr-Pixton + * + * To the extent possible under law, the author(s) have dedicated all + * copyright and related and neighboring rights to this software to the + * public domain worldwide. This software is distributed without any + * warranty. + * + * You should have received a copy of the CC0 Public Domain Dedication + * along with this software. If not, see + * . + */ + +#include "blockwise.h" +#include "bitops.h" +#include "handy.h" +#include "tassert.h" + +#include + +void cf_blockwise_accumulate(uint8_t *partial, size_t *npartial, size_t nblock, + const void *inp, size_t nbytes, + cf_blockwise_in_fn process, + void *ctx) +{ + cf_blockwise_accumulate_final(partial, npartial, nblock, + inp, nbytes, + process, process, ctx); +} + +void cf_blockwise_accumulate_final(uint8_t *partial, size_t *npartial, size_t nblock, + const void *inp, size_t nbytes, + cf_blockwise_in_fn process, + cf_blockwise_in_fn process_final, + void *ctx) +{ + const uint8_t *bufin = inp; + assert(partial && *npartial < nblock); + assert(inp || !nbytes); + assert(process && ctx); + + /* If we have partial data, copy in to buffer. */ + if (*npartial && nbytes) + { + size_t space = nblock - *npartial; + size_t taken = MIN(space, nbytes); + + memcpy(partial + *npartial, bufin, taken); + + bufin += taken; + nbytes -= taken; + *npartial += taken; + + /* If that gives us a full block, process it. */ + if (*npartial == nblock) + { + if (nbytes == 0) + process_final(ctx, partial); + else + process(ctx, partial); + *npartial = 0; + } + } + + /* now nbytes < nblock or *npartial == 0. */ + + /* If we have a full block of data, process it directly. */ + while (nbytes >= nblock) + { + /* Partial buffer must be empty, or we're ignoring extant data */ + assert(*npartial == 0); + + if (nbytes == nblock) + process_final(ctx, bufin); + else + process(ctx, bufin); + bufin += nblock; + nbytes -= nblock; + } + + /* Finally, if we have remaining data, buffer it. */ + while (nbytes) + { + size_t space = nblock - *npartial; + size_t taken = MIN(space, nbytes); + + memcpy(partial + *npartial, bufin, taken); + + bufin += taken; + nbytes -= taken; + *npartial += taken; + + /* If we started with *npartial, we must have copied it + * in first. */ + assert(*npartial < nblock); + } +} + +void cf_blockwise_xor(uint8_t *partial, size_t *npartial, size_t nblock, + const void *inp, void *outp, size_t nbytes, + cf_blockwise_out_fn process, void *ctx) +{ + const uint8_t *inb = inp; + uint8_t *outb = outp; + + assert(partial && *npartial < nblock); + assert(inp || !nbytes); + assert(process && ctx); + + while (nbytes) + { + /* If we're out of material, and need more, produce a block. */ + if (*npartial == 0) + { + process(ctx, partial); + *npartial = nblock; + } + + size_t offset = nblock - *npartial; + size_t taken = MIN(*npartial, nbytes); + xor_bb(outb, inb, partial + offset, taken); + *npartial -= taken; + nbytes -= taken; + outb += taken; + inb += taken; + } +} + +void cf_blockwise_acc_byte(uint8_t *partial, size_t *npartial, + size_t nblock, + uint8_t byte, size_t nbytes, + cf_blockwise_in_fn process, + void *ctx) +{ + /* only memset the whole of the block once */ + int filled = 0; + + while (nbytes) + { + size_t start = *npartial; + size_t count = MIN(nbytes, nblock - start); + + if (!filled) + memset(partial + start, byte, count); + + if (start == 0 && count == nblock) + filled = 1; + + if (start + count == nblock) + { + process(ctx, partial); + *npartial = 0; + } else { + *npartial += count; + } + + nbytes -= count; + } +} + +void cf_blockwise_acc_pad(uint8_t *partial, size_t *npartial, + size_t nblock, + uint8_t fbyte, uint8_t mbyte, uint8_t lbyte, + size_t nbytes, + cf_blockwise_in_fn process, + void *ctx) +{ + + switch (nbytes) + { + case 0: break; + case 1: fbyte ^= lbyte; + cf_blockwise_accumulate(partial, npartial, nblock, &fbyte, 1, process, ctx); + break; + case 2: + cf_blockwise_accumulate(partial, npartial, nblock, &fbyte, 1, process, ctx); + cf_blockwise_accumulate(partial, npartial, nblock, &lbyte, 1, process, ctx); + break; + default: + cf_blockwise_accumulate(partial, npartial, nblock, &fbyte, 1, process, ctx); + + /* If the middle and last bytes differ, then process the last byte separately. + * Otherwise, just extend the middle block size. */ + if (lbyte != mbyte) + { + cf_blockwise_acc_byte(partial, npartial, nblock, mbyte, nbytes - 2, process, ctx); + cf_blockwise_accumulate(partial, npartial, nblock, &lbyte, 1, process, ctx); + } else { + cf_blockwise_acc_byte(partial, npartial, nblock, mbyte, nbytes - 1, process, ctx); + } + + break; + } +} diff --git a/deps/picoquic/picotls/deps/cifra/src/blockwise.h b/deps/picoquic/picotls/deps/cifra/src/blockwise.h new file mode 100644 index 0000000..a20ff95 --- /dev/null +++ b/deps/picoquic/picotls/deps/cifra/src/blockwise.h @@ -0,0 +1,147 @@ +/* + * cifra - embedded cryptography library + * Written in 2014 by Joseph Birr-Pixton + * + * To the extent possible under law, the author(s) have dedicated all + * copyright and related and neighboring rights to this software to the + * public domain worldwide. This software is distributed without any + * warranty. + * + * You should have received a copy of the CC0 Public Domain Dedication + * along with this software. If not, see + * . + */ + +#ifndef BLOCKWISE_H +#define BLOCKWISE_H + +#include +#include + +/* Processing function for cf_blockwise_accumulate. */ +typedef void (*cf_blockwise_in_fn)(void *ctx, const uint8_t *data); + +/* Processing function for cf_blockwise_xor. */ +typedef void (*cf_blockwise_out_fn)(void *ctx, uint8_t *data); + +/* This function manages the common abstraction of accumulating input in + * a buffer, and processing it when a full block is available. + * + * partial is the buffer (maintained by the caller) + * on entry, npartial is the currently valid count of used bytes on + * the front of partial. + * on exit, npartial is updated to reflect the status of partial. + * nblock is the blocksize to accumulate -- partial must be at least + * this long! + * input is the new data to process, of length nbytes. + * process is the processing function, passed ctx and a pointer + * to the data to process (always exactly nblock bytes long!) + * which may not neccessarily be the same as partial. + */ +void cf_blockwise_accumulate(uint8_t *partial, size_t *npartial, + size_t nblock, + const void *input, size_t nbytes, + cf_blockwise_in_fn process, + void *ctx); + +/* This function manages the common abstraction of accumulating input in + * a buffer, and processing it when a full block is available. + * This version supports calling a different processing function for + * the last block. + * + * partial is the buffer (maintained by the caller) + * on entry, npartial is the currently valid count of used bytes on + * the front of partial. + * on exit, npartial is updated to reflect the status of partial. + * nblock is the blocksize to accumulate -- partial must be at least + * this long! + * input is the new data to process, of length nbytes. + * process is the processing function, passed ctx and a pointer + * to the data to process (always exactly nblock bytes long!) + * which may not neccessarily be the same as partial. + * process_final is called last (but may not be called at all if + * all input is buffered). + */ +void cf_blockwise_accumulate_final(uint8_t *partial, size_t *npartial, + size_t nblock, + const void *input, size_t nbytes, + cf_blockwise_in_fn process, + cf_blockwise_in_fn process_final, + void *ctx); + +/* This function manages XORing an input stream with a keystream + * to produce an output stream. The keystream is produced in blocks + * (ala a block cipher in counter mode). + * + * partial is the keystream buffer (maintained by the caller) + * on entry, *npartial is the currently valid count of bytes in partial: + * unused bytes are at the *end*. So *npartial = 4 means the last four + * bytes of partial are usable as keystream. + * on exit, npartial is updated to reflect the new state of partial. + * nblock is the blocksize to accumulate -- partial must be at least + * this long! + * input is the new data to process, of length nbytes. + * output is where to write input xored with the keystream -- also length + * nbytes. + * process is the processing function, passed ctx and partial which it + * should fill with fresh key stream. + */ +void cf_blockwise_xor(uint8_t *partial, size_t *npartial, + size_t nblock, + const void *input, void *output, size_t nbytes, + cf_blockwise_out_fn newblock, + void *ctx); + +/* This function processes a single byte a number of times. It's useful + * for padding, and more efficient than calling cf_blockwise_accumulate + * a bunch of times. + * + * partial is the buffer (maintained by the caller) + * on entry, npartial is the currently valid count of used bytes on + * the front of partial. + * on exit, npartial is updated to reflect the status of partial. + * nblock is the blocksize to accumulate -- partial must be at least + * this long! + * process is the processing function, passed ctx and a pointer + * to the data to process (always exactly nblock bytes long!) + * which may not neccessarily be the same as partial. + * byte is the byte to process, nbytes times. + */ +void cf_blockwise_acc_byte(uint8_t *partial, size_t *npartial, + size_t nblock, + uint8_t byte, size_t nbytes, + cf_blockwise_in_fn process, + void *ctx); + +/* This function attempts to process patterns of bytes common in + * block cipher padding. + * + * This takes three bytes: + * - a first byte, fbyte, + * - a middle byte, mbyte, + * - a last byte, lbyte. + * + * If nbytes is zero, nothing happens. + * If nbytes is one, the byte fbyte ^ lbyte is processed. + * If nbytes is two, the fbyte then lbyte are processed. + * If nbytes is three or more, fbyte, then one or more mbytes, then fbyte + * is processed. + * + * partial is the buffer (maintained by the caller) + * on entry, npartial is the currently valid count of used bytes on + * the front of partial. + * on exit, npartial is updated to reflect the status of partial. + * nblock is the blocksize to accumulate -- partial must be at least + * this long! + * process is the processing function, passed ctx and a pointer + * to the data to process (always exactly nblock bytes long!) + * which may not neccessarily be the same as partial. + */ +void cf_blockwise_acc_pad(uint8_t *partial, size_t *npartial, + size_t nblock, + uint8_t fbyte, uint8_t mbyte, uint8_t lbyte, + size_t nbytes, + cf_blockwise_in_fn process, + void *ctx); + +#endif diff --git a/deps/picoquic/picotls/deps/cifra/src/cbcmac.c b/deps/picoquic/picotls/deps/cifra/src/cbcmac.c new file mode 100644 index 0000000..f0dfe87 --- /dev/null +++ b/deps/picoquic/picotls/deps/cifra/src/cbcmac.c @@ -0,0 +1,79 @@ +/* + * cifra - embedded cryptography library + * Written in 2014 by Joseph Birr-Pixton + * + * To the extent possible under law, the author(s) have dedicated all + * copyright and related and neighboring rights to this software to the + * public domain worldwide. This software is distributed without any + * warranty. + * + * You should have received a copy of the CC0 Public Domain Dedication + * along with this software. If not, see + * . + */ + +#include "handy.h" +#include "prp.h" +#include "modes.h" +#include "bitops.h" +#include "blockwise.h" +#include "gf128.h" +#include "tassert.h" + +#include + +void cf_cbcmac_stream_init(cf_cbcmac_stream *ctx, const cf_prp *prp, void *prpctx) +{ + memset(ctx, 0, sizeof *ctx); + ctx->prp = prp; + ctx->prpctx = prpctx; + cf_cbcmac_stream_reset(ctx); +} + +void cf_cbcmac_stream_reset(cf_cbcmac_stream *ctx) +{ + uint8_t iv_zero[CF_MAXBLOCK] = { 0 }; + cf_cbc_init(&ctx->cbc, ctx->prp, ctx->prpctx, iv_zero); + mem_clean(ctx->buffer, sizeof ctx->buffer); + ctx->used = 0; +} + +static void cbcmac_process(void *vctx, const uint8_t *block) +{ + cf_cbcmac_stream *ctx = vctx; + uint8_t output[CF_MAXBLOCK]; + cf_cbc_encrypt(&ctx->cbc, block, output, 1); +} + +void cf_cbcmac_stream_update(cf_cbcmac_stream *ctx, const uint8_t *data, size_t len) +{ + cf_blockwise_accumulate(ctx->buffer, &ctx->used, ctx->prp->blocksz, + data, len, + cbcmac_process, + ctx); +} + +void cf_cbcmac_stream_finish_block_zero(cf_cbcmac_stream *ctx) +{ + if (ctx->used == 0) + return; + + memset(ctx->buffer + ctx->used, 0, ctx->prp->blocksz - ctx->used); + cbcmac_process(ctx, ctx->buffer); + ctx->used = 0; +} + +void cf_cbcmac_stream_nopad_final(cf_cbcmac_stream *ctx, uint8_t out[CF_MAXBLOCK]) +{ + assert(ctx->used == 0); + memcpy(out, ctx->cbc.block, ctx->prp->blocksz); +} + +void cf_cbcmac_stream_pad_final(cf_cbcmac_stream *ctx, uint8_t out[CF_MAXBLOCK]) +{ + uint8_t npad = ctx->prp->blocksz - ctx->used; + cf_blockwise_acc_byte(ctx->buffer, &ctx->used, ctx->prp->blocksz, + npad, npad, + cbcmac_process, ctx); + cf_cbcmac_stream_nopad_final(ctx, out); +} diff --git a/deps/picoquic/picotls/deps/cifra/src/ccm.c b/deps/picoquic/picotls/deps/cifra/src/ccm.c new file mode 100644 index 0000000..7ef87fc --- /dev/null +++ b/deps/picoquic/picotls/deps/cifra/src/ccm.c @@ -0,0 +1,193 @@ +/* + * cifra - embedded cryptography library + * Written in 2014 by Joseph Birr-Pixton + * + * To the extent possible under law, the author(s) have dedicated all + * copyright and related and neighboring rights to this software to the + * public domain worldwide. This software is distributed without any + * warranty. + * + * You should have received a copy of the CC0 Public Domain Dedication + * along with this software. If not, see + * . + */ + +#include "handy.h" +#include "prp.h" +#include "modes.h" +#include "tassert.h" + +#include + +#define CCM_ADATA_PRESENT 0x40 + +static void write_be(uint8_t *out, size_t value, size_t bytes) +{ + while (bytes) + { + out[bytes - 1] = value & 0xff; + value >>= 8; + bytes--; + } + + assert(value == 0); /* or we couldn't encode the value. */ +} + +static void zero_pad(cf_cbcmac_stream *cm) +{ + cf_cbcmac_stream_finish_block_zero(cm); +} + +/* nb. block is general workspace. */ +static void add_aad(cf_cbcmac_stream *cm, uint8_t block[CF_MAXBLOCK], + const uint8_t *header, size_t nheader) +{ + assert(nheader <= 0xffffffff); /* we don't support 64 bit lengths. */ + + /* Add length using stupidly complicated rules. */ + if (nheader < 0xff00) + { + write_be(block, nheader, 2); + cf_cbcmac_stream_update(cm, block, 2); + } else { + write_be(block, 0xfffe, 2); + write_be(block + 2, nheader, 4); + cf_cbcmac_stream_update(cm, block, 6); + } + + cf_cbcmac_stream_update(cm, header, nheader); + zero_pad(cm); +} + +static void add_block0(cf_cbcmac_stream *cm, + uint8_t block[CF_MAXBLOCK], size_t nblock, + const uint8_t *nonce, size_t nnonce, + size_t L, size_t nplain, + size_t nheader, size_t ntag) +{ + /* Construct first block B_0. */ + block[0] = ((nheader == 0) ? 0x00 : CCM_ADATA_PRESENT) | + ((ntag - 2) / 2) << 3 | + (L - 1); + memcpy(block + 1, nonce, nnonce); + write_be(block + 1 + nnonce, nplain, L); + + cf_cbcmac_stream_update(cm, block, nblock); +} + +static void build_ctr_nonce(uint8_t ctr_nonce[CF_MAXBLOCK], + size_t L, + const uint8_t *nonce, size_t nnonce) +{ + ctr_nonce[0] = (L - 1); + memcpy(ctr_nonce + 1, nonce, nnonce); + memset(ctr_nonce + 1 + nnonce, 0, L); +} + +void cf_ccm_encrypt(const cf_prp *prp, void *prpctx, + const uint8_t *plain, size_t nplain, size_t L, + const uint8_t *header, size_t nheader, + const uint8_t *nonce, size_t nnonce, + uint8_t *cipher, + uint8_t *tag, size_t ntag) +{ + uint8_t block[CF_MAXBLOCK]; + + assert(ntag >= 4 && ntag <= 16 && ntag % 2 == 0); + assert(L >= 2 && L <= 8); + assert(nnonce == prp->blocksz - L - 1); + + cf_cbcmac_stream cm; + cf_cbcmac_stream_init(&cm, prp, prpctx); + + /* Add first block. */ + add_block0(&cm, block, prp->blocksz, + nonce, nnonce, + L, nplain, nheader, ntag); + + /* Add AAD with length prefix, if present. */ + if (nheader) + add_aad(&cm, block, header, nheader); + + /* Add message. */ + cf_cbcmac_stream_update(&cm, plain, nplain); + zero_pad(&cm); + + /* Finish tag. */ + cf_cbcmac_stream_nopad_final(&cm, block); + + /* Start encryption. */ + /* Construct A_0 */ + uint8_t ctr_nonce[CF_MAXBLOCK]; + build_ctr_nonce(ctr_nonce, L, nonce, nnonce); + + cf_ctr ctr; + cf_ctr_init(&ctr, prp, prpctx, ctr_nonce); + cf_ctr_custom_counter(&ctr, prp->blocksz - L, L); + + /* Encrypt tag first. */ + cf_ctr_cipher(&ctr, block, block, prp->blocksz); + memcpy(tag, block, ntag); + + /* Then encrypt message. */ + cf_ctr_cipher(&ctr, plain, cipher, nplain); +} + +int cf_ccm_decrypt(const cf_prp *prp, void *prpctx, + const uint8_t *cipher, size_t ncipher, size_t L, + const uint8_t *header, size_t nheader, + const uint8_t *nonce, size_t nnonce, + const uint8_t *tag, size_t ntag, + uint8_t *plain) +{ + uint8_t block[CF_MAXBLOCK]; + + assert(ntag >= 4 && ntag <= 16 && ntag % 2 == 0); + assert(L >= 2 && L <= 8); + assert(nnonce == prp->blocksz - L - 1); + + uint8_t ctr_nonce[CF_MAXBLOCK]; + build_ctr_nonce(ctr_nonce, L, nonce, nnonce); + + cf_ctr ctr; + cf_ctr_init(&ctr, prp, prpctx, ctr_nonce); + cf_ctr_custom_counter(&ctr, prp->blocksz - L, L); + + /* Decrypt tag. */ + uint8_t plain_tag[CF_MAXBLOCK]; + cf_ctr_cipher(&ctr, tag, plain_tag, ntag); + cf_ctr_discard_block(&ctr); + + /* Decrypt message. */ + cf_ctr_cipher(&ctr, cipher, plain, ncipher); + + cf_cbcmac_stream cm; + cf_cbcmac_stream_init(&cm, prp, prpctx); + + /* Add first block. */ + add_block0(&cm, block, prp->blocksz, + nonce, nnonce, + L, ncipher, nheader, ntag); + + if (nheader) + add_aad(&cm, block, header, nheader); + + cf_cbcmac_stream_update(&cm, plain, ncipher); + zero_pad(&cm); + + /* Finish tag. */ + cf_cbcmac_stream_nopad_final(&cm, block); + + int err = 0; + + if (!mem_eq(block, plain_tag, ntag)) + { + err = 1; + mem_clean(plain, ncipher); + } + + mem_clean(block, sizeof block); + mem_clean(plain_tag, sizeof plain_tag); + return err; +} + diff --git a/deps/picoquic/picotls/deps/cifra/src/cf_config.h b/deps/picoquic/picotls/deps/cifra/src/cf_config.h new file mode 100644 index 0000000..ceb7e8d --- /dev/null +++ b/deps/picoquic/picotls/deps/cifra/src/cf_config.h @@ -0,0 +1,59 @@ +/* + * cifra - embedded cryptography library + * Written in 2014 by Joseph Birr-Pixton + * + * To the extent possible under law, the author(s) have dedicated all + * copyright and related and neighboring rights to this software to the + * public domain worldwide. This software is distributed without any + * warranty. + * + * You should have received a copy of the CC0 Public Domain Dedication + * along with this software. If not, see + * . + */ + +#ifndef CF_CONFIG_H +#define CF_CONFIG_H + +/** + * Library configuration + * ===================== + */ + +/* .. c:macro:: CF_SIDE_CHANNEL_PROTECTION + * Define this as 1 if you need all available side channel protections. + * **This option may alter the ABI**. + * + * This has a non-trivial performance penalty. Where a + * side-channel free option is cheap or free (like checking + * a MAC) this is always done in a side-channel free way. + * + * The default is **on** for all available protections. + */ +#ifndef CF_SIDE_CHANNEL_PROTECTION +# define CF_SIDE_CHANNEL_PROTECTION 1 +#endif + +/* .. c:macro:: CF_TIME_SIDE_CHANNEL_PROTECTION + * Define this as 1 if you need timing/branch prediction side channel + * protection. + * + * You probably want this. The default is on. */ +#ifndef CF_TIME_SIDE_CHANNEL_PROTECTION +# define CF_TIME_SIDE_CHANNEL_PROTECTION CF_SIDE_CHANNEL_PROTECTION +#endif + +/* .. c:macro:: CF_CACHE_SIDE_CHANNEL_PROTECTION + * Define this as 1 if you need cache side channel protection. + * + * If you have a microcontroller with no cache, you can turn this off + * without negative effects. + * + * The default is on. This will have some performance impact, + * especially on AES. + */ +#ifndef CF_CACHE_SIDE_CHANNEL_PROTECTION +# define CF_CACHE_SIDE_CHANNEL_PROTECTION CF_SIDE_CHANNEL_PROTECTION +#endif + +#endif diff --git a/deps/picoquic/picotls/deps/cifra/src/chacha20.c b/deps/picoquic/picotls/deps/cifra/src/chacha20.c new file mode 100644 index 0000000..7a3bf38 --- /dev/null +++ b/deps/picoquic/picotls/deps/cifra/src/chacha20.c @@ -0,0 +1,161 @@ +/* + * cifra - embedded cryptography library + * Written in 2014 by Joseph Birr-Pixton + * + * To the extent possible under law, the author(s) have dedicated all + * copyright and related and neighboring rights to this software to the + * public domain worldwide. This software is distributed without any + * warranty. + * + * You should have received a copy of the CC0 Public Domain Dedication + * along with this software. If not, see + * . + */ + +#include "bitops.h" +#include "salsa20.h" +#include "blockwise.h" +#include "tassert.h" + +#include +#include + +void cf_chacha20_core(const uint8_t key0[16], + const uint8_t key1[16], + const uint8_t nonce[16], + const uint8_t constant[16], + uint8_t out[64]) +{ + uint32_t z0, z1, z2, z3, z4, z5, z6, z7, + z8, z9, za, zb, zc, zd, ze, zf; + + uint32_t x0 = z0 = read32_le(constant + 0), + x1 = z1 = read32_le(constant + 4), + x2 = z2 = read32_le(constant + 8), + x3 = z3 = read32_le(constant + 12), + x4 = z4 = read32_le(key0 + 0), + x5 = z5 = read32_le(key0 + 4), + x6 = z6 = read32_le(key0 + 8), + x7 = z7 = read32_le(key0 + 12), + x8 = z8 = read32_le(key1 + 0), + x9 = z9 = read32_le(key1 + 4), + xa = za = read32_le(key1 + 8), + xb = zb = read32_le(key1 + 12), + xc = zc = read32_le(nonce + 0), + xd = zd = read32_le(nonce + 4), + xe = ze = read32_le(nonce + 8), + xf = zf = read32_le(nonce + 12); + +#define QUARTER(a, b, c, d) \ + a += b; d = rotl32(d ^ a, 16); \ + c += d; b = rotl32(b ^ c, 12); \ + a += b; d = rotl32(d ^ a, 8); \ + c += d; b = rotl32(b ^ c, 7); + + int i; + for (i = 0; i < 10; i++) + { + QUARTER(z0, z4, z8, zc); + QUARTER(z1, z5, z9, zd); + QUARTER(z2, z6, za, ze); + QUARTER(z3, z7, zb, zf); + QUARTER(z0, z5, za, zf); + QUARTER(z1, z6, zb, zc); + QUARTER(z2, z7, z8, zd); + QUARTER(z3, z4, z9, ze); + } + + x0 += z0; + x1 += z1; + x2 += z2; + x3 += z3; + x4 += z4; + x5 += z5; + x6 += z6; + x7 += z7; + x8 += z8; + x9 += z9; + xa += za; + xb += zb; + xc += zc; + xd += zd; + xe += ze; + xf += zf; + + write32_le(x0, out + 0); + write32_le(x1, out + 4); + write32_le(x2, out + 8); + write32_le(x3, out + 12); + write32_le(x4, out + 16); + write32_le(x5, out + 20); + write32_le(x6, out + 24); + write32_le(x7, out + 28); + write32_le(x8, out + 32); + write32_le(x9, out + 36); + write32_le(xa, out + 40); + write32_le(xb, out + 44); + write32_le(xc, out + 48); + write32_le(xd, out + 52); + write32_le(xe, out + 56); + write32_le(xf, out + 60); +} + +static const uint8_t *chacha20_tau = (const uint8_t *) "expand 16-byte k"; +static const uint8_t *chacha20_sigma = (const uint8_t *) "expand 32-byte k"; + +static void set_key(cf_chacha20_ctx *ctx, const uint8_t *key, size_t nkey) +{ + switch (nkey) + { + case 16: + memcpy(ctx->key0, key, 16); + memcpy(ctx->key1, key, 16); + ctx->constant = chacha20_tau; + break; + case 32: + memcpy(ctx->key0, key, 16); + memcpy(ctx->key1, key + 16, 16); + ctx->constant = chacha20_sigma; + break; + default: + abort(); + } +} + +void cf_chacha20_init(cf_chacha20_ctx *ctx, const uint8_t *key, size_t nkey, const uint8_t nonce[8]) +{ + set_key(ctx, key, nkey); + memset(ctx->nonce, 0, sizeof ctx->nonce); + memcpy(ctx->nonce + 8, nonce, 8); + ctx->nblock = 0; + ctx->ncounter = 8; +} + +void cf_chacha20_init_custom(cf_chacha20_ctx *ctx, const uint8_t *key, size_t nkey, + const uint8_t nonce[16], size_t ncounter) +{ + assert(ncounter > 0); + set_key(ctx, key, nkey); + memcpy(ctx->nonce, nonce, sizeof ctx->nonce); + ctx->nblock = 0; + ctx->ncounter = ncounter; +} + +static void cf_chacha20_next_block(void *vctx, uint8_t *out) +{ + cf_chacha20_ctx *ctx = vctx; + cf_chacha20_core(ctx->key0, + ctx->key1, + ctx->nonce, + ctx->constant, + out); + incr_le(ctx->nonce, ctx->ncounter); +} + +void cf_chacha20_cipher(cf_chacha20_ctx *ctx, const uint8_t *input, uint8_t *output, size_t bytes) +{ + cf_blockwise_xor(ctx->block, &ctx->nblock, 64, + input, output, bytes, + cf_chacha20_next_block, + ctx); +} diff --git a/deps/picoquic/picotls/deps/cifra/src/chacha20poly1305.c b/deps/picoquic/picotls/deps/cifra/src/chacha20poly1305.c new file mode 100644 index 0000000..0aef725 --- /dev/null +++ b/deps/picoquic/picotls/deps/cifra/src/chacha20poly1305.c @@ -0,0 +1,148 @@ +/* + * cifra - embedded cryptography library + * Written in 2014 by Joseph Birr-Pixton + * + * To the extent possible under law, the author(s) have dedicated all + * copyright and related and neighboring rights to this software to the + * public domain worldwide. This software is distributed without any + * warranty. + * + * You should have received a copy of the CC0 Public Domain Dedication + * along with this software. If not, see + * . + */ + +#include "chacha20poly1305.h" +#include "salsa20.h" +#include "poly1305.h" +#include "bitops.h" +#include "handy.h" + +#define ENCRYPT 1 +#define DECRYPT 0 + +#define SUCCESS 0 +#define FAILURE 1 + +static int process(const uint8_t key[32], + const uint8_t nonce[12], + const uint8_t *header, size_t nheader, + const uint8_t *input, size_t nbytes, + uint8_t *output, + int mode, + uint8_t tag[16]) +{ + /* First, generate the Poly1305 key by running ChaCha20 with the + * given key and a zero counter. The first half of the + * 64-byte output is the key. */ + uint8_t fullnonce[16] = { 0 }; + memcpy(fullnonce + 4, nonce, 12); + + uint8_t polykey[32] = { 0 }; + cf_chacha20_ctx chacha; + cf_chacha20_init_custom(&chacha, key, 32, fullnonce, 4); + cf_chacha20_cipher(&chacha, polykey, polykey, sizeof polykey); + + /* Now initialise Poly1305. */ + cf_poly1305 poly; + cf_poly1305_init(&poly, polykey, polykey + 16); + mem_clean(polykey, sizeof polykey); + + /* Discard next 32 bytes of chacha20 key stream. */ + cf_chacha20_cipher(&chacha, polykey, polykey, sizeof polykey); + mem_clean(polykey, sizeof polykey); + + /* The input to Poly1305 is: + * AAD || pad(AAD) || cipher || pad(cipher) || len_64(aad) || len_64(cipher) */ + uint8_t padbuf[16] = { 0 }; + +#define PADLEN(x) (16 - ((x) & 0xf)) + + /* AAD || pad(AAD) */ + cf_poly1305_update(&poly, header, nheader); + cf_poly1305_update(&poly, padbuf, PADLEN(nheader)); + + /* || cipher */ + if (mode == ENCRYPT) + { + /* If we're encrypting, we compute the ciphertext + * before inputting it into the MAC. */ + cf_chacha20_cipher(&chacha, input, output, nbytes); + cf_poly1305_update(&poly, output, nbytes); + } else { + /* Otherwise: decryption -- input the ciphertext. + * Delay actual decryption until we checked the MAC. */ + cf_poly1305_update(&poly, input, nbytes); + } + + /* || pad(cipher) */ + cf_poly1305_update(&poly, padbuf, PADLEN(nbytes)); + + /* || len_64(aad) || len_64(cipher) */ + write64_le(nheader, padbuf); + write64_le(nbytes, padbuf + 8); + cf_poly1305_update(&poly, padbuf, sizeof padbuf); + + /* MAC computation is now complete. */ + + if (mode == ENCRYPT) + { + cf_poly1305_finish(&poly, tag); + mem_clean(&chacha, sizeof chacha); + return SUCCESS; + } + + /* Decrypt mode: calculate tag, and check it. + * If it's correct, proceed with decryption. */ + uint8_t checktag[16]; + cf_poly1305_finish(&poly, checktag); + + if (mem_eq(checktag, tag, sizeof checktag)) + { + cf_chacha20_cipher(&chacha, input, output, nbytes); + mem_clean(&chacha, sizeof chacha); + mem_clean(checktag, sizeof checktag); + return SUCCESS; + } else { + mem_clean(output, nbytes); + mem_clean(&chacha, sizeof chacha); + mem_clean(checktag, sizeof checktag); + return FAILURE; + } +} + +void cf_chacha20poly1305_encrypt(const uint8_t key[32], + const uint8_t nonce[12], + const uint8_t *header, size_t nheader, + const uint8_t *plaintext, size_t nbytes, + uint8_t *ciphertext, + uint8_t tag[16]) +{ + process(key, + nonce, + header, nheader, + plaintext, nbytes, + ciphertext, + ENCRYPT, + tag); +} + +int cf_chacha20poly1305_decrypt(const uint8_t key[32], + const uint8_t nonce[12], + const uint8_t *header, size_t nheader, + const uint8_t *ciphertext, size_t nbytes, + const uint8_t tag[16], + uint8_t *plaintext) +{ + uint8_t ourtag[16]; + memcpy(ourtag, tag, sizeof ourtag); + + return process(key, + nonce, + header, nheader, + ciphertext, nbytes, + plaintext, + DECRYPT, + ourtag); +} + diff --git a/deps/picoquic/picotls/deps/cifra/src/chacha20poly1305.h b/deps/picoquic/picotls/deps/cifra/src/chacha20poly1305.h new file mode 100644 index 0000000..1d44156 --- /dev/null +++ b/deps/picoquic/picotls/deps/cifra/src/chacha20poly1305.h @@ -0,0 +1,73 @@ +/* + * cifra - embedded cryptography library + * Written in 2014 by Joseph Birr-Pixton + * + * To the extent possible under law, the author(s) have dedicated all + * copyright and related and neighboring rights to this software to the + * public domain worldwide. This software is distributed without any + * warranty. + * + * You should have received a copy of the CC0 Public Domain Dedication + * along with this software. If not, see + * . + */ + +#ifndef CHACHA20POLY1305_H +#define CHACHA20POLY1305_H + +#include +#include + +/** + * The ChaCha20-Poly1305 AEAD construction + * ======================================= + * This is a composition of the ChaCha20 stream cipher and + * the Poly1305 polynomial MAC to form an AEAD. + * It's specified for use in TLS in the form of RFC7539. + * + * It uses a 256-bit key and a 96-bit nonce. + * + * This is a one-shot interface. + */ + +/* .. c:function:: $DECL + * ChaCha20-Poly1305 authenticated encryption. + * + * :param key: key material. + * :param nonce: per-message nonce. + * :param header: header buffer. + * :param nheader: number of header bytes. + * :param plaintext: plaintext bytes to be encrypted. + * :param nbytes: number of plaintext/ciphertext bytes. + * :param ciphertext: ciphertext output buffer, nbytes in length. + * :param tag: authentication tag output buffer. + */ +void cf_chacha20poly1305_encrypt(const uint8_t key[32], + const uint8_t nonce[12], + const uint8_t *header, size_t nheader, + const uint8_t *plaintext, size_t nbytes, + uint8_t *ciphertext, + uint8_t tag[16]); + +/* .. c:function:: $DECL + * ChaCha20-Poly1305 authenticated decryption. + * + * :return: 0 on success, non-zero on error. Plaintext is zeroed on error. + * + * :param key: key material. + * :param nonce: per-message nonce. + * :param header: header buffer. + * :param nheader: number of header bytes. + * :param ciphertext: ciphertext bytes to be decrypted. + * :param nbytes: number of plaintext/ciphertext bytes. + * :param plaintext: plaintext output buffer, nbytes in length. + * :param tag: authentication tag output buffer. + */ +int cf_chacha20poly1305_decrypt(const uint8_t key[32], + const uint8_t nonce[12], + const uint8_t *header, size_t nheader, + const uint8_t *ciphertext, size_t nbytes, + const uint8_t tag[16], + uint8_t *plaintext); + +#endif diff --git a/deps/picoquic/picotls/deps/cifra/src/chash.c b/deps/picoquic/picotls/deps/cifra/src/chash.c new file mode 100644 index 0000000..4ee5d76 --- /dev/null +++ b/deps/picoquic/picotls/deps/cifra/src/chash.c @@ -0,0 +1,28 @@ +/* + * cifra - embedded cryptography library + * Written in 2014 by Joseph Birr-Pixton + * + * To the extent possible under law, the author(s) have dedicated all + * copyright and related and neighboring rights to this software to the + * public domain worldwide. This software is distributed without any + * warranty. + * + * You should have received a copy of the CC0 Public Domain Dedication + * along with this software. If not, see + * . + */ + +#include "chash.h" +#include "handy.h" +#include "tassert.h" + +void cf_hash(const cf_chash *h, const void *m, size_t nm, uint8_t *out) +{ + cf_chash_ctx ctx; + assert(h); + h->init(&ctx); + h->update(&ctx, m, nm); + h->digest(&ctx, out); + mem_clean(&ctx, sizeof ctx); +} + diff --git a/deps/picoquic/picotls/deps/cifra/src/chash.h b/deps/picoquic/picotls/deps/cifra/src/chash.h new file mode 100644 index 0000000..8f2e201 --- /dev/null +++ b/deps/picoquic/picotls/deps/cifra/src/chash.h @@ -0,0 +1,137 @@ +/* + * cifra - embedded cryptography library + * Written in 2014 by Joseph Birr-Pixton + * + * To the extent possible under law, the author(s) have dedicated all + * copyright and related and neighboring rights to this software to the + * public domain worldwide. This software is distributed without any + * warranty. + * + * You should have received a copy of the CC0 Public Domain Dedication + * along with this software. If not, see + * . + */ + +#ifndef CHASH_H +#define CHASH_H + +#include +#include + +/** + * General hash function description + * ================================= + * This allows us to make use of hash functions without depending + * on a specific one. This is useful in implementing, for example, + * :doc:`HMAC `. + */ + +/* .. c:type:: cf_chash_init + * Hashing initialisation function type. + * + * Functions of this type should initialise the context in preparation + * for hashing a message with `cf_chash_update` functions. + * + * :rtype: void + * :param ctx: hash function-specific context structure. + */ +typedef void (*cf_chash_init)(void *ctx); + +/* .. c:type:: cf_chash_update + * Hashing data processing function type. + * + * Functions of this type hash `count` bytes of data at `data`, + * updating the contents of `ctx`. + * + * :rtype: void + * :param ctx: hash function-specific context structure. + * :param data: input data to hash. + * :param count: number of bytes to hash. + */ +typedef void (*cf_chash_update)(void *ctx, const void *data, size_t count); + +/* .. c:type:: cf_chash_digest + * Hashing completion function type. + * + * Functions of this type complete a hashing operation, + * writing :c:member:`cf_chash.hashsz` bytes to `hash`. + * + * This function does not change `ctx` -- any padding which needs doing + * must be done seperately (in a copy of `ctx`, say). + * + * This means you can interlave `_update` and `_digest` calls to + * learn `H(A)` and `H(A || B)` without hashing `A` twice. + * + * :rtype: void + * :param ctx: hash function-specific context structure. + * :param hash: location to write hash result. + */ +typedef void (*cf_chash_digest)(const void *ctx, uint8_t *hash); + +/* .. c:type:: cf_chash + * This type describes an incremental hash function in an abstract way. + * + * .. c:member:: cf_chash.hashsz + * The hash function's output, in bytes. + * + * .. c:member:: cf_chash.blocksz + * The hash function's internal block size, in bytes. + * + * .. c:member:: cf_chash.init + * Context initialisation function. + * + * .. c:member:: cf_chash:update + * Data processing function. + * + * .. c:member:: cf_chash:digest + * Completion function. + * + */ +typedef struct +{ + size_t hashsz; + size_t blocksz; + + cf_chash_init init; + cf_chash_update update; + cf_chash_digest digest; +} cf_chash; + +/* .. c:macro:: CF_CHASH_MAXCTX + * The maximum size of a :c:type:`cf_chash_ctx`. This allows + * use to put a structure in automatic storage that can + * store working data for any supported hash function. */ +#define CF_CHASH_MAXCTX 360 + +/* .. c:macro:: CF_CHASH_MAXBLK + * Maximum hash function block size (in bytes). */ +#define CF_CHASH_MAXBLK 128 + +/* .. c:macro:: CF_MAXHASH + * Maximum hash function output (in bytes). */ +#define CF_MAXHASH 64 + +/* .. c:type:: cf_chash_ctx + * A type usable with any `cf_chash` as a context. */ +typedef union +{ + uint8_t ctx[CF_CHASH_MAXCTX]; + uint16_t u16; + uint32_t u32; + uint64_t u64; +} cf_chash_ctx; + +/* .. c:function:: $DECL + * One shot hashing: `out = h(m)`. + * + * Using the hash function `h`, `nm` bytes at `m` are hashed and `h->hashsz` bytes + * of result is written to the buffer `out`. + * + * :param h: hash function description. + * :param m: message buffer. + * :param nm: message length. + * :param out: hash result buffer (written). + */ +void cf_hash(const cf_chash *h, const void *m, size_t nm, uint8_t *out); + +#endif diff --git a/deps/picoquic/picotls/deps/cifra/src/cmac.c b/deps/picoquic/picotls/deps/cifra/src/cmac.c new file mode 100644 index 0000000..51f5843 --- /dev/null +++ b/deps/picoquic/picotls/deps/cifra/src/cmac.c @@ -0,0 +1,150 @@ +/* + * cifra - embedded cryptography library + * Written in 2014 by Joseph Birr-Pixton + * + * To the extent possible under law, the author(s) have dedicated all + * copyright and related and neighboring rights to this software to the + * public domain worldwide. This software is distributed without any + * warranty. + * + * You should have received a copy of the CC0 Public Domain Dedication + * along with this software. If not, see + * . + */ + +#include "handy.h" +#include "prp.h" +#include "modes.h" +#include "bitops.h" +#include "blockwise.h" +#include "gf128.h" +#include "tassert.h" + +#include + +void cf_cmac_init(cf_cmac *ctx, const cf_prp *prp, void *prpctx) +{ + uint8_t L[CF_MAXBLOCK]; + assert(prp->blocksz == 16); + + mem_clean(ctx, sizeof *ctx); + + /* L = E_K(0^n) */ + mem_clean(L, prp->blocksz); + prp->encrypt(prpctx, L, L); + + /* B = 2L */ + cf_gf128 gf; + cf_gf128_frombytes_be(L, gf); + cf_gf128_double(gf, gf); + cf_gf128_tobytes_be(gf, ctx->B); + + /* P = 4L */ + cf_gf128_double(gf, gf); + cf_gf128_tobytes_be(gf, ctx->P); + + ctx->prp = prp; + ctx->prpctx = prpctx; +} + +void cf_cmac_sign(cf_cmac *ctx, const uint8_t *data, size_t len, uint8_t out[CF_MAXBLOCK]) +{ + cf_cmac_stream stream; + stream.cmac = *ctx; + cf_cmac_stream_reset(&stream); + cf_cmac_stream_update(&stream, data, len, 1); + cf_cmac_stream_final(&stream, out); +} + +void cf_cmac_stream_init(cf_cmac_stream *ctx, const cf_prp *prp, void *prpctx) +{ + cf_cmac_init(&ctx->cmac, prp, prpctx); + cf_cmac_stream_reset(ctx); +} + +void cf_cmac_stream_reset(cf_cmac_stream *ctx) +{ + uint8_t iv_zero[CF_MAXBLOCK] = { 0 }; + cf_cbc_init(&ctx->cbc, ctx->cmac.prp, ctx->cmac.prpctx, iv_zero); + mem_clean(ctx->buffer, sizeof ctx->buffer); + ctx->used = 0; + ctx->processed = 0; + ctx->finalised = 0; +} + +static void cmac_process(void *vctx, const uint8_t *block) +{ + cf_cmac_stream *ctx = vctx; + uint8_t output[CF_MAXBLOCK]; + cf_cbc_encrypt(&ctx->cbc, block, output, 1); + ctx->processed += ctx->cmac.prp->blocksz; +} + +static void cmac_process_final(cf_cmac_stream *ctx, const uint8_t *block, + const uint8_t *xor) +{ + uint8_t input[CF_MAXBLOCK]; + uint8_t output[CF_MAXBLOCK]; + xor_bb(input, block, xor, ctx->cmac.prp->blocksz); + cf_cbc_encrypt(&ctx->cbc, input, output, 1); + ctx->processed += ctx->cmac.prp->blocksz; + /* signature is in ctx->cbc.block. */ +} + +static void cmac_process_final_nopad(void *vctx, const uint8_t *block) +{ + cf_cmac_stream *ctx = vctx; + cmac_process_final(ctx, block, ctx->cmac.B); + ctx->finalised = 1; +} + +static void cmac_process_final_pad(void *vctx, const uint8_t *block) +{ + cf_cmac_stream *ctx = vctx; + cmac_process_final(ctx, block, ctx->cmac.P); + ctx->finalised = 1; +} + +void cf_cmac_stream_update(cf_cmac_stream *ctx, const uint8_t *data, size_t len, int isfinal) +{ + size_t blocksz = ctx->cmac.prp->blocksz; + cf_blockwise_in_fn final_fn = cmac_process; + int needpad = 0; + + if (isfinal) + { + int whole_number_of_blocks = ((len + ctx->used) & 0xf) == 0; + int empty_message = len == 0 && ctx->used == 0 && ctx->processed == 0; + + assert(!ctx->finalised); /* finalised before? */ + assert(len != 0 || empty_message); /* we can't be told we're done after the fact. */ + + /* If we have a whole number of blocks, and at least 1 block, we XOR in B. + * Otherwise, we need to pad and XOR in P. */ + if (whole_number_of_blocks && !empty_message) + final_fn = cmac_process_final_nopad; + else + needpad = 1; + } + + /* Input data */ + cf_blockwise_accumulate_final(ctx->buffer, &ctx->used, blocksz, + data, len, + cmac_process, + final_fn, ctx); + + /* Input padding */ + if (needpad) + { + cf_blockwise_acc_pad(ctx->buffer, &ctx->used, blocksz, + 0x80, 0x00, 0x00, blocksz - ctx->used, + cmac_process_final_pad, ctx); + } +} + +void cf_cmac_stream_final(cf_cmac_stream *ctx, uint8_t out[CF_MAXBLOCK]) +{ + assert(ctx->finalised); + memcpy(out, ctx->cbc.block, ctx->cmac.prp->blocksz); +} + diff --git a/deps/picoquic/picotls/deps/cifra/src/curve25519.c b/deps/picoquic/picotls/deps/cifra/src/curve25519.c new file mode 100644 index 0000000..6c4b9c1 --- /dev/null +++ b/deps/picoquic/picotls/deps/cifra/src/curve25519.c @@ -0,0 +1,29 @@ +/* + * cifra - embedded cryptography library + * Written in 2014 by Joseph Birr-Pixton + * + * To the extent possible under law, the author(s) have dedicated all + * copyright and related and neighboring rights to this software to the + * public domain worldwide. This software is distributed without any + * warranty. + * + * You should have received a copy of the CC0 Public Domain Dedication + * along with this software. If not, see + * . + */ + +#if defined(CORTEX_M0) || defined(CORTEX_M3) || defined(CORTEX_M4) +#include "arm/unacl/scalarmult.c" + +void cf_curve25519_mul(uint8_t out[32], const uint8_t scalar[32], const uint8_t point[32]) +{ + crypto_scalarmult_curve25519(out, scalar, point); +} + +void cf_curve25519_mul_base(uint8_t out[32], const uint8_t scalar[32]) +{ + crypto_scalarmult_curve25519_base(out, scalar); +} +#else +#include "curve25519.tweetnacl.c" +#endif diff --git a/deps/picoquic/picotls/deps/cifra/src/curve25519.donna.c b/deps/picoquic/picotls/deps/cifra/src/curve25519.donna.c new file mode 100644 index 0000000..3c597d3 --- /dev/null +++ b/deps/picoquic/picotls/deps/cifra/src/curve25519.donna.c @@ -0,0 +1,867 @@ +/* Copyright 2008, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * curve25519-donna: Curve25519 elliptic curve, public key function + * + * http://code.google.com/p/curve25519-donna/ + * + * Adam Langley + * + * Derived from public domain C code by Daniel J. Bernstein + * + * More information about curve25519 can be found here + * http://cr.yp.to/ecdh.html + * + * djb's sample implementation of curve25519 is written in a special assembly + * language called qhasm and uses the floating point registers. + * + * This is, almost, a clean room reimplementation from the curve25519 paper. It + * uses many of the tricks described therein. Only the crecip function is taken + * from the sample implementation. */ + +#include "curve25519.h" + +#include +#include + +#ifdef _MSC_VER +#define inline __inline +#endif + +typedef uint8_t u8; +typedef int32_t s32; +typedef int64_t limb; + +/* Field element representation: + * + * Field elements are written as an array of signed, 64-bit limbs, least + * significant first. The value of the field element is: + * x[0] + 2^26·x[1] + x^51·x[2] + 2^102·x[3] + ... + * + * i.e. the limbs are 26, 25, 26, 25, ... bits wide. */ + +/* Sum two numbers: output += in */ +static void fsum(limb *output, const limb *in) { + unsigned i; + for (i = 0; i < 10; i += 2) { + output[0+i] = output[0+i] + in[0+i]; + output[1+i] = output[1+i] + in[1+i]; + } +} + +/* Find the difference of two numbers: output = in - output + * (note the order of the arguments!). */ +static void fdifference(limb *output, const limb *in) { + unsigned i; + for (i = 0; i < 10; ++i) { + output[i] = in[i] - output[i]; + } +} + +/* Multiply a number by a scalar: output = in * scalar */ +static void fscalar_product(limb *output, const limb *in, const limb scalar) { + unsigned i; + for (i = 0; i < 10; ++i) { + output[i] = in[i] * scalar; + } +} + +/* Multiply two numbers: output = in2 * in + * + * output must be distinct to both inputs. The inputs are reduced coefficient + * form, the output is not. + * + * output[x] <= 14 * the largest product of the input limbs. */ +static void fproduct(limb *output, const limb *in2, const limb *in) { + output[0] = ((limb) ((s32) in2[0])) * ((s32) in[0]); + output[1] = ((limb) ((s32) in2[0])) * ((s32) in[1]) + + ((limb) ((s32) in2[1])) * ((s32) in[0]); + output[2] = 2 * ((limb) ((s32) in2[1])) * ((s32) in[1]) + + ((limb) ((s32) in2[0])) * ((s32) in[2]) + + ((limb) ((s32) in2[2])) * ((s32) in[0]); + output[3] = ((limb) ((s32) in2[1])) * ((s32) in[2]) + + ((limb) ((s32) in2[2])) * ((s32) in[1]) + + ((limb) ((s32) in2[0])) * ((s32) in[3]) + + ((limb) ((s32) in2[3])) * ((s32) in[0]); + output[4] = ((limb) ((s32) in2[2])) * ((s32) in[2]) + + 2 * (((limb) ((s32) in2[1])) * ((s32) in[3]) + + ((limb) ((s32) in2[3])) * ((s32) in[1])) + + ((limb) ((s32) in2[0])) * ((s32) in[4]) + + ((limb) ((s32) in2[4])) * ((s32) in[0]); + output[5] = ((limb) ((s32) in2[2])) * ((s32) in[3]) + + ((limb) ((s32) in2[3])) * ((s32) in[2]) + + ((limb) ((s32) in2[1])) * ((s32) in[4]) + + ((limb) ((s32) in2[4])) * ((s32) in[1]) + + ((limb) ((s32) in2[0])) * ((s32) in[5]) + + ((limb) ((s32) in2[5])) * ((s32) in[0]); + output[6] = 2 * (((limb) ((s32) in2[3])) * ((s32) in[3]) + + ((limb) ((s32) in2[1])) * ((s32) in[5]) + + ((limb) ((s32) in2[5])) * ((s32) in[1])) + + ((limb) ((s32) in2[2])) * ((s32) in[4]) + + ((limb) ((s32) in2[4])) * ((s32) in[2]) + + ((limb) ((s32) in2[0])) * ((s32) in[6]) + + ((limb) ((s32) in2[6])) * ((s32) in[0]); + output[7] = ((limb) ((s32) in2[3])) * ((s32) in[4]) + + ((limb) ((s32) in2[4])) * ((s32) in[3]) + + ((limb) ((s32) in2[2])) * ((s32) in[5]) + + ((limb) ((s32) in2[5])) * ((s32) in[2]) + + ((limb) ((s32) in2[1])) * ((s32) in[6]) + + ((limb) ((s32) in2[6])) * ((s32) in[1]) + + ((limb) ((s32) in2[0])) * ((s32) in[7]) + + ((limb) ((s32) in2[7])) * ((s32) in[0]); + output[8] = ((limb) ((s32) in2[4])) * ((s32) in[4]) + + 2 * (((limb) ((s32) in2[3])) * ((s32) in[5]) + + ((limb) ((s32) in2[5])) * ((s32) in[3]) + + ((limb) ((s32) in2[1])) * ((s32) in[7]) + + ((limb) ((s32) in2[7])) * ((s32) in[1])) + + ((limb) ((s32) in2[2])) * ((s32) in[6]) + + ((limb) ((s32) in2[6])) * ((s32) in[2]) + + ((limb) ((s32) in2[0])) * ((s32) in[8]) + + ((limb) ((s32) in2[8])) * ((s32) in[0]); + output[9] = ((limb) ((s32) in2[4])) * ((s32) in[5]) + + ((limb) ((s32) in2[5])) * ((s32) in[4]) + + ((limb) ((s32) in2[3])) * ((s32) in[6]) + + ((limb) ((s32) in2[6])) * ((s32) in[3]) + + ((limb) ((s32) in2[2])) * ((s32) in[7]) + + ((limb) ((s32) in2[7])) * ((s32) in[2]) + + ((limb) ((s32) in2[1])) * ((s32) in[8]) + + ((limb) ((s32) in2[8])) * ((s32) in[1]) + + ((limb) ((s32) in2[0])) * ((s32) in[9]) + + ((limb) ((s32) in2[9])) * ((s32) in[0]); + output[10] = 2 * (((limb) ((s32) in2[5])) * ((s32) in[5]) + + ((limb) ((s32) in2[3])) * ((s32) in[7]) + + ((limb) ((s32) in2[7])) * ((s32) in[3]) + + ((limb) ((s32) in2[1])) * ((s32) in[9]) + + ((limb) ((s32) in2[9])) * ((s32) in[1])) + + ((limb) ((s32) in2[4])) * ((s32) in[6]) + + ((limb) ((s32) in2[6])) * ((s32) in[4]) + + ((limb) ((s32) in2[2])) * ((s32) in[8]) + + ((limb) ((s32) in2[8])) * ((s32) in[2]); + output[11] = ((limb) ((s32) in2[5])) * ((s32) in[6]) + + ((limb) ((s32) in2[6])) * ((s32) in[5]) + + ((limb) ((s32) in2[4])) * ((s32) in[7]) + + ((limb) ((s32) in2[7])) * ((s32) in[4]) + + ((limb) ((s32) in2[3])) * ((s32) in[8]) + + ((limb) ((s32) in2[8])) * ((s32) in[3]) + + ((limb) ((s32) in2[2])) * ((s32) in[9]) + + ((limb) ((s32) in2[9])) * ((s32) in[2]); + output[12] = ((limb) ((s32) in2[6])) * ((s32) in[6]) + + 2 * (((limb) ((s32) in2[5])) * ((s32) in[7]) + + ((limb) ((s32) in2[7])) * ((s32) in[5]) + + ((limb) ((s32) in2[3])) * ((s32) in[9]) + + ((limb) ((s32) in2[9])) * ((s32) in[3])) + + ((limb) ((s32) in2[4])) * ((s32) in[8]) + + ((limb) ((s32) in2[8])) * ((s32) in[4]); + output[13] = ((limb) ((s32) in2[6])) * ((s32) in[7]) + + ((limb) ((s32) in2[7])) * ((s32) in[6]) + + ((limb) ((s32) in2[5])) * ((s32) in[8]) + + ((limb) ((s32) in2[8])) * ((s32) in[5]) + + ((limb) ((s32) in2[4])) * ((s32) in[9]) + + ((limb) ((s32) in2[9])) * ((s32) in[4]); + output[14] = 2 * (((limb) ((s32) in2[7])) * ((s32) in[7]) + + ((limb) ((s32) in2[5])) * ((s32) in[9]) + + ((limb) ((s32) in2[9])) * ((s32) in[5])) + + ((limb) ((s32) in2[6])) * ((s32) in[8]) + + ((limb) ((s32) in2[8])) * ((s32) in[6]); + output[15] = ((limb) ((s32) in2[7])) * ((s32) in[8]) + + ((limb) ((s32) in2[8])) * ((s32) in[7]) + + ((limb) ((s32) in2[6])) * ((s32) in[9]) + + ((limb) ((s32) in2[9])) * ((s32) in[6]); + output[16] = ((limb) ((s32) in2[8])) * ((s32) in[8]) + + 2 * (((limb) ((s32) in2[7])) * ((s32) in[9]) + + ((limb) ((s32) in2[9])) * ((s32) in[7])); + output[17] = ((limb) ((s32) in2[8])) * ((s32) in[9]) + + ((limb) ((s32) in2[9])) * ((s32) in[8]); + output[18] = 2 * ((limb) ((s32) in2[9])) * ((s32) in[9]); +} + +/* Reduce a long form to a short form by taking the input mod 2^255 - 19. + * + * On entry: |output[i]| < 14*2^54 + * On exit: |output[0..8]| < 280*2^54 */ +static void freduce_degree(limb *output) { + /* Each of these shifts and adds ends up multiplying the value by 19. + * + * For output[0..8], the absolute entry value is < 14*2^54 and we add, at + * most, 19*14*2^54 thus, on exit, |output[0..8]| < 280*2^54. */ + output[8] += output[18] << 4; + output[8] += output[18] << 1; + output[8] += output[18]; + output[7] += output[17] << 4; + output[7] += output[17] << 1; + output[7] += output[17]; + output[6] += output[16] << 4; + output[6] += output[16] << 1; + output[6] += output[16]; + output[5] += output[15] << 4; + output[5] += output[15] << 1; + output[5] += output[15]; + output[4] += output[14] << 4; + output[4] += output[14] << 1; + output[4] += output[14]; + output[3] += output[13] << 4; + output[3] += output[13] << 1; + output[3] += output[13]; + output[2] += output[12] << 4; + output[2] += output[12] << 1; + output[2] += output[12]; + output[1] += output[11] << 4; + output[1] += output[11] << 1; + output[1] += output[11]; + output[0] += output[10] << 4; + output[0] += output[10] << 1; + output[0] += output[10]; +} + +#if (-1 & 3) != 3 +#error "This code only works on a two's complement system" +#endif + +/* return v / 2^26, using only shifts and adds. + * + * On entry: v can take any value. */ +static inline limb +div_by_2_26(const limb v) +{ + /* High word of v; no shift needed. */ + const uint32_t highword = (uint32_t) (((uint64_t) v) >> 32); + /* Set to all 1s if v was negative; else set to 0s. */ + const int32_t sign = ((int32_t) highword) >> 31; + /* Set to 0x3ffffff if v was negative; else set to 0. */ + const int32_t roundoff = ((uint32_t) sign) >> 6; + /* Should return v / (1<<26) */ + return (v + roundoff) >> 26; +} + +/* return v / (2^25), using only shifts and adds. + * + * On entry: v can take any value. */ +static inline limb +div_by_2_25(const limb v) +{ + /* High word of v; no shift needed*/ + const uint32_t highword = (uint32_t) (((uint64_t) v) >> 32); + /* Set to all 1s if v was negative; else set to 0s. */ + const int32_t sign = ((int32_t) highword) >> 31; + /* Set to 0x1ffffff if v was negative; else set to 0. */ + const int32_t roundoff = ((uint32_t) sign) >> 7; + /* Should return v / (1<<25) */ + return (v + roundoff) >> 25; +} + +/* Reduce all coefficients of the short form input so that |x| < 2^26. + * + * On entry: |output[i]| < 280*2^54 */ +static void freduce_coefficients(limb *output) { + unsigned i; + + output[10] = 0; + + for (i = 0; i < 10; i += 2) { + limb over = div_by_2_26(output[i]); + /* The entry condition (that |output[i]| < 280*2^54) means that over is, at + * most, 280*2^28 in the first iteration of this loop. This is added to the + * next limb and we can approximate the resulting bound of that limb by + * 281*2^54. */ + output[i] -= over << 26; + output[i+1] += over; + + /* For the first iteration, |output[i+1]| < 281*2^54, thus |over| < + * 281*2^29. When this is added to the next limb, the resulting bound can + * be approximated as 281*2^54. + * + * For subsequent iterations of the loop, 281*2^54 remains a conservative + * bound and no overflow occurs. */ + over = div_by_2_25(output[i+1]); + output[i+1] -= over << 25; + output[i+2] += over; + } + /* Now |output[10]| < 281*2^29 and all other coefficients are reduced. */ + output[0] += output[10] << 4; + output[0] += output[10] << 1; + output[0] += output[10]; + + output[10] = 0; + + /* Now output[1..9] are reduced, and |output[0]| < 2^26 + 19*281*2^29 + * So |over| will be no more than 2^16. */ + { + limb over = div_by_2_26(output[0]); + output[0] -= over << 26; + output[1] += over; + } + + /* Now output[0,2..9] are reduced, and |output[1]| < 2^25 + 2^16 < 2^26. The + * bound on |output[1]| is sufficient to meet our needs. */ +} + +/* A helpful wrapper around fproduct: output = in * in2. + * + * On entry: |in[i]| < 2^27 and |in2[i]| < 2^27. + * + * output must be distinct to both inputs. The output is reduced degree + * (indeed, one need only provide storage for 10 limbs) and |output[i]| < 2^26. */ +static void +fmul(limb *output, const limb *in, const limb *in2) { + limb t[19]; + fproduct(t, in, in2); + /* |t[i]| < 14*2^54 */ + freduce_degree(t); + freduce_coefficients(t); + /* |t[i]| < 2^26 */ + memcpy(output, t, sizeof(limb) * 10); +} + +/* Square a number: output = in**2 + * + * output must be distinct from the input. The inputs are reduced coefficient + * form, the output is not. + * + * output[x] <= 14 * the largest product of the input limbs. */ +static void fsquare_inner(limb *output, const limb *in) { + output[0] = ((limb) ((s32) in[0])) * ((s32) in[0]); + output[1] = 2 * ((limb) ((s32) in[0])) * ((s32) in[1]); + output[2] = 2 * (((limb) ((s32) in[1])) * ((s32) in[1]) + + ((limb) ((s32) in[0])) * ((s32) in[2])); + output[3] = 2 * (((limb) ((s32) in[1])) * ((s32) in[2]) + + ((limb) ((s32) in[0])) * ((s32) in[3])); + output[4] = ((limb) ((s32) in[2])) * ((s32) in[2]) + + 4 * ((limb) ((s32) in[1])) * ((s32) in[3]) + + 2 * ((limb) ((s32) in[0])) * ((s32) in[4]); + output[5] = 2 * (((limb) ((s32) in[2])) * ((s32) in[3]) + + ((limb) ((s32) in[1])) * ((s32) in[4]) + + ((limb) ((s32) in[0])) * ((s32) in[5])); + output[6] = 2 * (((limb) ((s32) in[3])) * ((s32) in[3]) + + ((limb) ((s32) in[2])) * ((s32) in[4]) + + ((limb) ((s32) in[0])) * ((s32) in[6]) + + 2 * ((limb) ((s32) in[1])) * ((s32) in[5])); + output[7] = 2 * (((limb) ((s32) in[3])) * ((s32) in[4]) + + ((limb) ((s32) in[2])) * ((s32) in[5]) + + ((limb) ((s32) in[1])) * ((s32) in[6]) + + ((limb) ((s32) in[0])) * ((s32) in[7])); + output[8] = ((limb) ((s32) in[4])) * ((s32) in[4]) + + 2 * (((limb) ((s32) in[2])) * ((s32) in[6]) + + ((limb) ((s32) in[0])) * ((s32) in[8]) + + 2 * (((limb) ((s32) in[1])) * ((s32) in[7]) + + ((limb) ((s32) in[3])) * ((s32) in[5]))); + output[9] = 2 * (((limb) ((s32) in[4])) * ((s32) in[5]) + + ((limb) ((s32) in[3])) * ((s32) in[6]) + + ((limb) ((s32) in[2])) * ((s32) in[7]) + + ((limb) ((s32) in[1])) * ((s32) in[8]) + + ((limb) ((s32) in[0])) * ((s32) in[9])); + output[10] = 2 * (((limb) ((s32) in[5])) * ((s32) in[5]) + + ((limb) ((s32) in[4])) * ((s32) in[6]) + + ((limb) ((s32) in[2])) * ((s32) in[8]) + + 2 * (((limb) ((s32) in[3])) * ((s32) in[7]) + + ((limb) ((s32) in[1])) * ((s32) in[9]))); + output[11] = 2 * (((limb) ((s32) in[5])) * ((s32) in[6]) + + ((limb) ((s32) in[4])) * ((s32) in[7]) + + ((limb) ((s32) in[3])) * ((s32) in[8]) + + ((limb) ((s32) in[2])) * ((s32) in[9])); + output[12] = ((limb) ((s32) in[6])) * ((s32) in[6]) + + 2 * (((limb) ((s32) in[4])) * ((s32) in[8]) + + 2 * (((limb) ((s32) in[5])) * ((s32) in[7]) + + ((limb) ((s32) in[3])) * ((s32) in[9]))); + output[13] = 2 * (((limb) ((s32) in[6])) * ((s32) in[7]) + + ((limb) ((s32) in[5])) * ((s32) in[8]) + + ((limb) ((s32) in[4])) * ((s32) in[9])); + output[14] = 2 * (((limb) ((s32) in[7])) * ((s32) in[7]) + + ((limb) ((s32) in[6])) * ((s32) in[8]) + + 2 * ((limb) ((s32) in[5])) * ((s32) in[9])); + output[15] = 2 * (((limb) ((s32) in[7])) * ((s32) in[8]) + + ((limb) ((s32) in[6])) * ((s32) in[9])); + output[16] = ((limb) ((s32) in[8])) * ((s32) in[8]) + + 4 * ((limb) ((s32) in[7])) * ((s32) in[9]); + output[17] = 2 * ((limb) ((s32) in[8])) * ((s32) in[9]); + output[18] = 2 * ((limb) ((s32) in[9])) * ((s32) in[9]); +} + +/* fsquare sets output = in^2. + * + * On entry: The |in| argument is in reduced coefficients form and |in[i]| < + * 2^27. + * + * On exit: The |output| argument is in reduced coefficients form (indeed, one + * need only provide storage for 10 limbs) and |out[i]| < 2^26. */ +static void +fsquare(limb *output, const limb *in) { + limb t[19]; + fsquare_inner(t, in); + /* |t[i]| < 14*2^54 because the largest product of two limbs will be < + * 2^(27+27) and fsquare_inner adds together, at most, 14 of those + * products. */ + freduce_degree(t); + freduce_coefficients(t); + /* |t[i]| < 2^26 */ + memcpy(output, t, sizeof(limb) * 10); +} + +/* Take a little-endian, 32-byte number and expand it into polynomial form */ +static void +fexpand(limb *output, const u8 *input) { +#define F(n,start,shift,mask) \ + output[n] = ((((limb) input[start + 0]) | \ + ((limb) input[start + 1]) << 8 | \ + ((limb) input[start + 2]) << 16 | \ + ((limb) input[start + 3]) << 24) >> shift) & mask; + F(0, 0, 0, 0x3ffffff); + F(1, 3, 2, 0x1ffffff); + F(2, 6, 3, 0x3ffffff); + F(3, 9, 5, 0x1ffffff); + F(4, 12, 6, 0x3ffffff); + F(5, 16, 0, 0x1ffffff); + F(6, 19, 1, 0x3ffffff); + F(7, 22, 3, 0x1ffffff); + F(8, 25, 4, 0x3ffffff); + F(9, 28, 6, 0x1ffffff); +#undef F +} + +#if (-32 >> 1) != -16 +#error "This code only works when >> does sign-extension on negative numbers" +#endif + +/* s32_eq returns 0xffffffff iff a == b and zero otherwise. */ +static s32 s32_eq(s32 a, s32 b) { + a = ~(a ^ b); + a &= a << 16; + a &= a << 8; + a &= a << 4; + a &= a << 2; + a &= a << 1; + return a >> 31; +} + +/* s32_gte returns 0xffffffff if a >= b and zero otherwise, where a and b are + * both non-negative. */ +static s32 s32_gte(s32 a, s32 b) { + a -= b; + /* a >= 0 iff a >= b. */ + return ~(a >> 31); +} + +/* Take a fully reduced polynomial form number and contract it into a + * little-endian, 32-byte array. + * + * On entry: |input_limbs[i]| < 2^26 */ +static void +fcontract(u8 *output, limb *input_limbs) { + int i; + int j; + s32 input[10]; + s32 mask; + + /* |input_limbs[i]| < 2^26, so it's valid to convert to an s32. */ + for (i = 0; i < 10; i++) { + input[i] = input_limbs[i]; + } + + for (j = 0; j < 2; ++j) { + for (i = 0; i < 9; ++i) { + if ((i & 1) == 1) { + /* This calculation is a time-invariant way to make input[i] + * non-negative by borrowing from the next-larger limb. */ + const s32 mask = input[i] >> 31; + const s32 carry = -((input[i] & mask) >> 25); + input[i] = input[i] + (carry << 25); + input[i+1] = input[i+1] - carry; + } else { + const s32 mask = input[i] >> 31; + const s32 carry = -((input[i] & mask) >> 26); + input[i] = input[i] + (carry << 26); + input[i+1] = input[i+1] - carry; + } + } + + /* There's no greater limb for input[9] to borrow from, but we can multiply + * by 19 and borrow from input[0], which is valid mod 2^255-19. */ + { + const s32 mask = input[9] >> 31; + const s32 carry = -((input[9] & mask) >> 25); + input[9] = input[9] + (carry << 25); + input[0] = input[0] - (carry * 19); + } + + /* After the first iteration, input[1..9] are non-negative and fit within + * 25 or 26 bits, depending on position. However, input[0] may be + * negative. */ + } + + /* The first borrow-propagation pass above ended with every limb + except (possibly) input[0] non-negative. + + If input[0] was negative after the first pass, then it was because of a + carry from input[9]. On entry, input[9] < 2^26 so the carry was, at most, + one, since (2**26-1) >> 25 = 1. Thus input[0] >= -19. + + In the second pass, each limb is decreased by at most one. Thus the second + borrow-propagation pass could only have wrapped around to decrease + input[0] again if the first pass left input[0] negative *and* input[1] + through input[9] were all zero. In that case, input[1] is now 2^25 - 1, + and this last borrow-propagation step will leave input[1] non-negative. */ + { + const s32 mask = input[0] >> 31; + const s32 carry = -((input[0] & mask) >> 26); + input[0] = input[0] + (carry << 26); + input[1] = input[1] - carry; + } + + /* All input[i] are now non-negative. However, there might be values between + * 2^25 and 2^26 in a limb which is, nominally, 25 bits wide. */ + for (j = 0; j < 2; j++) { + for (i = 0; i < 9; i++) { + if ((i & 1) == 1) { + const s32 carry = input[i] >> 25; + input[i] &= 0x1ffffff; + input[i+1] += carry; + } else { + const s32 carry = input[i] >> 26; + input[i] &= 0x3ffffff; + input[i+1] += carry; + } + } + + { + const s32 carry = input[9] >> 25; + input[9] &= 0x1ffffff; + input[0] += 19*carry; + } + } + + /* If the first carry-chain pass, just above, ended up with a carry from + * input[9], and that caused input[0] to be out-of-bounds, then input[0] was + * < 2^26 + 2*19, because the carry was, at most, two. + * + * If the second pass carried from input[9] again then input[0] is < 2*19 and + * the input[9] -> input[0] carry didn't push input[0] out of bounds. */ + + /* It still remains the case that input might be between 2^255-19 and 2^255. + * In this case, input[1..9] must take their maximum value and input[0] must + * be >= (2^255-19) & 0x3ffffff, which is 0x3ffffed. */ + mask = s32_gte(input[0], 0x3ffffed); + for (i = 1; i < 10; i++) { + if ((i & 1) == 1) { + mask &= s32_eq(input[i], 0x1ffffff); + } else { + mask &= s32_eq(input[i], 0x3ffffff); + } + } + + /* mask is either 0xffffffff (if input >= 2^255-19) and zero otherwise. Thus + * this conditionally subtracts 2^255-19. */ + input[0] -= mask & 0x3ffffed; + + for (i = 1; i < 10; i++) { + if ((i & 1) == 1) { + input[i] -= mask & 0x1ffffff; + } else { + input[i] -= mask & 0x3ffffff; + } + } + + input[1] <<= 2; + input[2] <<= 3; + input[3] <<= 5; + input[4] <<= 6; + input[6] <<= 1; + input[7] <<= 3; + input[8] <<= 4; + input[9] <<= 6; +#define F(i, s) \ + output[s+0] |= input[i] & 0xff; \ + output[s+1] = (input[i] >> 8) & 0xff; \ + output[s+2] = (input[i] >> 16) & 0xff; \ + output[s+3] = (input[i] >> 24) & 0xff; + output[0] = 0; + output[16] = 0; + F(0,0); + F(1,3); + F(2,6); + F(3,9); + F(4,12); + F(5,16); + F(6,19); + F(7,22); + F(8,25); + F(9,28); +#undef F +} + +/* Input: Q, Q', Q-Q' + * Output: 2Q, Q+Q' + * + * x2 z3: long form + * x3 z3: long form + * x z: short form, destroyed + * xprime zprime: short form, destroyed + * qmqp: short form, preserved + * + * On entry and exit, the absolute value of the limbs of all inputs and outputs + * are < 2^26. */ +static void fmonty(limb *x2, limb *z2, /* output 2Q */ + limb *x3, limb *z3, /* output Q + Q' */ + limb *x, limb *z, /* input Q */ + limb *xprime, limb *zprime, /* input Q' */ + const limb *qmqp /* input Q - Q' */) { + limb origx[10], origxprime[10], zzz[19], xx[19], zz[19], xxprime[19], + zzprime[19], zzzprime[19], xxxprime[19]; + + memcpy(origx, x, 10 * sizeof(limb)); + fsum(x, z); + /* |x[i]| < 2^27 */ + fdifference(z, origx); /* does x - z */ + /* |z[i]| < 2^27 */ + + memcpy(origxprime, xprime, sizeof(limb) * 10); + fsum(xprime, zprime); + /* |xprime[i]| < 2^27 */ + fdifference(zprime, origxprime); + /* |zprime[i]| < 2^27 */ + fproduct(xxprime, xprime, z); + /* |xxprime[i]| < 14*2^54: the largest product of two limbs will be < + * 2^(27+27) and fproduct adds together, at most, 14 of those products. + * (Approximating that to 2^58 doesn't work out.) */ + fproduct(zzprime, x, zprime); + /* |zzprime[i]| < 14*2^54 */ + freduce_degree(xxprime); + freduce_coefficients(xxprime); + /* |xxprime[i]| < 2^26 */ + freduce_degree(zzprime); + freduce_coefficients(zzprime); + /* |zzprime[i]| < 2^26 */ + memcpy(origxprime, xxprime, sizeof(limb) * 10); + fsum(xxprime, zzprime); + /* |xxprime[i]| < 2^27 */ + fdifference(zzprime, origxprime); + /* |zzprime[i]| < 2^27 */ + fsquare(xxxprime, xxprime); + /* |xxxprime[i]| < 2^26 */ + fsquare(zzzprime, zzprime); + /* |zzzprime[i]| < 2^26 */ + fproduct(zzprime, zzzprime, qmqp); + /* |zzprime[i]| < 14*2^52 */ + freduce_degree(zzprime); + freduce_coefficients(zzprime); + /* |zzprime[i]| < 2^26 */ + memcpy(x3, xxxprime, sizeof(limb) * 10); + memcpy(z3, zzprime, sizeof(limb) * 10); + + fsquare(xx, x); + /* |xx[i]| < 2^26 */ + fsquare(zz, z); + /* |zz[i]| < 2^26 */ + fproduct(x2, xx, zz); + /* |x2[i]| < 14*2^52 */ + freduce_degree(x2); + freduce_coefficients(x2); + /* |x2[i]| < 2^26 */ + fdifference(zz, xx); // does zz = xx - zz + /* |zz[i]| < 2^27 */ + memset(zzz + 10, 0, sizeof(limb) * 9); + fscalar_product(zzz, zz, 121665); + /* |zzz[i]| < 2^(27+17) */ + /* No need to call freduce_degree here: + fscalar_product doesn't increase the degree of its input. */ + freduce_coefficients(zzz); + /* |zzz[i]| < 2^26 */ + fsum(zzz, xx); + /* |zzz[i]| < 2^27 */ + fproduct(z2, zz, zzz); + /* |z2[i]| < 14*2^(26+27) */ + freduce_degree(z2); + freduce_coefficients(z2); + /* |z2|i| < 2^26 */ +} + +/* Conditionally swap two reduced-form limb arrays if 'iswap' is 1, but leave + * them unchanged if 'iswap' is 0. Runs in data-invariant time to avoid + * side-channel attacks. + * + * NOTE that this function requires that 'iswap' be 1 or 0; other values give + * wrong results. Also, the two limb arrays must be in reduced-coefficient, + * reduced-degree form: the values in a[10..19] or b[10..19] aren't swapped, + * and all all values in a[0..9],b[0..9] must have magnitude less than + * INT32_MAX. */ +static void +swap_conditional(limb a[19], limb b[19], limb iswap) { + unsigned i; + const s32 swap = (s32) -iswap; + + for (i = 0; i < 10; ++i) { + const s32 x = swap & ( ((s32)a[i]) ^ ((s32)b[i]) ); + a[i] = ((s32)a[i]) ^ x; + b[i] = ((s32)b[i]) ^ x; + } +} + +/* Calculates nQ where Q is the x-coordinate of a point on the curve + * + * resultx/resultz: the x coordinate of the resulting curve point (short form) + * n: a little endian, 32-byte number + * q: a point of the curve (short form) */ +static void +cmult(limb *resultx, limb *resultz, const u8 *n, const limb *q) { + limb a[19] = {0}, b[19] = {1}, c[19] = {1}, d[19] = {0}; + limb *nqpqx = a, *nqpqz = b, *nqx = c, *nqz = d, *t; + limb e[19] = {0}, f[19] = {1}, g[19] = {0}, h[19] = {1}; + limb *nqpqx2 = e, *nqpqz2 = f, *nqx2 = g, *nqz2 = h; + + unsigned i, j; + + memcpy(nqpqx, q, sizeof(limb) * 10); + + for (i = 0; i < 32; ++i) { + u8 byte = n[31 - i]; + for (j = 0; j < 8; ++j) { + const limb bit = byte >> 7; + + swap_conditional(nqx, nqpqx, bit); + swap_conditional(nqz, nqpqz, bit); + fmonty(nqx2, nqz2, + nqpqx2, nqpqz2, + nqx, nqz, + nqpqx, nqpqz, + q); + swap_conditional(nqx2, nqpqx2, bit); + swap_conditional(nqz2, nqpqz2, bit); + + t = nqx; + nqx = nqx2; + nqx2 = t; + t = nqz; + nqz = nqz2; + nqz2 = t; + t = nqpqx; + nqpqx = nqpqx2; + nqpqx2 = t; + t = nqpqz; + nqpqz = nqpqz2; + nqpqz2 = t; + + byte <<= 1; + } + } + + memcpy(resultx, nqx, sizeof(limb) * 10); + memcpy(resultz, nqz, sizeof(limb) * 10); +} + +// ----------------------------------------------------------------------------- +// Shamelessly copied from djb's code +// ----------------------------------------------------------------------------- +static void +crecip(limb *out, const limb *z) { + limb z2[10]; + limb z9[10]; + limb z11[10]; + limb z2_5_0[10]; + limb z2_10_0[10]; + limb z2_20_0[10]; + limb z2_50_0[10]; + limb z2_100_0[10]; + limb t0[10]; + limb t1[10]; + int i; + + /* 2 */ fsquare(z2,z); + /* 4 */ fsquare(t1,z2); + /* 8 */ fsquare(t0,t1); + /* 9 */ fmul(z9,t0,z); + /* 11 */ fmul(z11,z9,z2); + /* 22 */ fsquare(t0,z11); + /* 2^5 - 2^0 = 31 */ fmul(z2_5_0,t0,z9); + + /* 2^6 - 2^1 */ fsquare(t0,z2_5_0); + /* 2^7 - 2^2 */ fsquare(t1,t0); + /* 2^8 - 2^3 */ fsquare(t0,t1); + /* 2^9 - 2^4 */ fsquare(t1,t0); + /* 2^10 - 2^5 */ fsquare(t0,t1); + /* 2^10 - 2^0 */ fmul(z2_10_0,t0,z2_5_0); + + /* 2^11 - 2^1 */ fsquare(t0,z2_10_0); + /* 2^12 - 2^2 */ fsquare(t1,t0); + /* 2^20 - 2^10 */ for (i = 2;i < 10;i += 2) { fsquare(t0,t1); fsquare(t1,t0); } + /* 2^20 - 2^0 */ fmul(z2_20_0,t1,z2_10_0); + + /* 2^21 - 2^1 */ fsquare(t0,z2_20_0); + /* 2^22 - 2^2 */ fsquare(t1,t0); + /* 2^40 - 2^20 */ for (i = 2;i < 20;i += 2) { fsquare(t0,t1); fsquare(t1,t0); } + /* 2^40 - 2^0 */ fmul(t0,t1,z2_20_0); + + /* 2^41 - 2^1 */ fsquare(t1,t0); + /* 2^42 - 2^2 */ fsquare(t0,t1); + /* 2^50 - 2^10 */ for (i = 2;i < 10;i += 2) { fsquare(t1,t0); fsquare(t0,t1); } + /* 2^50 - 2^0 */ fmul(z2_50_0,t0,z2_10_0); + + /* 2^51 - 2^1 */ fsquare(t0,z2_50_0); + /* 2^52 - 2^2 */ fsquare(t1,t0); + /* 2^100 - 2^50 */ for (i = 2;i < 50;i += 2) { fsquare(t0,t1); fsquare(t1,t0); } + /* 2^100 - 2^0 */ fmul(z2_100_0,t1,z2_50_0); + + /* 2^101 - 2^1 */ fsquare(t1,z2_100_0); + /* 2^102 - 2^2 */ fsquare(t0,t1); + /* 2^200 - 2^100 */ for (i = 2;i < 100;i += 2) { fsquare(t1,t0); fsquare(t0,t1); } + /* 2^200 - 2^0 */ fmul(t1,t0,z2_100_0); + + /* 2^201 - 2^1 */ fsquare(t0,t1); + /* 2^202 - 2^2 */ fsquare(t1,t0); + /* 2^250 - 2^50 */ for (i = 2;i < 50;i += 2) { fsquare(t0,t1); fsquare(t1,t0); } + /* 2^250 - 2^0 */ fmul(t0,t1,z2_50_0); + + /* 2^251 - 2^1 */ fsquare(t1,t0); + /* 2^252 - 2^2 */ fsquare(t0,t1); + /* 2^253 - 2^3 */ fsquare(t1,t0); + /* 2^254 - 2^4 */ fsquare(t0,t1); + /* 2^255 - 2^5 */ fsquare(t1,t0); + /* 2^255 - 21 */ fmul(out,t1,z11); +} + +void cf_curve25519_mul(u8 out[32], const u8 scalar[32], const u8 point[32]) +{ + limb bp[10], x[10], z[11], zmone[10]; + uint8_t e[32]; + int i; + + for (i = 0; i < 32; ++i) e[i] = scalar[i]; + e[0] &= 248; + e[31] &= 127; + e[31] |= 64; + + fexpand(bp, point); + cmult(x, z, e, bp); + crecip(zmone, z); + fmul(z, x, zmone); + fcontract(out, z); +} + +void cf_curve25519_mul_base(u8 out[32], const u8 scalar[32]) +{ + uint8_t base[32] = { 9 }; + cf_curve25519_mul(out, scalar, base); +} diff --git a/deps/picoquic/picotls/deps/cifra/src/curve25519.h b/deps/picoquic/picotls/deps/cifra/src/curve25519.h new file mode 100644 index 0000000..9b0b7f5 --- /dev/null +++ b/deps/picoquic/picotls/deps/cifra/src/curve25519.h @@ -0,0 +1,42 @@ +/* + * cifra - embedded cryptography library + * Written in 2014 by Joseph Birr-Pixton + * + * To the extent possible under law, the author(s) have dedicated all + * copyright and related and neighboring rights to this software to the + * public domain worldwide. This software is distributed without any + * warranty. + * + * You should have received a copy of the CC0 Public Domain Dedication + * along with this software. If not, see + * . + */ + +#ifndef CURVE25519_H +#define CURVE25519_H + +#include +#include + +/** + * Curve25519 + * ========== + * This is `curve25519 `_ with switchable + * implementations underneath. + * + * By default tweetnacl is used on hosts, and the implementation + * from μNaCl for Cortex-M0, M3 and M4. + */ + +/* .. c:function:: $DECL + * Multiplies `point` by `scalar`, putting the resulting point into `out`. */ +void cf_curve25519_mul(uint8_t out[32], + const uint8_t scalar[32], + const uint8_t point[32]); + +/* .. c:function:: $DECL + * Multiplies `scalar` by the curve25519 base point, putting the resulting + * point into `out`. */ +void cf_curve25519_mul_base(uint8_t out[32], const uint8_t scalar[32]); + +#endif diff --git a/deps/picoquic/picotls/deps/cifra/src/curve25519.naclref.c b/deps/picoquic/picotls/deps/cifra/src/curve25519.naclref.c new file mode 100644 index 0000000..9f331d1 --- /dev/null +++ b/deps/picoquic/picotls/deps/cifra/src/curve25519.naclref.c @@ -0,0 +1,273 @@ +/* +version 20081011 +Matthew Dempsky +Public domain. +Derived from public domain code by D. J. Bernstein. +*/ +#include "curve25519.h" + +static void add(unsigned int out[32],const unsigned int a[32],const unsigned int b[32]) +{ + unsigned int j; + unsigned int u; + u = 0; + for (j = 0;j < 31;++j) { u += a[j] + b[j]; out[j] = u & 255; u >>= 8; } + u += a[31] + b[31]; out[31] = u; +} + +static void sub(unsigned int out[32],const unsigned int a[32],const unsigned int b[32]) +{ + unsigned int j; + unsigned int u; + u = 218; + for (j = 0;j < 31;++j) { + u += a[j] + 65280 - b[j]; + out[j] = u & 255; + u >>= 8; + } + u += a[31] - b[31]; + out[31] = u; +} + +static void squeeze(unsigned int a[32]) +{ + unsigned int j; + unsigned int u; + u = 0; + for (j = 0;j < 31;++j) { u += a[j]; a[j] = u & 255; u >>= 8; } + u += a[31]; a[31] = u & 127; + u = 19 * (u >> 7); + for (j = 0;j < 31;++j) { u += a[j]; a[j] = u & 255; u >>= 8; } + u += a[31]; a[31] = u; +} + +static const unsigned int minusp[32] = { + 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128 +} ; + +static void freeze(unsigned int a[32]) +{ + unsigned int aorig[32]; + unsigned int j; + unsigned int negative; + + for (j = 0;j < 32;++j) aorig[j] = a[j]; + add(a,a,minusp); + negative = -((a[31] >> 7) & 1); + for (j = 0;j < 32;++j) a[j] ^= negative & (aorig[j] ^ a[j]); +} + +static void mult(unsigned int out[32],const unsigned int a[32],const unsigned int b[32]) +{ + unsigned int i; + unsigned int j; + unsigned int u; + + for (i = 0;i < 32;++i) { + u = 0; + for (j = 0;j <= i;++j) u += a[j] * b[i - j]; + for (j = i + 1;j < 32;++j) u += 38 * a[j] * b[i + 32 - j]; + out[i] = u; + } + squeeze(out); +} + +static void mult121665(unsigned int out[32],const unsigned int a[32]) +{ + unsigned int j; + unsigned int u; + + u = 0; + for (j = 0;j < 31;++j) { u += 121665 * a[j]; out[j] = u & 255; u >>= 8; } + u += 121665 * a[31]; out[31] = u & 127; + u = 19 * (u >> 7); + for (j = 0;j < 31;++j) { u += out[j]; out[j] = u & 255; u >>= 8; } + u += out[j]; out[j] = u; +} + +static void square(unsigned int out[32],const unsigned int a[32]) +{ + unsigned int i; + unsigned int j; + unsigned int u; + + for (i = 0;i < 32;++i) { + u = 0; + for (j = 0;j < i - j;++j) u += a[j] * a[i - j]; + for (j = i + 1;j < i + 32 - j;++j) u += 38 * a[j] * a[i + 32 - j]; + u *= 2; + if ((i & 1) == 0) { + u += a[i / 2] * a[i / 2]; + u += 38 * a[i / 2 + 16] * a[i / 2 + 16]; + } + out[i] = u; + } + squeeze(out); +} + +static void select(unsigned int p[64],unsigned int q[64],const unsigned int r[64],const unsigned int s[64],unsigned int b) +{ + unsigned int j; + unsigned int t; + unsigned int bminus1; + + bminus1 = b - 1; + for (j = 0;j < 64;++j) { + t = bminus1 & (r[j] ^ s[j]); + p[j] = s[j] ^ t; + q[j] = r[j] ^ t; + } +} + +static void mainloop(unsigned int work[64],const unsigned char e[32]) +{ + unsigned int xzm1[64]; + unsigned int xzm[64]; + unsigned int xzmb[64]; + unsigned int xzm1b[64]; + unsigned int xznb[64]; + unsigned int xzn1b[64]; + unsigned int a0[64]; + unsigned int a1[64]; + unsigned int b0[64]; + unsigned int b1[64]; + unsigned int c1[64]; + unsigned int r[32]; + unsigned int s[32]; + unsigned int t[32]; + unsigned int u[32]; + unsigned int j; + unsigned int b; + int pos; + + for (j = 0;j < 32;++j) xzm1[j] = work[j]; + xzm1[32] = 1; + for (j = 33;j < 64;++j) xzm1[j] = 0; + + xzm[0] = 1; + for (j = 1;j < 64;++j) xzm[j] = 0; + + for (pos = 254;pos >= 0;--pos) { + b = e[pos / 8] >> (pos & 7); + b &= 1; + select(xzmb,xzm1b,xzm,xzm1,b); + add(a0,xzmb,xzmb + 32); + sub(a0 + 32,xzmb,xzmb + 32); + add(a1,xzm1b,xzm1b + 32); + sub(a1 + 32,xzm1b,xzm1b + 32); + square(b0,a0); + square(b0 + 32,a0 + 32); + mult(b1,a1,a0 + 32); + mult(b1 + 32,a1 + 32,a0); + add(c1,b1,b1 + 32); + sub(c1 + 32,b1,b1 + 32); + square(r,c1 + 32); + sub(s,b0,b0 + 32); + mult121665(t,s); + add(u,t,b0); + mult(xznb,b0,b0 + 32); + mult(xznb + 32,s,u); + square(xzn1b,c1); + mult(xzn1b + 32,r,work); + select(xzm,xzm1,xznb,xzn1b,b); + } + + for (j = 0;j < 64;++j) work[j] = xzm[j]; +} + +static void recip(unsigned int out[32],const unsigned int z[32]) +{ + unsigned int z2[32]; + unsigned int z9[32]; + unsigned int z11[32]; + unsigned int z2_5_0[32]; + unsigned int z2_10_0[32]; + unsigned int z2_20_0[32]; + unsigned int z2_50_0[32]; + unsigned int z2_100_0[32]; + unsigned int t0[32]; + unsigned int t1[32]; + int i; + + /* 2 */ square(z2,z); + /* 4 */ square(t1,z2); + /* 8 */ square(t0,t1); + /* 9 */ mult(z9,t0,z); + /* 11 */ mult(z11,z9,z2); + /* 22 */ square(t0,z11); + /* 2^5 - 2^0 = 31 */ mult(z2_5_0,t0,z9); + + /* 2^6 - 2^1 */ square(t0,z2_5_0); + /* 2^7 - 2^2 */ square(t1,t0); + /* 2^8 - 2^3 */ square(t0,t1); + /* 2^9 - 2^4 */ square(t1,t0); + /* 2^10 - 2^5 */ square(t0,t1); + /* 2^10 - 2^0 */ mult(z2_10_0,t0,z2_5_0); + + /* 2^11 - 2^1 */ square(t0,z2_10_0); + /* 2^12 - 2^2 */ square(t1,t0); + /* 2^20 - 2^10 */ for (i = 2;i < 10;i += 2) { square(t0,t1); square(t1,t0); } + /* 2^20 - 2^0 */ mult(z2_20_0,t1,z2_10_0); + + /* 2^21 - 2^1 */ square(t0,z2_20_0); + /* 2^22 - 2^2 */ square(t1,t0); + /* 2^40 - 2^20 */ for (i = 2;i < 20;i += 2) { square(t0,t1); square(t1,t0); } + /* 2^40 - 2^0 */ mult(t0,t1,z2_20_0); + + /* 2^41 - 2^1 */ square(t1,t0); + /* 2^42 - 2^2 */ square(t0,t1); + /* 2^50 - 2^10 */ for (i = 2;i < 10;i += 2) { square(t1,t0); square(t0,t1); } + /* 2^50 - 2^0 */ mult(z2_50_0,t0,z2_10_0); + + /* 2^51 - 2^1 */ square(t0,z2_50_0); + /* 2^52 - 2^2 */ square(t1,t0); + /* 2^100 - 2^50 */ for (i = 2;i < 50;i += 2) { square(t0,t1); square(t1,t0); } + /* 2^100 - 2^0 */ mult(z2_100_0,t1,z2_50_0); + + /* 2^101 - 2^1 */ square(t1,z2_100_0); + /* 2^102 - 2^2 */ square(t0,t1); + /* 2^200 - 2^100 */ for (i = 2;i < 100;i += 2) { square(t1,t0); square(t0,t1); } + /* 2^200 - 2^0 */ mult(t1,t0,z2_100_0); + + /* 2^201 - 2^1 */ square(t0,t1); + /* 2^202 - 2^2 */ square(t1,t0); + /* 2^250 - 2^50 */ for (i = 2;i < 50;i += 2) { square(t0,t1); square(t1,t0); } + /* 2^250 - 2^0 */ mult(t0,t1,z2_50_0); + + /* 2^251 - 2^1 */ square(t1,t0); + /* 2^252 - 2^2 */ square(t0,t1); + /* 2^253 - 2^3 */ square(t1,t0); + /* 2^254 - 2^4 */ square(t0,t1); + /* 2^255 - 2^5 */ square(t1,t0); + /* 2^255 - 21 */ mult(out,t1,z11); +} + +static void crypto_scalarmult(unsigned char *q, + const unsigned char *n, + const unsigned char *p) +{ + unsigned int work[96]; + unsigned char e[32]; + unsigned int i; + for (i = 0;i < 32;++i) e[i] = n[i]; + e[0] &= 248; + e[31] &= 127; + e[31] |= 64; + for (i = 0;i < 32;++i) work[i] = p[i]; + mainloop(work,e); + recip(work + 32,work + 32); + mult(work + 64,work,work + 32); + freeze(work + 64); + for (i = 0;i < 32;++i) q[i] = work[64 + i]; +} + +void cf_curve25519_mul(uint8_t out[32], const uint8_t scalar[32], const uint8_t point[32]) +{ + crypto_scalarmult(out, scalar, point); +} + +void cf_curve25519_mul_base(uint8_t out[32], const uint8_t scalar[32]) +{ + uint8_t base_point[32] = { 9 }; + cf_curve25519_mul(out, scalar, base_point); +} diff --git a/deps/picoquic/picotls/deps/cifra/src/curve25519.tweetnacl.c b/deps/picoquic/picotls/deps/cifra/src/curve25519.tweetnacl.c new file mode 100644 index 0000000..c98c107 --- /dev/null +++ b/deps/picoquic/picotls/deps/cifra/src/curve25519.tweetnacl.c @@ -0,0 +1,235 @@ +/* This is based on tweetnacl. Some typedefs have been + * replaced with their stdint equivalents. + * + * Original code was public domain. */ + +#include +#include + +#include "handy.h" + +typedef int64_t gf[16]; + +static const uint8_t _0[16], + _9[32] = {9}; +static const gf gf0, + gf1 = {1}, + _121665 = {0xDB41, 1}, + D = {0x78a3, 0x1359, 0x4dca, 0x75eb, + 0xd8ab, 0x4141, 0x0a4d, 0x0070, + 0xe898, 0x7779, 0x4079, 0x8cc7, + 0xfe73, 0x2b6f, 0x6cee, 0x5203}, + D2 = {0xf159, 0x26b2, 0x9b94, 0xebd6, + 0xb156, 0x8283, 0x149a, 0x00e0, + 0xd130, 0xeef3, 0x80f2, 0x198e, + 0xfce7, 0x56df, 0xd9dc, 0x2406}, + X = {0xd51a, 0x8f25, 0x2d60, 0xc956, + 0xa7b2, 0x9525, 0xc760, 0x692c, + 0xdc5c, 0xfdd6, 0xe231, 0xc0a4, + 0x53fe, 0xcd6e, 0x36d3, 0x2169}, + Y = {0x6658, 0x6666, 0x6666, 0x6666, + 0x6666, 0x6666, 0x6666, 0x6666, + 0x6666, 0x6666, 0x6666, 0x6666, + 0x6666, 0x6666, 0x6666, 0x6666}, + I = {0xa0b0, 0x4a0e, 0x1b27, 0xc4ee, + 0xe478, 0xad2f, 0x1806, 0x2f43, + 0xd7a7, 0x3dfb, 0x0099, 0x2b4d, + 0xdf0b, 0x4fc1, 0x2480, 0x2b83}; + +static void set25519(gf r, const gf a) +{ + size_t i; + for (i = 0; i < 16; i++) + r[i] = a[i]; +} + +static void car25519(gf o) +{ + int64_t c; + size_t i; + + for (i = 0; i < 16; i++) + { + o[i] += (1LL << 16); + c = o[i] >> 16; + o[(i + 1) * (i < 15)] += c - 1 + 37 * (c - 1) * (i == 15); + o[i] -= c << 16; + } +} + +static void sel25519(gf p, gf q, int64_t b) +{ + int64_t tmp, mask = ~(b-1); + size_t i; + for (i = 0; i < 16; i++) + { + tmp = mask & (p[i] ^ q[i]); + p[i] ^= tmp; + q[i] ^= tmp; + } +} + +static void pack25519(uint8_t out[32], const gf n) +{ + size_t i, j; + int b; + gf m, t; + set25519(t, n); + car25519(t); + car25519(t); + car25519(t); + + for(j = 0; j < 2; j++) + { + m[0] = t[0] - 0xffed; + for (i = 1; i < 15; i++) + { + m[i] = t[i] - 0xffff - ((m[i - 1] >> 16) & 1); + m[i - 1] &= 0xffff; + } + m[15] = t[15] - 0x7fff - ((m[14] >> 16) & 1); + b = (m[15] >> 16) & 1; + m[14] &= 0xffff; + sel25519(t, m, 1 - b); + } + + for (i = 0; i < 16; i++) + { + out[2 * i] = t[i] & 0xff; + out[2 * i + 1] = (uint8_t) (t[i] >> 8); + } +} + + + +static void unpack25519(gf o, const uint8_t *n) +{ + size_t i; + for (i = 0; i < 16; i++) + o[i] = n[2 * i] + ((int64_t) n[2 * i + 1] << 8); + o[15] &= 0x7fff; +} + +static void add(gf o, const gf a, const gf b) +{ + size_t i; + for (i = 0; i < 16; i++) + o[i] = a[i] + b[i]; +} + +static void sub(gf o, const gf a, const gf b) +{ + size_t i; + for (i = 0; i < 16; i++) + o[i] = a[i] - b[i]; +} + +static void mul(gf o, const gf a, const gf b) +{ + int64_t t[31]; + size_t i, j; + + for (i = 0; i < 31; i++) + t[i] = 0; + + for (i = 0; i < 16; i++) + for (j = 0; j < 16; j++) + t[i + j] += a[i] * b[j]; + + for (i = 0; i < 15; i++) + t[i] += 38 * t[i + 16]; + + for (i = 0; i < 16; i++) + o[i] = t[i]; + + car25519(o); + car25519(o); +} + +static void sqr(gf o, const gf a) +{ + mul(o, a, a); +} + +static void inv25519(gf o, const gf i) +{ + gf c; + int a; + for (a = 0; a < 16; a++) + c[a] = i[a]; + + for (a = 253; a >= 0; a--) + { + sqr(c, c); + if(a != 2 && a != 4) + mul(c, c, i); + } + + for (a = 0; a < 16; a++) + o[a] = c[a]; +} + + +void cf_curve25519_mul(uint8_t *q, const uint8_t *n, const uint8_t *p) +{ + uint8_t z[32]; + gf x; + gf a, b, c, d, e, f; + + { + size_t i; + for (i = 0; i < 31; i++) + z[i] = n[i]; + z[31] = (n[31] & 127) | 64; + z[0] &= 248; + + unpack25519(x, p); + + for(i = 0; i < 16; i++) + { + b[i] = x[i]; + d[i] = a[i] = c[i] = 0; + } + } + + a[0] = d[0] = 1; + + {int i; + for (i = 254; i >= 0; i--) + { + int64_t r = (z[i >> 3] >> (i & 7)) & 1; + sel25519(a, b, r); + sel25519(c, d, r); + add(e, a, c); + sub(a, a, c); + add(c, b, d); + sub(b, b, d); + sqr(d, e); + sqr(f, a); + mul(a, c, a); + mul(c, b, e); + add(e, a, c); + sub(a, a, c); + sqr(b, a); + sub(c, d, f); + mul(a, c, _121665); + add(a, a, d); + mul(c, c, a); + mul(a, d, f); + mul(d, b, x); + sqr(b, e); + sel25519(a, b, r); + sel25519(c, d, r); + } + } + + inv25519(c, c); + mul(a, a, c); + pack25519(q, a); +} + +void cf_curve25519_mul_base(uint8_t *q, const uint8_t *n) +{ + cf_curve25519_mul(q, n, _9); +} + diff --git a/deps/picoquic/picotls/deps/cifra/src/drbg.c b/deps/picoquic/picotls/deps/cifra/src/drbg.c new file mode 100644 index 0000000..ae79067 --- /dev/null +++ b/deps/picoquic/picotls/deps/cifra/src/drbg.c @@ -0,0 +1,434 @@ +/* + * cifra - embedded cryptography library + * Written in 2016 by Joseph Birr-Pixton + * + * To the extent possible under law, the author(s) have dedicated all + * copyright and related and neighboring rights to this software to the + * public domain worldwide. This software is distributed without any + * warranty. + * + * You should have received a copy of the CC0 Public Domain Dedication + * along with this software. If not, see + * . + */ + +#include "drbg.h" +#include "handy.h" +#include "bitops.h" +#include "sha2.h" +#include "tassert.h" + +#include + +#define MAX_DRBG_GENERATE 0x10000ul + +static void hash_df(const cf_chash *H, + const void *in1, size_t nin1, + const void *in2, size_t nin2, + const void *in3, size_t nin3, + const void *in4, size_t nin4, + uint8_t *out, size_t nout) +{ + uint8_t counter = 1; + uint32_t bits_to_return = nout * 8; + uint8_t cbuf[4]; + uint8_t block[CF_MAXHASH]; + + write32_be(bits_to_return, cbuf); + + while (nout) + { + /* Make a block. This is the hash of: + * counter || bits_to_return || in1 || in2 || in3 | in4 + */ + cf_chash_ctx ctx; + H->init(&ctx); + H->update(&ctx, &counter, sizeof counter); + H->update(&ctx, cbuf, sizeof cbuf); + H->update(&ctx, in1, nin1); + H->update(&ctx, in2, nin2); + H->update(&ctx, in3, nin3); + H->update(&ctx, in4, nin4); + H->digest(&ctx, block); + + size_t take = MIN(H->hashsz, nout); + memcpy(out, block, take); + out += take; + nout -= take; + + counter += 1; + } +} + +void cf_hash_drbg_sha256_init(cf_hash_drbg_sha256 *ctx, + const void *entropy, size_t nentropy, + const void *nonce, size_t nnonce, + const void *persn, size_t npersn) +{ + mem_clean(ctx, sizeof *ctx); + + /* 1. seed_material = entropy_input || nonce || personalization_string + * 2. seed = Hash_df(seed_material, seedlen) + * 3. V = seed */ + hash_df(&cf_sha256, + entropy, nentropy, + nonce, nnonce, + persn, npersn, + NULL, 0, + ctx->V, sizeof ctx->V); + + /* 4. C = Hash_df(0x00 || V, seedlen) */ + uint8_t zero = 0; + hash_df(&cf_sha256, + &zero, sizeof zero, + ctx->V, sizeof ctx->V, + NULL, 0, + NULL, 0, + ctx->C, sizeof ctx->C); + + /* 5. reseed_counter = 1 */ + ctx->reseed_counter = 1; +} + +/* Add out += in, mod 2^nout. + * Runs in time dependent on nout and nin, but not the contents of out or in. + */ +static void add(uint8_t *out, size_t nout, const uint8_t *in, size_t nin) +{ + assert(nout >= nin); + + uint16_t carry = 0; + int oi, ii; + + for (oi = nout - 1, ii = nin - 1; + oi >= 0; + ii--, oi--) + { + carry += out[oi]; + if (ii >= 0) + carry += in[ii]; + out[oi] = carry & 0xff; + carry >>= 8; + } +} + +static void hash_process_addnl(const cf_chash *H, + const void *input, size_t ninput, + uint8_t *V, size_t nV) +{ + if (!ninput) + return; + + /* 2.1. w = Hash(0x02 || V || additional_input) */ + uint8_t two = 2; + uint8_t w[CF_MAXHASH]; + cf_chash_ctx ctx; + H->init(&ctx); + H->update(&ctx, &two, sizeof two); + H->update(&ctx, V, nV); + H->update(&ctx, input, ninput); + H->digest(&ctx, w); + + /* 2.2. V = (V + w) mod 2 ^ seedlen */ + add(V, nV, w, H->hashsz); +} + +static void hash_generate(const cf_chash *H, + uint8_t *data, size_t ndata, /* initialised with V */ + void *out, size_t nout) +{ + cf_chash_ctx ctx; + uint8_t w[CF_MAXHASH]; + uint8_t *bout = out; + uint8_t one = 1; + + while (nout) + { + /* 4.1. w = Hash(data) */ + H->init(&ctx); + H->update(&ctx, data, ndata); + H->digest(&ctx, w); + + /* 4.2. W = W || w */ + size_t take = MIN(H->hashsz, nout); + memcpy(bout, w, take); + bout += take; + nout -= take; + + /* 4.3. data = (data + 1) mod 2 ^ seedlen */ + add(data, ndata, &one, sizeof one); + } +} + +static void hash_step(const cf_chash *H, + uint8_t *V, size_t nV, + const uint8_t *C, size_t nC, + uint32_t *reseed_counter) +{ + /* 4. h = Hash(0x03 || V) */ + uint8_t h[CF_MAXHASH]; + uint8_t three = 3; + cf_chash_ctx ctx; + + H->init(&ctx); + H->update(&ctx, &three, sizeof three); + H->update(&ctx, V, nV); + H->digest(&ctx, h); + + /* 5. V = (V + h + C + reseed_counter) mod 2 ^ seedlen */ + uint8_t reseed_counter_buf[4]; + write32_be(*reseed_counter, reseed_counter_buf); + + add(V, nV, h, H->hashsz); + add(V, nV, C, nC); + add(V, nV, reseed_counter_buf, sizeof reseed_counter_buf); + + /* 6. reseed_counter = reseed_counter + 1 */ + *reseed_counter = *reseed_counter + 1; +} + +/* This is Hash_DRBG_Generate_algorithm. + * nout is a maximum of MAX_DRBG_GENERATE */ +static void hash_gen_request(cf_hash_drbg_sha256 *ctx, + const void *addnl, size_t naddnl, + void *out, size_t nout) +{ + uint8_t data[440/8]; /* a temporary copy of V, which gets incremented by generate */ + + assert(!cf_hash_drbg_sha256_needs_reseed(ctx)); + + hash_process_addnl(&cf_sha256, addnl, naddnl, ctx->V, sizeof ctx->V); + assert(sizeof data == sizeof ctx->V); + memcpy(data, ctx->V, sizeof ctx->V); + hash_generate(&cf_sha256, data, sizeof data, out, nout); + hash_step(&cf_sha256, ctx->V, sizeof ctx->V, ctx->C, sizeof ctx->C, &ctx->reseed_counter); +} + +void cf_hash_drbg_sha256_gen_additional(cf_hash_drbg_sha256 *ctx, + const void *addnl, size_t naddnl, + void *out, size_t nout) +{ + uint8_t *bout = out; + + /* Generate output in requests of MAX_DRBG_GENERATE in size. */ + while (nout != 0) + { + size_t take = MIN(MAX_DRBG_GENERATE, nout); + hash_gen_request(ctx, addnl, naddnl, bout, take); + bout += take; + nout -= take; + + /* Add additional data only once. */ + addnl = NULL; + naddnl = 0; + } +} + +void cf_hash_drbg_sha256_gen(cf_hash_drbg_sha256 *ctx, + void *out, size_t nout) +{ + cf_hash_drbg_sha256_gen_additional(ctx, + NULL, 0, + out, nout); +} + +void cf_hash_drbg_sha256_reseed(cf_hash_drbg_sha256 *ctx, + const void *entropy, size_t nentropy, + const void *addnl, size_t naddnl) +{ + /* 1. seed_material = 0x01 || V || entropy_input || additional_input + * 2. seed = Hash_df(seed_material, seedlen) + * 3. V = seed */ + uint8_t one = 1; + /* stash V in C, because it cannot alias output */ + memcpy(ctx->C, ctx->V, sizeof ctx->C); + hash_df(&cf_sha256, + &one, sizeof one, + ctx->C, sizeof ctx->C, + entropy, nentropy, + addnl, naddnl, + ctx->V, sizeof ctx->V); + + /* 4. C = Hash_df(0x00 || V, seedlen) */ + uint8_t zero = 0; + hash_df(&cf_sha256, + &zero, sizeof zero, + ctx->V, sizeof ctx->V, + NULL, 0, + NULL, 0, + ctx->C, sizeof ctx->C); + + /* 5. reseed_counter = 1 */ + ctx->reseed_counter = 1; +} + +uint32_t cf_hash_drbg_sha256_needs_reseed(const cf_hash_drbg_sha256 *ctx) +{ + /* we need reseeding after 2 ^ 32 - 1 requests. */ + return ctx->reseed_counter == 0; +} + +/* --- HMAC_DRBG --- */ + +/* provided_data is in1 || in2 || in3. + * K is already scheduled in ctx->hmac. */ +static void hmac_drbg_update(cf_hmac_drbg *ctx, + const void *in1, size_t nin1, + const void *in2, size_t nin2, + const void *in3, size_t nin3) +{ + cf_hmac_ctx local; + const cf_chash *H = ctx->hmac.hash; + uint8_t new_key[CF_MAXHASH]; + uint8_t zero = 0; + + /* 1. K = HMAC(K, V || 0x00 || provided_data) */ + local = ctx->hmac; + cf_hmac_update(&local, ctx->V, H->hashsz); + cf_hmac_update(&local, &zero, sizeof zero); + cf_hmac_update(&local, in1, nin1); + cf_hmac_update(&local, in2, nin2); + cf_hmac_update(&local, in3, nin3); + cf_hmac_finish(&local, new_key); + cf_hmac_init(&ctx->hmac, H, new_key, H->hashsz); + mem_clean(new_key, sizeof new_key); + + /* 2. V = HMAC(K, V) */ + local = ctx->hmac; + cf_hmac_update(&local, ctx->V, H->hashsz); + cf_hmac_finish(&local, ctx->V); + + /* 3. if (provided_data = null) then return K and V */ + if (nin1 == 0 && nin2 == 0 && nin3 == 0) + return; + + /* 4. K = HMAC(K, V || 0x01 || provided_data) */ + uint8_t one = 1; + local = ctx->hmac; + cf_hmac_update(&local, ctx->V, H->hashsz); + cf_hmac_update(&local, &one, sizeof one); + cf_hmac_update(&local, in1, nin1); + cf_hmac_update(&local, in2, nin2); + cf_hmac_update(&local, in3, nin3); + cf_hmac_finish(&local, new_key); + cf_hmac_init(&ctx->hmac, H, new_key, H->hashsz); + mem_clean(new_key, sizeof new_key); + + /* 5. V = HMAC(K, V) */ + local = ctx->hmac; + cf_hmac_update(&local, ctx->V, H->hashsz); + cf_hmac_finish(&local, ctx->V); +} + +void cf_hmac_drbg_init(cf_hmac_drbg *ctx, + const cf_chash *hash, + const void *entropy, size_t nentropy, + const void *nonce, size_t nnonce, + const void *persn, size_t npersn) +{ + mem_clean(ctx, sizeof *ctx); + + assert(hash->hashsz <= CF_MAXHASH); + + /* 2. Key = 0x00 00 ... 00 + * 3. V = 0x01 01 ... 01 */ + uint8_t initial_key[CF_MAXHASH]; + memset(initial_key, 0x00, hash->hashsz); + memset(ctx->V, 0x01, hash->hashsz); + cf_hmac_init(&ctx->hmac, hash, initial_key, hash->hashsz); + + /* 1. seed_material = entropy_input || nonce || personalization_string + * 4. (Key, V) = HMAC_DRBG_Update(seed_material, Key, V) */ + hmac_drbg_update(ctx, entropy, nentropy, nonce, nnonce, persn, npersn); + + /* 5. reseed_counter = 1 */ + ctx->reseed_counter = 1; +} + +uint32_t cf_hmac_drbg_needs_reseed(const cf_hmac_drbg *ctx) +{ + return ctx->reseed_counter == 0; +} + +static void hmac_drbg_generate(cf_hmac_drbg *ctx, + const void *addnl, size_t naddnl, + void *out, size_t nout) +{ + /* 1. If reseed_counter > reseed_interval, then return an indication + * that a reseed is required */ + assert(!cf_hmac_drbg_needs_reseed(ctx)); + + /* 2. If additional_input != null, then + * (Key, V) = HMAC_DRBG_Update(additional_input, Key, V) + */ + if (naddnl) + hmac_drbg_update(ctx, addnl, naddnl, NULL, 0, NULL, 0); + + /* 3. temp = Null + * 4. While (len(temp) < requested_number_of_bits) do: + * 4.1. V = HMAC(Key, V) + * 4.2. temp = temp || V + * 5. returned_bits = leftmost(temp, requested_number_of_bits) + * + * We write the contents of temp directly into the caller's + * out buffer. + */ + uint8_t *bout = out; + cf_hmac_ctx local; + + while (nout) + { + local = ctx->hmac; + cf_hmac_update(&local, ctx->V, ctx->hmac.hash->hashsz); + cf_hmac_finish(&local, ctx->V); + + size_t take = MIN(ctx->hmac.hash->hashsz, nout); + memcpy(bout, ctx->V, take); + bout += take; + nout -= take; + } + + /* 6. (Key, V) = HMAC_DRBG_Update(additional_input, Key, V) */ + hmac_drbg_update(ctx, addnl, naddnl, NULL, 0, NULL, 0); + + /* 7. reseed_counter = reseed_counter + 1 */ + ctx->reseed_counter++; +} + +void cf_hmac_drbg_gen_additional(cf_hmac_drbg *ctx, + const void *addnl, size_t naddnl, + void *out, size_t nout) +{ + uint8_t *bout = out; + + while (nout != 0) + { + size_t take = MIN(MAX_DRBG_GENERATE, nout); + hmac_drbg_generate(ctx, addnl, naddnl, bout, take); + bout += take; + nout -= take; + + /* Add additional data only once. */ + addnl = NULL; + naddnl = 0; + } +} + +void cf_hmac_drbg_gen(cf_hmac_drbg *ctx, void *out, size_t nout) +{ + cf_hmac_drbg_gen_additional(ctx, + NULL, 0, + out, nout); +} + +void cf_hmac_drbg_reseed(cf_hmac_drbg *ctx, + const void *entropy, size_t nentropy, + const void *addnl, size_t naddnl) +{ + /* 1. seed_material = entropy_input || additional_input + * 2. (Key, V) = HMAC_DRBG_Update(seed_material, Key, V) */ + hmac_drbg_update(ctx, entropy, nentropy, addnl, naddnl, NULL, 0); + + /* 3. reseed_counter = 1 */ + ctx->reseed_counter = 1; +} diff --git a/deps/picoquic/picotls/deps/cifra/src/drbg.h b/deps/picoquic/picotls/deps/cifra/src/drbg.h new file mode 100644 index 0000000..18a1070 --- /dev/null +++ b/deps/picoquic/picotls/deps/cifra/src/drbg.h @@ -0,0 +1,191 @@ +/* + * cifra - embedded cryptography library + * Written in 2016 by Joseph Birr-Pixton + * + * To the extent possible under law, the author(s) have dedicated all + * copyright and related and neighboring rights to this software to the + * public domain worldwide. This software is distributed without any + * warranty. + * + * You should have received a copy of the CC0 Public Domain Dedication + * along with this software. If not, see + * . + */ + +#ifndef DRBG_H +#define DRBG_H + +#include +#include + +#include "chash.h" +#include "hmac.h" + +/** + * Hash_DRBG + * ========= + * This is Hash_DRBG from SP800-90A rev 1, with SHA256 as + * the underlying hash function. + * + * This generator enforces a `reseed_interval` of 2^32-1: + * use :c:func:`cf_hash_drbg_sha256_needs_reseed` to check + * whether you need to reseed before use, and reseed using + * :c:func:`cf_hash_drbg_sha256_reseed`. If you try to use + * the generator when it thinks it needs reseeding, it will + * call `abort`. + * + * Internally it enforces a `max_number_of_bits_per_request` + * of 2^19 bits. It sorts out chunking up multiple requests + * for you though, so feel free to ask for more than 2^16 bytes + * at a time. If you provide additional input when doing that, + * it is added only once, on the first subrequest. + * + * It does not enforce any `max_length` or + * `max_personalization_string_length`. + */ + +/* .. c:type:: cf_hash_drbg_sha256 + * Hash_DRBG with SHA256 context. + * + * .. c:member:: cf_hash_drbg_sha256.V + * Current internal state. + * + * .. c:member:: cf_hash_drbg_sha256.C + * Current update offset. + * + * .. c:member:: cf_hash_drbg_sha256.reseed_counter + * Current number of times entropy has been extracted from + * generator. + */ +typedef struct +{ + uint8_t V[440/8]; + uint8_t C[440/8]; + uint32_t reseed_counter; +} cf_hash_drbg_sha256; + +/* .. c:function:: $DECL + * Initialises the generator state `ctx`, using the provided `entropy`, + * `nonce` and personalisation string `persn`. + */ +extern void cf_hash_drbg_sha256_init(cf_hash_drbg_sha256 *ctx, + const void *entropy, size_t nentropy, + const void *nonce, size_t nnonce, + const void *persn, size_t npersn); + +/* .. c:function:: $DECL + * Returns non-zero if the generator needs reseeding. If + * this function returns non-zero, the next :c:func:`cf_hash_drbg_sha256_gen` + * or :c:func:`cf_hash_drbg_sha256_gen_additional` call will call `abort`. + */ +extern uint32_t cf_hash_drbg_sha256_needs_reseed(const cf_hash_drbg_sha256 *ctx); + +/* .. c:function:: $DECL + * Reseeds the generator with the given `entropy` and additional data `addnl`. + */ +extern void cf_hash_drbg_sha256_reseed(cf_hash_drbg_sha256 *ctx, + const void *entropy, size_t nentropy, + const void *addnl, size_t naddnl); + +/* .. c:function:: $DECL + * Generates pseudo-random output, writing `nout` bytes at `out`. + * This function aborts if the generator needs seeding. + */ +extern void cf_hash_drbg_sha256_gen(cf_hash_drbg_sha256 *ctx, + void *out, size_t nout); + +/* .. c:function:: $DECL + * Generates pseudo-random output, writing `nout` bytes at `out`. + * At the same time, `addnl` is input to the generator as further + * entropy. + * This function aborts if the generator needs seeding. + */ +extern void cf_hash_drbg_sha256_gen_additional(cf_hash_drbg_sha256 *ctx, + const void *addnl, size_t naddnl, + void *out, size_t nout); + +/** + * HMAC_DRBG + * ========= + * This is HMAC_DRBG from SP800-90a r1 with any hash function. + * + * This generator enforces a `reseed_interval` of 2^32-1: + * use :c:func:`cf_hmac_drbg_needs_reseed` to check whether + * you need to reseed before use, and reseed using + * :c:func:`cf_hmac_drbg_reseed`. If you try to use the + * generator when it thinks it needs reseeding, it will + * call `abort`. + * + * Internally it enforces a `max_number_of_bits_per_request` + * of 2^19 bits. It sorts out chunking up multiple requests + * for you though, so feel free to ask for more than 2^16 bytes + * at a time. If you provide additional input when doing that, + * it is added only once, on the first subrequest. + * + * It does not enforce any `max_length` or + * `max_personalization_string_length`. + */ + +/* .. c:type:: cf_hmac_drbg + * HMAC_DRBG context. + * + * .. c:member:: cf_hmac_drbg.V + * Current internal state. + * + * .. c:member:: cf_hmac_drbg.hmac + * Current HMAC context, with key scheduled in it. + * + * .. c:member:: cf_hmac_drbg.reseed_counter + * Current number of times entropy has been extracted from + * generator. + */ +typedef struct +{ + uint8_t V[CF_MAXHASH]; + cf_hmac_ctx hmac; /* pristine context with key scheduled */ + uint32_t reseed_counter; +} cf_hmac_drbg; + +/* .. c:function:: $DECL + * Initialises the generator state `ctx`, using the provided `entropy`, + * `nonce` and personalisation string `persn`. + */ +extern void cf_hmac_drbg_init(cf_hmac_drbg *ctx, + const cf_chash *hash, + const void *entropy, size_t nentropy, + const void *nonce, size_t nnonce, + const void *persn, size_t npersn); + +/* .. c:function:: $DECL + * Returns non-zero if the generator needs reseeding. If + * this function returns non-zero, the next :c:func:`cf_hmac_drbg_gen` + * or :c:func:`cf_hmac_drbg_gen_additional` call will call `abort`. + */ +extern uint32_t cf_hmac_drbg_needs_reseed(const cf_hmac_drbg *ctx); + +/* .. c:function:: $DECL + * Reseeds the generator with the given `entropy` and additional data + * `addnl`. + */ +extern void cf_hmac_drbg_reseed(cf_hmac_drbg *ctx, + const void *entropy, size_t nentropy, + const void *addnl, size_t naddnl); + +/* .. c:function:: $DECL + * Generates pseudo-random output, writing `nout` bytes at `out`. + * This function aborts if the generator needs seeding. + */ +extern void cf_hmac_drbg_gen(cf_hmac_drbg *ctx, + void *out, size_t nout); + +/* .. c:function:: $DECL + * Generates pseudo-random output, writing `nout` bytes at `out`. + * At the same time, `addnl` is input to the generator as further + * entropy. + * This function aborts if the generator needs seeding. + */ +extern void cf_hmac_drbg_gen_additional(cf_hmac_drbg *ctx, + const void *addnl, size_t naddnl, + void *out, size_t nout); + +#endif diff --git a/deps/picoquic/picotls/deps/cifra/src/eax.c b/deps/picoquic/picotls/deps/cifra/src/eax.c new file mode 100644 index 0000000..80ba46f --- /dev/null +++ b/deps/picoquic/picotls/deps/cifra/src/eax.c @@ -0,0 +1,116 @@ +/* + * cifra - embedded cryptography library + * Written in 2014 by Joseph Birr-Pixton + * + * To the extent possible under law, the author(s) have dedicated all + * copyright and related and neighboring rights to this software to the + * public domain worldwide. This software is distributed without any + * warranty. + * + * You should have received a copy of the CC0 Public Domain Dedication + * along with this software. If not, see + * . + */ + +#include "handy.h" +#include "prp.h" +#include "modes.h" +#include "tassert.h" + +#include + +static void cmac_compute_n(cf_cmac_stream *ctx, + uint8_t t, + const uint8_t *input, size_t ninput, + uint8_t out[CF_MAXBLOCK]) +{ + size_t blocksz = ctx->cmac.prp->blocksz; + assert(blocksz > 0); + + uint8_t firstblock[CF_MAXBLOCK]; + memset(firstblock, 0, blocksz); + firstblock[blocksz - 1] = t; + + cf_cmac_stream_reset(ctx); + if (ninput) + { + cf_cmac_stream_update(ctx, firstblock, blocksz, 0); + cf_cmac_stream_update(ctx, input, ninput, 1); + } else { + cf_cmac_stream_update(ctx, firstblock, blocksz, 1); + } + + cf_cmac_stream_final(ctx, out); +} + +void cf_eax_encrypt(const cf_prp *prp, void *prpctx, + const uint8_t *plain, size_t nplain, + const uint8_t *header, size_t nheader, + const uint8_t *nonce, size_t nnonce, + uint8_t *cipher, /* the same size as nplain */ + uint8_t *tag, size_t ntag) +{ + uint8_t NN[CF_MAXBLOCK], + HH[CF_MAXBLOCK], + CC[CF_MAXBLOCK]; + + cf_cmac_stream cmac; + cf_cmac_stream_init(&cmac, prp, prpctx); + + /* NN = OMAC_K^0(N) */ + cmac_compute_n(&cmac, 0, nonce, nnonce, NN); + + /* HH = OMAC_K^1(H) */ + cmac_compute_n(&cmac, 1, header, nheader, HH); + + /* C = CTR_K^NN(M) */ + cf_ctr ctr; + cf_ctr_init(&ctr, prp, prpctx, NN); + cf_ctr_cipher(&ctr, plain, cipher, nplain); + + /* CC = OMAC_K^2(C) */ + cmac_compute_n(&cmac, 2, cipher, nplain, CC); + + /* Tag = NN ^ CC ^ HH + * T = Tag [ first tau bits ] */ + assert(ntag <= prp->blocksz); + for (size_t i = 0; i < ntag; i++) + tag[i] = NN[i] ^ CC[i] ^ HH[i]; +} + +int cf_eax_decrypt(const cf_prp *prp, void *prpctx, + const uint8_t *cipher, size_t ncipher, + const uint8_t *header, size_t nheader, + const uint8_t *nonce, size_t nnonce, + const uint8_t *tag, size_t ntag, + uint8_t *plain) /* the same size as ncipher */ +{ + uint8_t NN[CF_MAXBLOCK], + HH[CF_MAXBLOCK], + CC[CF_MAXBLOCK]; + + cf_cmac_stream cmac; + cf_cmac_stream_init(&cmac, prp, prpctx); + + /* NN = OMAC_K^0(N) */ + cmac_compute_n(&cmac, 0, nonce, nnonce, NN); + + /* HH = OMAC_K^1(H) */ + cmac_compute_n(&cmac, 1, header, nheader, HH); + + /* CC = OMAC_K^2(C) */ + cmac_compute_n(&cmac, 2, cipher, ncipher, CC); + + uint8_t tt[CF_MAXBLOCK]; + assert(ntag && ntag <= prp->blocksz); + for (size_t i = 0; i < ntag; i++) + tt[i] = NN[i] ^ CC[i] ^ HH[i]; + + if (!mem_eq(tt, tag, ntag)) + return 1; + + cf_ctr ctr; + cf_ctr_init(&ctr, prp, prpctx, NN); + cf_ctr_cipher(&ctr, cipher, plain, ncipher); + return 0; +} diff --git a/deps/picoquic/picotls/deps/cifra/src/ext/cutest.h b/deps/picoquic/picotls/deps/cifra/src/ext/cutest.h new file mode 100644 index 0000000..c9af425 --- /dev/null +++ b/deps/picoquic/picotls/deps/cifra/src/ext/cutest.h @@ -0,0 +1,620 @@ +/* + * CUTest -- C/C++ Unit Test facility + * + * + * Copyright (c) 2013-2014 Martin Mitas + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef CUTEST_H__ +#define CUTEST_H__ + + +/************************ + *** Public interface *** + ************************/ + +/* By default, provides the main program entry point (function + * main()). However, if the test suite is composed of multiple source files + * which include , then this brings a problem of multiple main() + * definitions. To avoid this problem, #define macro TEST_NO_MAIN in all + * compilation units but one. + */ + +/* Macro to specify list of unit tests in the suite. + * The unit test implementation MUST provide list of unit tests it implements + * with this macro: + * + * TEST_LIST = { + * { "test1_name", test1_func_ptr }, + * { "test2_name", test2_func_ptr }, + * ... + * { 0 } + * }; + * + * The list specifies names of each tests (must be unique) and pointer to + * a function implementing it. The function does not take any arguments + * and have no return values, i.e. the test functions should have this + * prototype: + * + * void test_func(void); + */ +#define TEST_LIST const struct test__ test_list__[] + + +/* Macros for testing whether an unit test succeeds or fails. These macros + * can be used arbitrarily in functions implementing the unit tests. + * + * If any condition fails throughout execution of a test, the test fails. + * + * TEST_CHECK takes only one argument (the condition), TEST_CHECK_ allows + * also to specify an error message to print out if the condition fails. + * (It expects printf-like format string and its parameters). The macros + * return non-zero (condition passes) or 0 (condition fails). + * + * That can be useful when more conditions should be checked only if some + * preceding condition passes, as illustrated here: + * + * SomeStruct* ptr = allocate_some_struct(); + * if(TEST_CHECK(ptr != NULL)) { + * TEST_CHECK(ptr->member1 < 100); + * TEST_CHECK(ptr->member2 > 200); + * } + */ +#define TEST_CHECK_(cond,...) test_check__((cond), __FILE__, __LINE__, __VA_ARGS__) +#define TEST_CHECK(cond) test_check__((cond), __FILE__, __LINE__, "%s", #cond) + + +/********************** + *** Implementation *** + **********************/ + +/* The unit test files should not rely on anything below. */ + +#include +#include +#include +#include + +#if defined(unix) || defined(__unix__) || defined(__unix) || defined(__APPLE__) + #define CUTEST_UNIX__ 1 + #include + #include + #include + #include + #include +#endif + +#if defined(_WIN32) || defined(__WIN32__) || defined(__WINDOWS__) + #define CUTEST_WIN__ 1 + #include + #include +#endif + +#ifdef __cplusplus + #include +#endif + + +/* Note our global private identifiers end with '__' to minimize risk of clash + * with the unit tests implementation. */ + + +#ifdef __cplusplus + extern "C" { +#endif + + +struct test__ { + const char* name; + void (*func)(void); +}; + +extern const struct test__ test_list__[]; +extern int test_verbose_level__; +extern const struct test__* test_current_unit__; +extern int test_current_already_logged__; +extern int test_current_failures__; +extern int test_colorize__; + + +#define CUTEST_COLOR_DEFAULT__ 0 +#define CUTEST_COLOR_GREEN__ 1 +#define CUTEST_COLOR_RED__ 2 +#define CUTEST_COLOR_DEFAULT_INTENSIVE__ 3 +#define CUTEST_COLOR_GREEN_INTENSIVE__ 4 +#define CUTEST_COLOR_RED_INTENSIVE__ 5 + +size_t +test_print_in_color(int color, const char* fmt, ...) +{ + va_list args; + char buffer[256]; + size_t n; + + va_start(args, fmt); + vsnprintf(buffer, sizeof(buffer), fmt, args); + va_end(args); + buffer[sizeof(buffer)-1] = '\0'; + + if(!test_colorize__) { + return printf("%s", buffer); + } + +#if defined CUTEST_UNIX__ + const char* col_str; + switch(color) { + case CUTEST_COLOR_GREEN__: col_str = "\e[0;32m"; break; + case CUTEST_COLOR_RED__: col_str = "\e[0;31m"; break; + case CUTEST_COLOR_GREEN_INTENSIVE__: col_str = "\e[1;32m"; break; + case CUTEST_COLOR_RED_INTENSIVE__: col_str = "\e[1;30m"; break; + case CUTEST_COLOR_DEFAULT_INTENSIVE__: col_str = "\e[1m"; break; + default: col_str = "\e[0m"; break; + } + printf("%s", col_str); + n = printf("%s", buffer); + printf("\e[0m"); + return n; +#elif defined CUTEST_WIN__ + HANDLE h; + CONSOLE_SCREEN_BUFFER_INFO info; + WORD attr; + + h = GetStdHandle(STD_OUTPUT_HANDLE); + GetConsoleScreenBufferInfo(h, &info); + + switch(color) { + case CUTEST_COLOR_GREEN__: attr = FOREGROUND_GREEN; break; + case CUTEST_COLOR_RED__: attr = FOREGROUND_RED; break; + case CUTEST_COLOR_GREEN_INTENSIVE__: attr = FOREGROUND_GREEN | FOREGROUND_INTENSITY; break; + case CUTEST_COLOR_RED_INTENSIVE__: attr = FOREGROUND_RED | FOREGROUND_INTENSITY; break; + case CUTEST_COLOR_DEFAULT_INTENSIVE__: attr = FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_INTENSITY; break; + default: attr = 0; break; + } + if(attr != 0) + SetConsoleTextAttribute(h, attr); + n = printf("%s", buffer); + SetConsoleTextAttribute(h, info.wAttributes); + return n; +#else + n = printf("%s", buffer); + return n; +#endif +} + +int +test_check__(int cond, const char* file, int line, const char* fmt, ...) +{ + const char *result_str; + int result_color; + int verbose_level; + + if(cond) { + result_str = "ok"; + result_color = CUTEST_COLOR_GREEN__; + verbose_level = 3; + } else { + if(!test_current_already_logged__ && test_current_unit__ != NULL) { + printf("[ "); + test_print_in_color(CUTEST_COLOR_RED_INTENSIVE__, "FAILED"); + printf(" ]\n"); + } + result_str = "failed"; + result_color = CUTEST_COLOR_RED__; + verbose_level = 2; + test_current_failures__++; + test_current_already_logged__++; + } + + if(test_verbose_level__ >= verbose_level) { + size_t n = 0; + va_list args; + + printf(" "); + + if(file != NULL) + n += printf("%s:%d: Check ", file, line); + + va_start(args, fmt); + n += vprintf(fmt, args); + va_end(args); + + printf("... "); + test_print_in_color(result_color, result_str); + printf("\n"); + test_current_already_logged__++; + } + + return (cond != 0); +} + + +#ifndef TEST_NO_MAIN + +static char* test_argv0__ = NULL; +static int test_count__ = 0; +static int test_no_exec__ = 0; +static int test_no_summary__ = 0; +static int test_skip_mode__ = 0; + +static int test_stat_failed_units__ = 0; +static int test_stat_run_units__ = 0; + +const struct test__* test_current_unit__ = NULL; +int test_current_already_logged__ = 0; +int test_verbose_level__ = 2; +int test_current_failures__ = 0; +int test_colorize__ = 0; + + +static void +test_list_names__(void) +{ + const struct test__* test; + + printf("Unit tests:\n"); + for(test = &test_list__[0]; test->func != NULL; test++) + printf(" %s\n", test->name); +} + +static const struct test__* +test_by_name__(const char* name) +{ + const struct test__* test; + + for(test = &test_list__[0]; test->func != NULL; test++) { + if(strcmp(test->name, name) == 0) + return test; + } + + return NULL; +} + +static int +test_do_run__(const struct test__* test) +{ + test_current_unit__ = test; + test_current_failures__ = 0; + test_current_already_logged__ = 0; + + if(test_verbose_level__ >= 3) { + test_print_in_color(CUTEST_COLOR_DEFAULT_INTENSIVE__, "Test %s:\n", test->name); + test_current_already_logged__++; + } else if(test_verbose_level__ >= 1) { + size_t n; + char spaces[32]; + + n = test_print_in_color(CUTEST_COLOR_DEFAULT_INTENSIVE__, "Test %s... ", test->name); + memset(spaces, ' ', sizeof(spaces)); + if(n < sizeof(spaces)) + printf("%.*s", (int) (sizeof(spaces) - n), spaces); + } else { + test_current_already_logged__ = 1; + } + +#ifdef __cplusplus + try { +#endif + + test->func(); + +#ifdef __cplusplus + } catch(std::exception& e) { + const char* what = e.what(); + if(what != NULL) + test_check__(0, NULL, 0, "Threw std::exception: %s", what); + else + test_check__(0, NULL, 0, "Threw std::exception"); + } catch(...) { + test_check__(0, NULL, 0, "Threw an exception"); + } +#endif + + if(test_verbose_level__ >= 3) { + switch(test_current_failures__) { + case 0: test_print_in_color(CUTEST_COLOR_GREEN_INTENSIVE__, " All conditions have passed.\n\n"); break; + case 1: test_print_in_color(CUTEST_COLOR_RED_INTENSIVE__, " One condition has FAILED.\n\n"); break; + default: test_print_in_color(CUTEST_COLOR_RED_INTENSIVE__, " %d conditions have FAILED.\n\n", test_current_failures__); break; + } + } else if(test_verbose_level__ >= 1 && test_current_failures__ == 0) { + printf("[ "); + test_print_in_color(CUTEST_COLOR_GREEN_INTENSIVE__, "OK"); + printf(" ]\n"); + } + + test_current_unit__ = NULL; + return (test_current_failures__ == 0) ? 0 : -1; +} + +static void +test_error__(const char* fmt, ...) +{ + va_list args; + + if(!test_current_already_logged__ && test_current_unit__ != NULL) { + printf("[ "); + test_print_in_color(CUTEST_COLOR_RED_INTENSIVE__, "FAILED"); + printf(" ]\n"); + } + + if(test_verbose_level__ < 2) + return; + + test_print_in_color(CUTEST_COLOR_RED_INTENSIVE__, " Error: "); + va_start(args, fmt); + vprintf(fmt, args); + va_end(args); + printf("\n"); +} + +static void +test_run__(const struct test__* test) +{ + int failed = 1; + + test_current_unit__ = test; + test_current_already_logged__ = 0; + + if(!test_no_exec__) { + +#if defined(CUTEST_UNIX__) + + pid_t pid; + int exit_code; + + pid = fork(); + if(pid == (pid_t)-1) { + test_error__("Cannot fork. %s [%d]", strerror(errno), errno); + failed = 1; + } else if(pid == 0) { + failed = (test_do_run__(test) != 0); + exit(failed ? 1 : 0); + } else { + waitpid(pid, &exit_code, 0); + if(WIFEXITED(exit_code)) { + switch(WEXITSTATUS(exit_code)) { + case 0: failed = 0; break; /* test has passed. */ + case 1: /* noop */ break; /* "normal" failure. */ + default: test_error__("Unexpected exit code [%d]", WEXITSTATUS(exit_code)); + } + } else if(WIFSIGNALED(exit_code)) { + char tmp[32]; + const char* signame; + switch(WTERMSIG(exit_code)) { + case SIGINT: signame = "SIGINT"; break; + case SIGHUP: signame = "SIGHUP"; break; + case SIGQUIT: signame = "SIGQUIT"; break; + case SIGABRT: signame = "SIGABRT"; break; + case SIGKILL: signame = "SIGKILL"; break; + case SIGSEGV: signame = "SIGSEGV"; break; + case SIGILL: signame = "SIGILL"; break; + case SIGTERM: signame = "SIGTERM"; break; + default: sprintf(tmp, "signal %d", WTERMSIG(exit_code)); signame = tmp; break; + } + test_error__("Test interrupted by %s", signame); + } else { + test_error__("Test ended in an unexpected way [%d]", exit_code); + } + } + +#elif defined(CUTEST_WIN__) + + char buffer[512] = {0}; + STARTUPINFOA startupInfo = {0}; + PROCESS_INFORMATION processInfo; + DWORD exitCode; + + _snprintf(buffer, sizeof(buffer)-1, + "%s --no-exec --no-summary --verbose=%d --color=%s -- \"%s\"", + test_argv0__, test_verbose_level__, + test_colorize__ ? "always" : "never", test->name); + startupInfo.cb = sizeof(STARTUPINFO); + if(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0, NULL, NULL, &startupInfo, &processInfo)) { + WaitForSingleObject(processInfo.hProcess, INFINITE); + GetExitCodeProcess(processInfo.hProcess, &exitCode); + CloseHandle(processInfo.hThread); + CloseHandle(processInfo.hProcess); + failed = (exitCode != 0); + } else { + test_error__("Cannot create unit test subprocess [%ld].", GetLastError()); + failed = 1; + } + +#else + + failed = (test_do_run__(test) != 0); + +#endif + + } else { + failed = (test_do_run__(test) != 0); + } + + test_current_unit__ = NULL; + + test_stat_run_units__++; + if(failed) + test_stat_failed_units__++; +} + +#if defined(CUTEST_WIN__) +static LONG CALLBACK +test_exception_filter__(EXCEPTION_POINTERS *ptrs) +{ + test_error__("Unhandled SEH exception %08lx at %p.", + ptrs->ExceptionRecord->ExceptionCode, + ptrs->ExceptionRecord->ExceptionAddress); + fflush(stdout); + fflush(stderr); + return EXCEPTION_EXECUTE_HANDLER; +} +#endif + +static void +test_help__(void) +{ + printf("Usage: %s [options] [test...]\n", test_argv0__); + printf("Run the specified unit tests; or if the option '--skip' is used, run all\n"); + printf("tests in the suite but those listed. By default, if no tests are specified\n"); + printf("on the command line, all unit tests in the suite are run.\n"); + printf("\n"); + printf("Options:\n"); + printf(" -s, --skip Execute all unit tests but the listed ones\n"); + printf(" --no-exec Do not execute unit tests as child processes\n"); + printf(" --no-summary Suppress printing of test results summary\n"); + printf(" -l, --list List unit tests in the suite and exit\n"); + printf(" -v, --verbose Enable more verbose output\n"); + printf(" --verbose=LEVEL Set verbose level to LEVEL (small integer)\n"); + printf(" --color=WHEN Enable colorized output (WHEN is one of 'auto', 'always', 'never')\n"); + printf(" -h, --help Display this help and exit\n"); + printf("\n"); + test_list_names__(); +} + +int +main(int argc, char** argv) +{ + const struct test__** tests = NULL; + int i, j, n = 0; + int seen_double_dash = 0; + + test_argv0__ = argv[0]; + +#if defined CUTEST_UNIX__ + test_colorize__ = isatty(fileno(stdout)); +#elif defined CUTEST_WIN__ + test_colorize__ = _isatty(_fileno(stdout)); +#else + test_colorize__ = 0; +#endif + + /* Parse options */ + for(i = 1; i < argc; i++) { + if(seen_double_dash || argv[i][0] != '-') { + tests = (const struct test__**) realloc(tests, (n+1) * sizeof(const struct test__*)); + if(tests == NULL) { + fprintf(stderr, "Out of memory.\n"); + exit(2); + } + tests[n] = test_by_name__(argv[i]); + if(tests[n] == NULL) { + fprintf(stderr, "%s: Unrecognized unit test '%s'\n", argv[0], argv[i]); + fprintf(stderr, "Try '%s --list' for list of unit tests.\n", argv[0]); + exit(2); + } + n++; + } else if(strcmp(argv[i], "--") == 0) { + seen_double_dash = 1; + } else if(strcmp(argv[i], "--help") == 0 || strcmp(argv[i], "-h") == 0) { + test_help__(); + exit(0); + } else if(strcmp(argv[i], "--verbose") == 0 || strcmp(argv[i], "-v") == 0) { + test_verbose_level__++; + } else if(strncmp(argv[i], "--verbose=", 10) == 0) { + test_verbose_level__ = atoi(argv[i] + 10); + } else if(strcmp(argv[i], "--color=auto") == 0) { + /* noop (set from above) */ + } else if(strcmp(argv[i], "--color=always") == 0 || strcmp(argv[i], "--color") == 0) { + test_colorize__ = 1; + } else if(strcmp(argv[i], "--color=never") == 0) { + test_colorize__ = 0; + } else if(strcmp(argv[i], "--skip") == 0 || strcmp(argv[i], "-s") == 0) { + test_skip_mode__ = 1; + } else if(strcmp(argv[i], "--no-exec") == 0) { + test_no_exec__ = 1; + } else if(strcmp(argv[i], "--no-summary") == 0) { + test_no_summary__ = 1; + } else if(strcmp(argv[i], "--list") == 0 || strcmp(argv[i], "-l") == 0) { + test_list_names__(); + exit(0); + } else { + fprintf(stderr, "%s: Unrecognized option '%s'\n", argv[0], argv[i]); + fprintf(stderr, "Try '%s --help' for more information.\n", argv[0]); + exit(2); + } + } + +#if defined(CUTEST_WIN__) + SetUnhandledExceptionFilter(test_exception_filter__); +#endif + + /* Count all test units */ + test_count__ = 0; + for(i = 0; test_list__[i].func != NULL; i++) + test_count__++; + + /* Run the tests */ + if(n == 0) { + /* Run all tests */ + for(i = 0; test_list__[i].func != NULL; i++) + test_run__(&test_list__[i]); + } else if(!test_skip_mode__) { + /* Run the listed tests */ + for(i = 0; i < n; i++) + test_run__(tests[i]); + } else { + /* Run all tests except those listed */ + int is_skipped; + + for(i = 0; test_list__[i].func != NULL; i++) { + is_skipped = 0; + for(j = 0; j < n; j++) { + if(tests[j] == &test_list__[i]) { + is_skipped = 1; + break; + } + } + if(!is_skipped) + test_run__(&test_list__[i]); + } + } + + /* Write a summary */ + if(!test_no_summary__ && test_verbose_level__ >= 1) { + test_print_in_color(CUTEST_COLOR_DEFAULT_INTENSIVE__, "\nSummary:\n"); + + if(test_verbose_level__ >= 3) { + printf(" Count of all unit tests: %4d\n", test_count__); + printf(" Count of run unit tests: %4d\n", test_stat_run_units__); + printf(" Count of failed unit tests: %4d\n", test_stat_failed_units__); + printf(" Count of skipped unit tests: %4d\n", test_count__ - test_stat_run_units__); + } + + if(test_stat_failed_units__ == 0) { + test_print_in_color(CUTEST_COLOR_GREEN_INTENSIVE__, + " SUCCESS: All unit tests have passed.\n"); + } else { + test_print_in_color(CUTEST_COLOR_RED_INTENSIVE__, + " FAILED: %d of %d unit tests have failed.\n", + test_stat_failed_units__, test_stat_run_units__); + } + } + + if(tests != NULL) + free(tests); + + return (test_stat_failed_units__ == 0) ? 0 : 1; +} + + +#endif /* #ifndef TEST_NO_MAIN */ + +#ifdef __cplusplus + } /* extern "C" */ +#endif + + +#endif /* #ifndef CUTEST_H__ */ diff --git a/deps/picoquic/picotls/deps/cifra/src/ext/handy.h b/deps/picoquic/picotls/deps/cifra/src/ext/handy.h new file mode 100644 index 0000000..936e70f --- /dev/null +++ b/deps/picoquic/picotls/deps/cifra/src/ext/handy.h @@ -0,0 +1,66 @@ +#ifndef HANDY_H +#define HANDY_H + +#include +#include +#include + +/* + * Handy CPP defines and C inline functions. + */ + +/* Evaluates to the number of items in array-type variable arr. */ +#define ARRAYCOUNT(arr) (sizeof arr / sizeof arr[0]) + +#ifndef MIN +# define MIN(x, y) ((x) < (y) ? (x) : (y)) +#endif + +/** Stringify its argument. */ +#define STRINGIFY(x) STRINGIFY_(x) +#define STRINGIFY_(x) #x + +/* Error handling macros. + * + * These expect a zero = success, non-zero = error convention. + */ + +/** Error: return. + * + * If the expression fails, return the error from this function. */ +#define ER(expr) do { typeof (expr) err_ = (expr); if (err_) return err_; } while (0) + +/** Error: goto. + * + * If the expression fails, goto x_err. Assumes defn of label + * x_err and 'error_type err'. */ +#define EG(expr) do { err = (expr); if (err) goto x_err; } while (0) + +/** Like memset(ptr, 0, len), but not allowed to be removed by + * compilers. */ +static inline void mem_clean(volatile void *v, size_t len) +{ + if (len) + { + memset((void *) v, 0, len); + (void) *((volatile uint8_t *) v); + } +} + +/** Returns 1 if len bytes at va equal len bytes at vb, 0 if they do not. + * Does not leak length of common prefix through timing. */ +static inline unsigned mem_eq(const void *va, const void *vb, size_t len) +{ + const volatile uint8_t *a = va; + const volatile uint8_t *b = vb; + uint8_t diff = 0; + + while (len--) + { + diff |= *a++ ^ *b++; + } + + return !diff; +} + +#endif diff --git a/deps/picoquic/picotls/deps/cifra/src/gcm.c b/deps/picoquic/picotls/deps/cifra/src/gcm.c new file mode 100644 index 0000000..5b37473 --- /dev/null +++ b/deps/picoquic/picotls/deps/cifra/src/gcm.c @@ -0,0 +1,249 @@ +/* + * cifra - embedded cryptography library + * Written in 2014 by Joseph Birr-Pixton + * + * To the extent possible under law, the author(s) have dedicated all + * copyright and related and neighboring rights to this software to the + * public domain worldwide. This software is distributed without any + * warranty. + * + * You should have received a copy of the CC0 Public Domain Dedication + * along with this software. If not, see + * . + */ + +#include "handy.h" +#include "prp.h" +#include "modes.h" +#include "blockwise.h" +#include "bitops.h" +#include "gf128.h" +#include "tassert.h" + +#include + +#define STATE_INVALID 0 +#define STATE_AAD 1 +#define STATE_CIPHER 2 + +static void ghash_init(ghash_ctx *ctx, uint8_t H[16]) +{ + memset(ctx, 0, sizeof *ctx); + cf_gf128_frombytes_be(H, ctx->H); + ctx->state = STATE_AAD; +} + +static void ghash_block(void *vctx, const uint8_t *data) +{ + ghash_ctx *ctx = vctx; + cf_gf128 gfdata; + cf_gf128_frombytes_be(data, gfdata); + cf_gf128_add(gfdata, ctx->Y, ctx->Y); + cf_gf128_mul(ctx->Y, ctx->H, ctx->Y); +} + +static void ghash_add(ghash_ctx *ctx, const uint8_t *buf, size_t n) +{ + cf_blockwise_accumulate(ctx->buffer, &ctx->buffer_used, + sizeof ctx->buffer, + buf, n, + ghash_block, + ctx); +} + +static void ghash_add_pad(ghash_ctx *ctx) +{ + if (ctx->buffer_used == 0) + return; + + memset(ctx->buffer + ctx->buffer_used, 0, sizeof(ctx->buffer) - ctx->buffer_used); + ghash_block(ctx, ctx->buffer); + ctx->buffer_used = 0; +} + +static void ghash_add_aad(ghash_ctx *ctx, const uint8_t *buf, size_t n) +{ + assert(ctx->state == STATE_AAD); + ctx->len_aad += n; + ghash_add(ctx, buf, n); +} + +static void ghash_add_cipher(ghash_ctx *ctx, const uint8_t *buf, size_t n) +{ + if (ctx->state == STATE_AAD) + { + ghash_add_pad(ctx); + ctx->state = STATE_CIPHER; + } + + assert(ctx->state == STATE_CIPHER); + ctx->len_cipher += n; + ghash_add(ctx, buf, n); +} + +static void ghash_final(ghash_ctx *ctx, uint8_t out[16]) +{ + uint8_t lenbuf[8]; + + if (ctx->state == STATE_AAD || ctx->state == STATE_CIPHER) + { + ghash_add_pad(ctx); + ctx->state = STATE_INVALID; + } + + /* Add len(A) || len(C) */ + write64_be(ctx->len_aad * 8, lenbuf); + ghash_add(ctx, lenbuf, sizeof lenbuf); + + write64_be(ctx->len_cipher * 8, lenbuf); + ghash_add(ctx, lenbuf, sizeof lenbuf); + + assert(ctx->buffer_used == 0); + cf_gf128_tobytes_be(ctx->Y, out); +} + +void cf_gcm_encrypt_init(const cf_prp *prp, void *prpctx, cf_gcm_ctx *gcmctx, + const uint8_t *header, size_t nheader, + const uint8_t *nonce, size_t nnonce) +{ + uint8_t H[16] = { 0 }; + + /* H = E_K(0^128) */ + prp->encrypt(prpctx, H, H); + + /* Produce CTR nonce, Y_0: + * + * if len(IV) == 96 + * Y_0 = IV || 0^31 || 1 + * otherwise + * Y_0 = GHASH(H, {}, IV) + */ + + if (nnonce == 12) + { + memcpy(gcmctx->Y0, nonce, nnonce); + gcmctx->Y0[12] = gcmctx->Y0[13] = gcmctx->Y0[14] = 0x00; + gcmctx->Y0[15] = 0x01; + } else { + ghash_init(&gcmctx->gh, H); + ghash_add_cipher(&gcmctx->gh, nonce, nnonce); + ghash_final(&gcmctx->gh, gcmctx->Y0); + } + + /* Hash AAD */ + ghash_init(&gcmctx->gh, H); + ghash_add_aad(&gcmctx->gh, header, nheader); + + /* Produce ciphertext */ + memset(gcmctx->e_Y0, 0, sizeof(gcmctx->e_Y0)); + cf_ctr_init(&gcmctx->ctr, prp, prpctx, gcmctx->Y0); + cf_ctr_custom_counter(&gcmctx->ctr, 12, 4); /* counter is 2^32 */ + cf_ctr_cipher(&gcmctx->ctr, gcmctx->e_Y0, gcmctx->e_Y0, sizeof gcmctx->e_Y0); /* first block is tag offset */ + + mem_clean(H, sizeof H); +} + +void cf_gcm_encrypt_update(cf_gcm_ctx *gcmctx, const uint8_t *plain, size_t nplain, uint8_t *cipher) +{ + cf_ctr_cipher(&gcmctx->ctr, plain, cipher, nplain); + ghash_add_cipher(&gcmctx->gh, cipher, nplain); +} + +void cf_gcm_encrypt_final(cf_gcm_ctx *gcmctx, uint8_t *tag, size_t ntag) +{ + /* Post-process ghash output */ + uint8_t full_tag[16] = { 0 }; + ghash_final(&gcmctx->gh, full_tag); + + assert(ntag > 1 && ntag <= 16); + xor_bb(tag, full_tag, gcmctx->e_Y0, ntag); + + mem_clean(full_tag, sizeof full_tag); + mem_clean(gcmctx, sizeof *gcmctx); +} + +void cf_gcm_encrypt(const cf_prp *prp, void *prpctx, + const uint8_t *plain, size_t nplain, + const uint8_t *header, size_t nheader, + const uint8_t *nonce, size_t nnonce, + uint8_t *cipher, /* the same size as nplain */ + uint8_t *tag, size_t ntag) +{ + cf_gcm_ctx gcmctx; + + cf_gcm_encrypt_init(prp, prpctx, &gcmctx, header, nheader, nonce, nnonce); + cf_gcm_encrypt_update(&gcmctx, plain, nplain, cipher); + cf_gcm_encrypt_final(&gcmctx, tag, ntag); +} + +int cf_gcm_decrypt(const cf_prp *prp, void *prpctx, + const uint8_t *cipher, size_t ncipher, + const uint8_t *header, size_t nheader, + const uint8_t *nonce, size_t nnonce, + const uint8_t *tag, size_t ntag, + uint8_t *plain) +{ + uint8_t H[16] = { 0 }; + uint8_t Y0[16]; + + /* H = E_K(0^128) */ + prp->encrypt(prpctx, H, H); + + /* Produce CTR nonce, Y_0: + * + * if len(IV) == 96 + * Y_0 = IV || 0^31 || 1 + * otherwise + * Y_0 = GHASH(H, {}, IV) + */ + + if (nnonce == 12) + { + memcpy(Y0, nonce, nnonce); + Y0[12] = Y0[13] = Y0[14] = 0x00; + Y0[15] = 0x01; + } else { + ghash_ctx gh; + ghash_init(&gh, H); + ghash_add_cipher(&gh, nonce, nnonce); + ghash_final(&gh, Y0); + } + + /* Hash AAD. */ + ghash_ctx gh; + ghash_init(&gh, H); + ghash_add_aad(&gh, header, nheader); + + /* Start counter mode, to obtain offset on tag. */ + uint8_t e_Y0[16] = { 0 }; + cf_ctr ctr; + cf_ctr_init(&ctr, prp, prpctx, Y0); + cf_ctr_custom_counter(&ctr, 12, 4); + cf_ctr_cipher(&ctr, e_Y0, e_Y0, sizeof e_Y0); + + /* Hash ciphertext. */ + ghash_add_cipher(&gh, cipher, ncipher); + + /* Produce tag. */ + uint8_t full_tag[16]; + ghash_final(&gh, full_tag); + assert(ntag > 1 && ntag <= 16); + xor_bb(full_tag, full_tag, e_Y0, ntag); + + int err = 1; + if (!mem_eq(full_tag, tag, ntag)) + goto x_err; + + /* Complete decryption. */ + cf_ctr_cipher(&ctr, cipher, plain, ncipher); + err = 0; + +x_err: + mem_clean(H, sizeof H); + mem_clean(Y0, sizeof Y0); + mem_clean(e_Y0, sizeof e_Y0); + mem_clean(full_tag, sizeof full_tag); + mem_clean(&gh, sizeof gh); + mem_clean(&ctr, sizeof ctr); + return err; +} diff --git a/deps/picoquic/picotls/deps/cifra/src/gf128.c b/deps/picoquic/picotls/deps/cifra/src/gf128.c new file mode 100644 index 0000000..f7ea834 --- /dev/null +++ b/deps/picoquic/picotls/deps/cifra/src/gf128.c @@ -0,0 +1,114 @@ +/* + * cifra - embedded cryptography library + * Written in 2014 by Joseph Birr-Pixton + * + * To the extent possible under law, the author(s) have dedicated all + * copyright and related and neighboring rights to this software to the + * public domain worldwide. This software is distributed without any + * warranty. + * + * You should have received a copy of the CC0 Public Domain Dedication + * along with this software. If not, see + * . + */ + +#include "cf_config.h" +#include "gf128.h" +#include "bitops.h" + +#include + +void cf_gf128_tobytes_be(const cf_gf128 in, uint8_t out[16]) +{ + write32_be(in[0], out + 0); + write32_be(in[1], out + 4); + write32_be(in[2], out + 8); + write32_be(in[3], out + 12); +} + +void cf_gf128_frombytes_be(const uint8_t in[16], cf_gf128 out) +{ + out[0] = read32_be(in + 0); + out[1] = read32_be(in + 4); + out[2] = read32_be(in + 8); + out[3] = read32_be(in + 12); +} + +/* out = 2 * in. Arguments may alias. */ +void cf_gf128_double(const cf_gf128 in, cf_gf128 out) +{ + uint8_t table[2] = { 0x00, 0x87 }; + uint32_t borrow = 0; + uint32_t inword; + + inword = in[3]; out[3] = (inword << 1) | borrow; borrow = inword >> 31; + inword = in[2]; out[2] = (inword << 1) | borrow; borrow = inword >> 31; + inword = in[1]; out[1] = (inword << 1) | borrow; borrow = inword >> 31; + inword = in[0]; out[0] = (inword << 1) | borrow; borrow = inword >> 31; + +#if CF_CACHE_SIDE_CHANNEL_PROTECTION + out[3] ^= select_u8(borrow, table, 2); +#else + out[3] ^= table[borrow]; +#endif +} + +/* out = 2 * in. Arguments may alias. */ +void cf_gf128_double_le(const cf_gf128 in, cf_gf128 out) +{ + uint8_t table[2] = { 0x00, 0xe1 }; + uint32_t borrow = 0; + uint32_t inword; + + inword = in[0]; out[0] = (inword >> 1) | (borrow << 31); borrow = inword & 1; + inword = in[1]; out[1] = (inword >> 1) | (borrow << 31); borrow = inword & 1; + inword = in[2]; out[2] = (inword >> 1) | (borrow << 31); borrow = inword & 1; + inword = in[3]; out[3] = (inword >> 1) | (borrow << 31); borrow = inword & 1; + +#if CF_CACHE_SIDE_CHANNEL_PROTECTION + out[0] ^= select_u8(borrow, table, 2) << 24; +#else + out[0] ^= table[borrow] << 24; +#endif +} + +/* out = x + y. Arguments may alias. */ +void cf_gf128_add(const cf_gf128 x, const cf_gf128 y, cf_gf128 out) +{ + out[0] = x[0] ^ y[0]; + out[1] = x[1] ^ y[1]; + out[2] = x[2] ^ y[2]; + out[3] = x[3] ^ y[3]; +} + +/* out = xy. Arguments may alias. */ +void cf_gf128_mul(const cf_gf128 x, const cf_gf128 y, cf_gf128 out) +{ +#if CF_TIME_SIDE_CHANNEL_PROTECTION + cf_gf128 zero = { 0 }; +#endif + + /* Z_0 = 0^128 + * V_0 = Y */ + cf_gf128 Z, V; + memset(Z, 0, sizeof Z); + memcpy(V, y, sizeof V); + + int i; + for (i = 0; i < 128; i++) + { + uint32_t word = x[i >> 5]; + uint8_t bit = (word >> (31 - (i & 31))) & 1; + +#if CF_TIME_SIDE_CHANNEL_PROTECTION + select_xor128(Z, zero, V, bit); +#else + if (bit) + xor_words(Z, V, 4); +#endif + + cf_gf128_double_le(V, V); + } + + memcpy(out, Z, sizeof Z); +} diff --git a/deps/picoquic/picotls/deps/cifra/src/gf128.h b/deps/picoquic/picotls/deps/cifra/src/gf128.h new file mode 100644 index 0000000..7560862 --- /dev/null +++ b/deps/picoquic/picotls/deps/cifra/src/gf128.h @@ -0,0 +1,55 @@ +/* + * cifra - embedded cryptography library + * Written in 2014 by Joseph Birr-Pixton + * + * To the extent possible under law, the author(s) have dedicated all + * copyright and related and neighboring rights to this software to the + * public domain worldwide. This software is distributed without any + * warranty. + * + * You should have received a copy of the CC0 Public Domain Dedication + * along with this software. If not, see + * . + */ + +#ifndef GF128_H +#define GF128_H + +#include +#include + +/** + * @brief Operations in GF(2^128). + * + * These implementations are constant time, but relatively slow. + */ + +typedef uint32_t cf_gf128[4]; + +/* Unpack from big-endian bytes into out. */ +void cf_gf128_frombytes_be(const uint8_t in[16], cf_gf128 out); + +/* Pack in big-endian order into out. */ +void cf_gf128_tobytes_be(const cf_gf128 in, uint8_t out[16]); + +/* out = 2 * in. Arguments may not alias. */ +void cf_gf128_double(const cf_gf128 in, cf_gf128 out); + +/* out = 2 * in. Arguments may not alias. + * This differs from cf_gf128_double because it interprets the + * block in little endian: the lsb is the msb of the + * first element, the msb is the lsb of the last element. + * + * GCM uses this convention. */ +void cf_gf128_double_le(const cf_gf128 in, cf_gf128 out); + +/* out = x + y. Arguments may alias. */ +void cf_gf128_add(const cf_gf128 x, const cf_gf128 y, cf_gf128 out); + +/* out = xy. Arguments may alias. + * + * This uses cf_gf128_double_le internally, and is suitable for + * GCM. */ +void cf_gf128_mul(const cf_gf128 x, const cf_gf128 y, cf_gf128 out); + +#endif diff --git a/deps/picoquic/picotls/deps/cifra/src/hmac.c b/deps/picoquic/picotls/deps/cifra/src/hmac.c new file mode 100644 index 0000000..98646d7 --- /dev/null +++ b/deps/picoquic/picotls/deps/cifra/src/hmac.c @@ -0,0 +1,106 @@ +/* + * cifra - embedded cryptography library + * Written in 2014 by Joseph Birr-Pixton + * + * To the extent possible under law, the author(s) have dedicated all + * copyright and related and neighboring rights to this software to the + * public domain worldwide. This software is distributed without any + * warranty. + * + * You should have received a copy of the CC0 Public Domain Dedication + * along with this software. If not, see + * . + */ + +#include "hmac.h" +#include "chash.h" +#include "bitops.h" +#include "handy.h" +#include "tassert.h" + +#include + +void cf_hmac_init(cf_hmac_ctx *ctx, + const cf_chash *hash, + const uint8_t *key, size_t nkey) +{ + assert(ctx); + assert(hash); + + mem_clean(ctx, sizeof *ctx); + ctx->hash = hash; + + /* Prepare key: */ + uint8_t k[CF_CHASH_MAXBLK]; + + /* Shorten long keys. */ + if (nkey > hash->blocksz) + { + /* Standard doesn't cover case where blocksz < hashsz. + * FIPS186-1 seems to want to append a negative number of zero bytes. + * In any case, we only have a k buffer of CF_CHASH_MAXBLK! */ + assert(hash->hashsz <= hash->blocksz); + + cf_hash(hash, key, nkey, k); + key = k; + nkey = hash->hashsz; + } + + /* Right zero-pad short keys. */ + if (k != key) + memcpy(k, key, nkey); + if (hash->blocksz > nkey) + memset(k + nkey, 0, hash->blocksz - nkey); + + /* Start inner hash computation */ + uint8_t blk[CF_CHASH_MAXBLK]; + + xor_b8(blk, k, 0x36, hash->blocksz); + hash->init(&ctx->inner); + hash->update(&ctx->inner, blk, hash->blocksz); + + /* And outer. */ + xor_b8(blk, k, 0x5c, hash->blocksz); + hash->init(&ctx->outer); + hash->update(&ctx->outer, blk, hash->blocksz); + + mem_clean(blk, sizeof blk); + mem_clean(k, sizeof k); +} + +void cf_hmac_update(cf_hmac_ctx *ctx, const void *data, size_t ndata) +{ + assert(ctx && ctx->hash); + + ctx->hash->update(&ctx->inner, data, ndata); +} + +void cf_hmac_finish(cf_hmac_ctx *ctx, uint8_t *out) +{ + assert(ctx && ctx->hash); + assert(out); + + uint8_t innerh[CF_MAXHASH]; + ctx->hash->digest(&ctx->inner, innerh); + + ctx->hash->update(&ctx->outer, innerh, ctx->hash->hashsz); + ctx->hash->digest(&ctx->outer, out); + + mem_clean(ctx, sizeof *ctx); +} + +void cf_hmac(const uint8_t *key, size_t nkey, + const uint8_t *msg, size_t nmsg, + uint8_t *out, + const cf_chash *hash) +{ + cf_hmac_ctx ctx; + + assert(out); + assert(hash); + + cf_hmac_init(&ctx, hash, key, nkey); + cf_hmac_update(&ctx, msg, nmsg); + cf_hmac_finish(&ctx, out); +} + diff --git a/deps/picoquic/picotls/deps/cifra/src/hmac.h b/deps/picoquic/picotls/deps/cifra/src/hmac.h new file mode 100644 index 0000000..2f9ea7b --- /dev/null +++ b/deps/picoquic/picotls/deps/cifra/src/hmac.h @@ -0,0 +1,78 @@ +/* + * cifra - embedded cryptography library + * Written in 2014 by Joseph Birr-Pixton + * + * To the extent possible under law, the author(s) have dedicated all + * copyright and related and neighboring rights to this software to the + * public domain worldwide. This software is distributed without any + * warranty. + * + * You should have received a copy of the CC0 Public Domain Dedication + * along with this software. If not, see + * . + */ + +#ifndef HMAC_H +#define HMAC_H + +#include +#include + +#include "chash.h" + +/** + * HMAC + * ==== + * This is a one-shot and incremental interface to computing + * HMAC with any hash function. + * + * (Note: HMAC with SHA3 is possible, but is probably not a + * sensible thing to want.) + */ + +/* .. c:type:: cf_hmac_ctx + * HMAC incremental interface context. + * + * .. c:member:: cf_hmac_ctx.hash + * Hash function description. + * + * .. c:member:: cf_hmac_ctx.inner + * Inner hash computation. + * + * .. c:member:: cf_hmac_ctx.outer + * Outer hash computation. + */ +typedef struct +{ + const cf_chash *hash; + cf_chash_ctx inner; + cf_chash_ctx outer; +} cf_hmac_ctx; + +/* .. c:function:: $DECL + * Set up ctx for computing a HMAC using the given hash and key. */ +void cf_hmac_init(cf_hmac_ctx *ctx, + const cf_chash *hash, + const uint8_t *key, size_t nkey); + +/* .. c:function:: $DECL + * Input data. */ +void cf_hmac_update(cf_hmac_ctx *ctx, + const void *data, size_t ndata); + +/* .. c:function:: $DECL + * Finish and compute HMAC. + * `ctx->hash->hashsz` bytes are written to `out`. */ +void cf_hmac_finish(cf_hmac_ctx *ctx, uint8_t *out); + +/* .. c:function:: $DECL + * One shot interface: compute `HMAC_hash(key, msg)`, writing the + * answer (which is `hash->hashsz` long) to `out`. + * + * This function does not fail. */ +void cf_hmac(const uint8_t *key, size_t nkey, + const uint8_t *msg, size_t nmsg, + uint8_t *out, + const cf_chash *hash); + +#endif diff --git a/deps/picoquic/picotls/deps/cifra/src/modes.c b/deps/picoquic/picotls/deps/cifra/src/modes.c new file mode 100644 index 0000000..1e44af1 --- /dev/null +++ b/deps/picoquic/picotls/deps/cifra/src/modes.c @@ -0,0 +1,99 @@ +/* + * cifra - embedded cryptography library + * Written in 2014 by Joseph Birr-Pixton + * + * To the extent possible under law, the author(s) have dedicated all + * copyright and related and neighboring rights to this software to the + * public domain worldwide. This software is distributed without any + * warranty. + * + * You should have received a copy of the CC0 Public Domain Dedication + * along with this software. If not, see + * . + */ + +#include "prp.h" +#include "modes.h" +#include "bitops.h" +#include "blockwise.h" + +#include +#include "tassert.h" + +/* CBC */ +void cf_cbc_init(cf_cbc *ctx, const cf_prp *prp, void *prpctx, const uint8_t iv[CF_MAXBLOCK]) +{ + ctx->prp = prp; + ctx->prpctx = prpctx; + memcpy(ctx->block, iv, prp->blocksz); +} + +void cf_cbc_encrypt(cf_cbc *ctx, const uint8_t *input, uint8_t *output, size_t blocks) +{ + uint8_t buf[CF_MAXBLOCK]; + size_t nblk = ctx->prp->blocksz; + + while (blocks--) + { + xor_bb(buf, input, ctx->block, nblk); + ctx->prp->encrypt(ctx->prpctx, buf, ctx->block); + memcpy(output, ctx->block, nblk); + input += nblk; + output += nblk; + } +} + +void cf_cbc_decrypt(cf_cbc *ctx, const uint8_t *input, uint8_t *output, size_t blocks) +{ + uint8_t buf[CF_MAXBLOCK]; + size_t nblk = ctx->prp->blocksz; + + while (blocks--) + { + ctx->prp->decrypt(ctx->prpctx, input, buf); + xor_bb(output, buf, ctx->block, nblk); + memcpy(ctx->block, input, nblk); + input += nblk; + output += nblk; + } +} + +/* CTR */ +void cf_ctr_init(cf_ctr *ctx, const cf_prp *prp, void *prpctx, const uint8_t nonce[CF_MAXBLOCK]) +{ + memset(ctx, 0, sizeof *ctx); + ctx->counter_offset = 0; + ctx->counter_width = prp->blocksz; + ctx->prp = prp; + ctx->prpctx = prpctx; + ctx->nkeymat = 0; + memcpy(ctx->nonce, nonce, prp->blocksz); +} + +void cf_ctr_custom_counter(cf_ctr *ctx, size_t offset, size_t width) +{ + assert(ctx->prp->blocksz <= offset + width); + ctx->counter_offset = offset; + ctx->counter_width = width; +} + +static void ctr_next_block(void *vctx, uint8_t *out) +{ + cf_ctr *ctx = vctx; + ctx->prp->encrypt(ctx->prpctx, ctx->nonce, out); + incr_be(ctx->nonce + ctx->counter_offset, ctx->counter_width); +} + +void cf_ctr_cipher(cf_ctr *ctx, const uint8_t *input, uint8_t *output, size_t bytes) +{ + cf_blockwise_xor(ctx->keymat, &ctx->nkeymat, + ctx->prp->blocksz, + input, output, bytes, + ctr_next_block, + ctx); +} + +void cf_ctr_discard_block(cf_ctr *ctx) +{ + ctx->nkeymat = 0; +} diff --git a/deps/picoquic/picotls/deps/cifra/src/modes.h b/deps/picoquic/picotls/deps/cifra/src/modes.h new file mode 100644 index 0000000..20940a3 --- /dev/null +++ b/deps/picoquic/picotls/deps/cifra/src/modes.h @@ -0,0 +1,587 @@ +/* + * cifra - embedded cryptography library + * Written in 2014 by Joseph Birr-Pixton + * + * To the extent possible under law, the author(s) have dedicated all + * copyright and related and neighboring rights to this software to the + * public domain worldwide. This software is distributed without any + * warranty. + * + * You should have received a copy of the CC0 Public Domain Dedication + * along with this software. If not, see + * . + */ + +#ifndef MODES_H +#define MODES_H + +#include +#include + +#include "gf128.h" +#include "prp.h" + +/** + * Block cipher modes + * ================== + */ + +/** + * CBC mode + * -------- + * This implementation allows encryption or decryption of whole + * blocks in CBC mode. It does not offer a byte-wise incremental + * interface, or do any padding. + * + * This mode provides no useful integrity and should not be used + * directly. + */ + +/* .. c:type:: cf_cbc + * This structure binds together the things needed to encrypt/decrypt whole + * blocks in CBC mode. + * + * .. c:member:: cf_cbc.prp + * How to encrypt or decrypt blocks. This could be, for example, :c:data:`cf_aes`. + * + * .. c:member:: cf_cbc.prpctx + * Private data for prp functions. For a `prp` of `cf_aes`, this would be a + * pointer to a :c:type:`cf_aes_context` instance. + * + * .. c:member:: cf_cbc.block + * The IV or last ciphertext block. + */ +typedef struct +{ + const cf_prp *prp; + void *prpctx; + uint8_t block[CF_MAXBLOCK]; +} cf_cbc; + +/* .. c:function:: $DECL + * Initialise CBC encryption/decryption context using selected prp, prp context and IV. */ +void cf_cbc_init(cf_cbc *ctx, const cf_prp *prp, void *prpctx, const uint8_t iv[CF_MAXBLOCK]); + +/* .. c:function:: $DECL + * Encrypt blocks in CBC mode. input and output + * must point to blocks * ctx->prp->blocksz bytes of storage (and may alias). */ +void cf_cbc_encrypt(cf_cbc *ctx, const uint8_t *input, uint8_t *output, size_t blocks); + +/* .. c:function:: $DECL + * Decrypt blocks in CBC mode. input and output + * must point to blocks * ctx->prp->blocksz bytes of storage (and may alias). */ +void cf_cbc_decrypt(cf_cbc *ctx, const uint8_t *input, uint8_t *output, size_t blocks); + +/** + * Counter mode + * ------------ + * This implementation allows incremental encryption/decryption of + * messages. Encryption and decryption are the same operation. + * + * The counter is always big-endian, but has configurable location + * and size within the nonce block. The counter wraps, so you + * should make sure the length of a message with a given nonce + * doesn't cause nonce reuse. + * + * This mode provides no integrity and should not be used directly. + */ + +/* .. c:type:: cf_ctr + * + * .. c:member:: cf_ctr.prp + * How to encrypt or decrypt blocks. This could be, for example, :c:data:`cf_aes`. + * + * .. c:member:: cf_ctr.prpctx + * Private data for prp functions. For a `prp` of `cf_aes`, this would be a + * pointer to a :c:type:`cf_aes_context` instance. + * + * .. c:member:: cf_ctr.nonce + * The next block to encrypt to get another block of key stream. + * + * .. c:member:: cf_ctr.keymat + * The current block of key stream. + * + * .. c:member:: cf_ctr.nkeymat + * The number of bytes at the end of :c:member:`keymat` that are so-far unused. + * If this is zero, all the bytes are used up and/or of undefined value. + * + * .. c:member:: cf_ctr.counter_offset + * The offset (in bytes) of the counter block within the nonce. + * + * .. c:member:: cf_ctr.counter_width + * The width (in bytes) of the counter block in the nonce. + */ +typedef struct +{ + const cf_prp *prp; + void *prpctx; + uint8_t nonce[CF_MAXBLOCK]; + uint8_t keymat[CF_MAXBLOCK]; + size_t nkeymat; + size_t counter_offset; + size_t counter_width; +} cf_ctr; + +/* .. c:function:: $DECL + * Initialise CTR encryption/decryption context using selected prp and nonce. + * (nb, this only increments the whole nonce as a big endian block) */ +void cf_ctr_init(cf_ctr *ctx, const cf_prp *prp, void *prpctx, const uint8_t nonce[CF_MAXBLOCK]); + +/* .. c:function:: $DECL + * Set the location and width of the nonce counter. + * + * eg. offset = 12, width = 4 means the counter is mod 2^32 and placed + * at the end of the nonce. */ +void cf_ctr_custom_counter(cf_ctr *ctx, size_t offset, size_t width); + +/* .. c:function:: $DECL + * Encrypt or decrypt bytes in CTR mode. + * input and output may alias and must point to specified number of bytes. */ +void cf_ctr_cipher(cf_ctr *ctx, const uint8_t *input, uint8_t *output, size_t bytes); + +/* .. c:function:: $DECL + * Discards the rest of this block of key stream. */ +void cf_ctr_discard_block(cf_ctr *ctx); + +/** + * CBC-MAC + * ------- + * This is a incremental interface to computing a CBC-MAC tag over a message. + * + * It optionally pads the message with PKCS#5/PKCS#7 padding -- if you don't + * do this, messages must be an exact number of blocks long. + * + * You shouldn't use this directly because it isn't secure for variable-length + * messages. Use CMAC instead. + */ + +/* .. c:type:: cf_cbcmac_stream + * Stream interface to CBC-MAC signing. + * + * .. c:member:: cf_cbcmac.prp + * How to encrypt or decrypt blocks. This could be, for example, :c:data:`cf_aes`. + * + * .. c:member:: cf_cbcmac.prpctx + * Private data for prp functions. For a `prp` of `cf_aes`, this would be a + * pointer to a :c:type:`cf_aes_context` instance. + * + * .. c:member:: cf_cbcmac.cbc + * CBC data. + * + * .. c:member:: cf_cbcmac.buffer + * Buffer for data which can't be processed until we have a full block. + * + * .. c:member:: cf_cbcmac.used + * How many bytes at the front of :c:member:`buffer` are valid. + */ +typedef struct +{ + const cf_prp *prp; + void *prpctx; + cf_cbc cbc; + uint8_t buffer[CF_MAXBLOCK]; + size_t used; +} cf_cbcmac_stream; + +/* .. c:function:: $DECL + * Initialise CBC-MAC signing context using selected prp. */ +void cf_cbcmac_stream_init(cf_cbcmac_stream *ctx, const cf_prp *prp, void *prpctx); + +/* .. c:function:: $DECL + * Reset the streaming signing context, to sign a new message. */ +void cf_cbcmac_stream_reset(cf_cbcmac_stream *ctx); + +/* .. c:function:: $DECL + * Process ndata bytes at data. */ +void cf_cbcmac_stream_update(cf_cbcmac_stream *ctx, const uint8_t *data, size_t ndata); + +/* .. c:function:: $DECL + * Finish the current block of data by adding zeroes. Does nothing if there + * are no bytes awaiting processing. */ +void cf_cbcmac_stream_finish_block_zero(cf_cbcmac_stream *ctx); + +/* .. c:function:: $DECL + * Output the MAC to ctx->prp->blocksz bytes at out. + * ctx->used must be zero: the inputed message must be an exact number of + * blocks. */ +void cf_cbcmac_stream_nopad_final(cf_cbcmac_stream *ctx, uint8_t out[CF_MAXBLOCK]); + +/* .. c:function:: $DECL + * Output the MAC to ctx->prp->blocksz bytes at out. + * + * The message is padded with PKCS#5 padding. */ +void cf_cbcmac_stream_pad_final(cf_cbcmac_stream *ctx, uint8_t out[CF_MAXBLOCK]); + +/** + * CMAC + * ---- + * This is both a one-shot and incremental interface to + * computing a CMAC tag over a message. + * + * The one-shot interface separates out the per-key computation, + * so if you need to compute lots of MACs with one key you don't + * pay that cost more than once. + * + * CMAC is a good choice for a symmetric MAC. + */ + +/* .. c:type:: cf_cmac + * One-shot interface to CMAC signing. + * + * .. c:member:: cf_cmac.prp + * How to encrypt or decrypt blocks. This could be, for example, :c:data:`cf_aes`. + * + * .. c:member:: cf_cmac.prpctx + * Private data for prp functions. For a `prp` of `cf_aes`, this would be a + * pointer to a :c:type:`cf_aes_context` instance. + * + * .. c:member:: cf_cmac.B + * The XOR offset for the last message block if it is a complete block + * (also known as K\ :sub:`1`). + * + * .. c:member:: cf_cmac.P + * The XOR offset for the last message block if it is a partial block + * (also known as K\ :sub:`2`). + */ +typedef struct +{ + const cf_prp *prp; + void *prpctx; + uint8_t B[CF_MAXBLOCK]; + uint8_t P[CF_MAXBLOCK]; +} cf_cmac; + +/* .. c:function:: $DECL + * Initialise CMAC signing context using selected prp. */ +void cf_cmac_init(cf_cmac *ctx, const cf_prp *prp, void *prpctx); + +/* .. c:function:: $DECL + * CMAC sign the given data. The MAC is written to ctx->prp->blocksz + * bytes at out. This is a one-shot function. */ +void cf_cmac_sign(cf_cmac *ctx, const uint8_t *data, size_t bytes, + uint8_t out[CF_MAXBLOCK]); + +/* .. c:type:: cf_cmac_stream + * Stream interface to CMAC signing. + * + * Input data in arbitrary chunks using :c:func:`cf_cmac_stream_update`. + * The last bit of data must be signalled with the `isfinal` flag to + * that function, and the data cannot be zero length unless the whole + * message is empty. + * + * .. c:member:: cf_cmac_stream.cmac + * CMAC one-shot data. + * + * .. c:member:: cf_cmac_stream.cbc + * CBC block encryption data. + * + * .. c:member:: cf_cmac_stream.buffer + * Buffer for data which can't be processed until we have a full block. + * + * .. c:member:: cf_cmac_stream.used + * How many bytes at the front of :c:member:`buffer` are valid. + * + * .. c:member:: cf_cmac_stream.processed + * How many bytes in total we've processed. This is used to correctly + * process empty messages. + * + * .. c:member:: cf_cmac_stream.finalised + * A flag set when the final chunk of the message has been processed. + * Only when this flag is set can you get the MAC out. + */ +typedef struct +{ + cf_cmac cmac; + cf_cbc cbc; + uint8_t buffer[CF_MAXBLOCK]; + size_t used; + size_t processed; + int finalised; +} cf_cmac_stream; + +/* .. c:function:: $DECL + * Initialise CMAC streaming signing context using selected prp. */ +void cf_cmac_stream_init(cf_cmac_stream *ctx, const cf_prp *prp, void *prpctx); + +/* .. c:function:: $DECL + * Reset the streaming signing context, to sign a new message. */ +void cf_cmac_stream_reset(cf_cmac_stream *ctx); + +/* .. c:function:: $DECL + * Process ndata bytes at data. isfinal is non-zero if this is the last piece + * of data. */ +void cf_cmac_stream_update(cf_cmac_stream *ctx, const uint8_t *data, size_t ndata, + int isfinal); + +/* .. c:function:: $DECL + * Output the MAC to ctx->cmac->prp->blocksz bytes at out. + * cf_cmac_stream_update with isfinal non-zero must have been called + * since the last _init/_reset. */ +void cf_cmac_stream_final(cf_cmac_stream *ctx, uint8_t out[CF_MAXBLOCK]); + +/** + * EAX + * --- + * + * The EAX authenticated encryption mode. This is a one-shot + * interface. + * + * EAX is a pretty respectable and fast AEAD mode. + */ + +/* .. c:function:: $DECL + * EAX authenticated encryption. + * + * This function does not fail. + * + * :param prp/prpctx: describe the block cipher to use. + * :param plain: message plaintext. + * :param nplain: length of message. May be zero. + * :param header: additionally authenticated data (AAD). + * :param nheader: length of AAD. May be zero. + * :param nonce: nonce. This must not repeat for a given key. + * :param nnonce: length of nonce. The nonce can be any length. + * :param cipher: ciphertext output. `nplain` bytes are written here. + * :param tag: authentication tag. `ntag` bytes are written here. + * :param ntag: authentication tag length. This must be non-zero and no greater than `prp->blocksz`. + */ +void cf_eax_encrypt(const cf_prp *prp, void *prpctx, + const uint8_t *plain, size_t nplain, + const uint8_t *header, size_t nheader, + const uint8_t *nonce, size_t nnonce, + uint8_t *cipher, + uint8_t *tag, size_t ntag); + +/* .. c:function:: $DECL + * EAX authenticated decryption. + * + * :return: 0 on success, non-zero on error. Nothing is written to plain on error. + * + * :param prp/prpctx: describe the block cipher to use. + * :param cipher: message ciphertext. + * :param ncipher: message length. + * :param header: additionally authenticated data (AAD). + * :param nheader: length of AAD. + * :param nonce: nonce. + * :param nnonce: length of nonce. + * :param tag: authentication tag. `ntag` bytes are read from here. + * :param ntag: authentication tag length. + * :param plain: plaintext output. `ncipher` bytes are written here. + */ +int cf_eax_decrypt(const cf_prp *prp, void *prpctx, + const uint8_t *cipher, size_t ncipher, + const uint8_t *header, size_t nheader, + const uint8_t *nonce, size_t nnonce, + const uint8_t *tag, size_t ntag, + uint8_t *plain); + +/** + * GCM + * --- + * The GCM ('Galois counter mode') authenticated encryption mode. + * This is a one-shot interface. + * + * GCM is a reasonably respectable AEAD mode. It's somewhat more + * complex than EAX, and side channel-free implementations can + * be quite slow. + */ + +/* .. c:function:: $DECL + * GCM authenticated encryption. + * + * This function does not fail. + * + * :param prp/prpctx: describe the block cipher to use. + * :param plain: message plaintext. + * :param nplain: length of message. May be zero. + * :param header: additionally authenticated data (AAD). + * :param nheader: length of AAD. May be zero. + * :param nonce: nonce. This must not repeat for a given key. + * :param nnonce: length of nonce. The nonce can be any length, but 12 bytes is strongly recommended. + * :param cipher: ciphertext output. `nplain` bytes are written here. + * :param tag: authentication tag. `ntag` bytes are written here. + * :param ntag: authentication tag length. This must be non-zero and no greater than `prp->blocksz`. + * + * This function does not fail. + */ +void cf_gcm_encrypt(const cf_prp *prp, void *prpctx, + const uint8_t *plain, size_t nplain, + const uint8_t *header, size_t nheader, + const uint8_t *nonce, size_t nnonce, + uint8_t *cipher, + uint8_t *tag, size_t ntag); + +/* Incremental GHASH computation. */ +typedef struct +{ + cf_gf128 H; + cf_gf128 Y; + uint8_t buffer[16]; + size_t buffer_used; + uint64_t len_aad; + uint64_t len_cipher; + unsigned state; +} ghash_ctx; + +typedef struct +{ + cf_ctr ctr; + ghash_ctx gh; + uint8_t Y0[16]; + uint8_t e_Y0[16]; +} cf_gcm_ctx; + +void cf_gcm_encrypt_init(const cf_prp *prp, void *prpctx, cf_gcm_ctx *gcmctx, + const uint8_t *header, size_t nheader, + const uint8_t *nonce, size_t nnonce); +void cf_gcm_encrypt_update(cf_gcm_ctx *gcmctx, const uint8_t *plain, size_t nplain, uint8_t *cipher); +void cf_gcm_encrypt_final(cf_gcm_ctx *gcmctx, uint8_t *tag, size_t ntag); + +/* .. c:function:: $DECL + * GCM authenticated decryption. + * + * :return: 0 on success, non-zero on error. Nothing is written to plain on error. + * + * :param prp: describe the block cipher to use. + * :param prpctx: describe the block cipher to use. + * :param cipher: message ciphertext. + * :param ncipher: message length. + * :param header: additionally authenticated data (AAD). + * :param nheader: length of AAD. + * :param nonce: nonce. + * :param nnonce: length of nonce. + * :param tag: authentication tag. `ntag` bytes are read from here. + * :param ntag: authentication tag length. + * :param plain: plaintext output. `ncipher` bytes are written here. + */ +int cf_gcm_decrypt(const cf_prp *prp, void *prpctx, + const uint8_t *cipher, size_t ncipher, + const uint8_t *header, size_t nheader, + const uint8_t *nonce, size_t nnonce, + const uint8_t *tag, size_t ntag, + uint8_t *plain); + +/** + * CCM + * --- + * + * The CCM ('Counter with CBC-MAC') authenticated encryption mode. + * CCM is a widely used AEAD mode (in IPSec, WPA2, Bluetooth, etc.) + * + * It works (at a high level) by just gluing together CTR and CBC-MAC + * modes (in MAC-then-encrypt mode) and then fixing the problems inherent + * with CBC-MAC in over-complicated ways. + * + * This is a one-shot interface, which is good because the underlying + * mechanism isn't actually online: you need to know the message length + * before you start, or do everything in two passes. + */ + +/* .. c:function:: $DECL + * CCM authenticated encryption. + * + * This function does not fail. + * + * :param prp/prpctx: describe the block cipher to use. + * :param plain: message plaintext. + * :param nplain: length of message. May be zero. Must meet the constraints placed on it by `L`. + * :param L: length of the message length encoding. This must be in the interval `[2,8]` and gives a maximum message size of 2\ :sup:`8L` bytes. + * :param header: additionally authenticated data (AAD). + * :param nheader: length of AAD. May be zero. + * :param nonce: nonce. This must not repeat for a given key. + * :param nnonce: length of nonce. Must be exactly `15 - L` bytes for a 128-bit block cipher. + * :param cipher: ciphertext output. `nplain` bytes are written here. + * :param tag: authentication tag. `ntag` bytes are written here. + * :param ntag: authentication tag length. This must be 4, 6, 8, 10, 12, 14 or 16. + */ +void cf_ccm_encrypt(const cf_prp *prp, void *prpctx, + const uint8_t *plain, size_t nplain, size_t L, + const uint8_t *header, size_t nheader, + const uint8_t *nonce, size_t nnonce, + uint8_t *cipher, + uint8_t *tag, size_t ntag); + +/* .. c:function:: $DECL + * CCM authenticated decryption. + * + * :return: 0 on success, non-zero on error. Plain is cleared on error. + * + * :param prp: describe the block cipher to use. + * :param prpctx: describe the block cipher to use. + * :param cipher: message ciphertext. + * :param ncipher: length of message. + * :param L: length of the message length encoding. See :c:func:`cf_ccm_encrypt`. + * :param header: additionally authenticated data (AAD). + * :param nheader: length of AAD. + * :param nonce: nonce. + * :param nnonce: length of nonce. + * :param tag: authentication tag. `ntag` bytes are read from here. + * :param ntag: authentication tag length. This must be 4, 6, 8, 10, 12, 14 or 16. + * :param plain: plaintext output. `ncipher` bytes are written here. + */ +int cf_ccm_decrypt(const cf_prp *prp, void *prpctx, + const uint8_t *cipher, size_t ncipher, size_t L, + const uint8_t *header, size_t nheader, + const uint8_t *nonce, size_t nnonce, + const uint8_t *tag, size_t ntag, + uint8_t *plain); + +/** + * OCB + * --- + * + * OCB is an authenticated encryption mode by Phil Rogaway. + * + * This is version 3, as standardised in RFC7253. It's defined + * only for block ciphers with a 128-bit block size. + * + * This is a one-shot interface. + */ + +/* .. c:function:: $DECL + * OCB authenticated encryption. + * + * This function does not fail. + * + * :param prp/prpctx: describe the block cipher to use. + * :param plain: message plaintext. + * :param nplain: length of message. May be zero. + * :param header: additionally authenticated data (AAD). + * :param nheader: length of AAD. May be zero. + * :param nonce: nonce. This must not repeat for a given key. + * :param nnonce: length of nonce. Must be 15 or fewer bytes. + * :param cipher: ciphertext output. `nplain` bytes are written here. + * :param tag: authentication tag. `ntag` bytes are written here. + * :param ntag: authentication tag length. Must be 16 or fewer bytes. + */ +void cf_ocb_encrypt(const cf_prp *prp, void *prpctx, + const uint8_t *plain, size_t nplain, + const uint8_t *header, size_t nheader, + const uint8_t *nonce, size_t nnonce, + uint8_t *cipher, + uint8_t *tag, size_t ntag); + +/* .. c:function:: $DECL + * OCB authenticated decryption. + * + * :return: 0 on success, non-zero on error. `plain` is cleared on error. + * + * :param prp: describe the block cipher to use. + * :param prpctx: describe the block cipher to use. + * :param cipher: message ciphertext. + * :param ncipher: length of message. + * :param header: additionally authenticated data (AAD). + * :param nheader: length of AAD. + * :param nonce: nonce. + * :param nnonce: length of nonce. + * :param tag: authentication tag. `ntag` bytes are read from here. + * :param ntag: authentication tag length. + * :param plain: plaintext output. `ncipher` bytes are written here. + */ +int cf_ocb_decrypt(const cf_prp *prp, void *prpctx, + const uint8_t *cipher, size_t ncipher, + const uint8_t *header, size_t nheader, + const uint8_t *nonce, size_t nnonce, + const uint8_t *tag, size_t ntag, + uint8_t *plain); +#endif diff --git a/deps/picoquic/picotls/deps/cifra/src/norx.c b/deps/picoquic/picotls/deps/cifra/src/norx.c new file mode 100644 index 0000000..8f0aba6 --- /dev/null +++ b/deps/picoquic/picotls/deps/cifra/src/norx.c @@ -0,0 +1,410 @@ +/* + * cifra - embedded cryptography library + * Written in 2014 by Joseph Birr-Pixton + * + * To the extent possible under law, the author(s) have dedicated all + * copyright and related and neighboring rights to this software to the + * public domain worldwide. This software is distributed without any + * warranty. + * + * You should have received a copy of the CC0 Public Domain Dedication + * along with this software. If not, see + * . + */ + + +#include "norx.h" +#include "bitops.h" +#include "handy.h" +#include "blockwise.h" +#include "tassert.h" + +#include + +typedef struct +{ + uint32_t s[16]; +} norx32_ctx; + +/* Domain separation constants */ +#define DOMAIN_HEADER 0x01 +#define DOMAIN_PAYLOAD 0x02 +#define DOMAIN_TRAILER 0x04 +#define DOMAIN_TAG 0x08 + +#define WORD_BYTES 4 +#define WORD_BITS 32 +#define ROUNDS 4 +#define DEGREE 1 +#define TAG_BITS 128 +#define RATE_BYTES 48 +#define RATE_WORDS 12 + +static void permute(norx32_ctx *ctx) +{ +#ifdef CORTEX_M0 + /* Register usage: A-D r2, r3, r4, r5. + * Temps: r6 */ + +#define in(xx) "%[" #xx "]" + + /* Load numbered slots of S into r2-r5 */ +#define LOAD(u, v, w, x) \ + " ldr r2, [%[S], " in(u) "]\n" \ + " ldr r3, [%[S], " in(v) "]\n" \ + " ldr r4, [%[S], " in(w) "]\n" \ + " ldr r5, [%[S], " in(x) "]\n" + + /* Store r2-r5 into numbered slots of S */ +#define STORE(u, v, w, x) \ + " str r2, [%[S], " in(u) "]\n" \ + " str r3, [%[S], " in(v) "]\n" \ + " str r4, [%[S], " in(w) "]\n" \ + " str r5, [%[S], " in(x) "]\n" + + /* This is H() plus the xor and rotate in one step of G. + * rx is the register containing x (read/write) + * ry is the register containing y (read) + * rw is the register containing d (read/write) + * rot is the rotation constant r_n */ +#define P(rx, ry, rw, rot) \ + " mov r6, " #rx "\n" \ + " and " #rx ", " #ry "\n" \ + " lsl " #rx ", #1\n" \ + " eor " #rx ", r6\n" \ + " eor " #rx ", " #ry "\n" \ + " mov r6, #" #rot "\n" \ + " eor " #rw ", " #rx "\n" \ + " ror " #rw ", r6\n" + + /* The function G. s is the state array, a-d are indices + * into it. */ +#define G(s, a, b, c, d) \ + __asm__ ( \ + LOAD(A, B, C, D) \ + P(r2, r3, r5, 8) \ + P(r4, r5, r3, 11) \ + P(r2, r3, r5, 16) \ + P(r4, r5, r3, 31) \ + STORE(A, B, C, D) \ + : \ + : [S] "r" (s), \ + [A] "i" (a << 2), \ + [B] "i" (b << 2), \ + [C] "i" (c << 2), \ + [D] "i" (d << 2) \ + : "memory", "cc", "r2", "r3", "r4", "r5", "r6"); +#else + + /* This is one quarter of G; the function H plus xor/rotate. */ +#define P(u, v, w, rr) \ + (u) = ((u) ^ (v)) ^ (((u) & (v)) << 1); \ + (w) = rotr32((u) ^ (w), rr); + +#define G(s, a, b, c, d) \ + P(s[a], s[b], s[d], 8) \ + P(s[c], s[d], s[b], 11) \ + P(s[a], s[b], s[d], 16) \ + P(s[c], s[d], s[b], 31) +#endif + + for (int i = 0; i < ROUNDS; i++) + { + /* columns */ + G(ctx->s, 0, 4, 8, 12); + G(ctx->s, 1, 5, 9, 13); + G(ctx->s, 2, 6, 10, 14); + G(ctx->s, 3, 7, 11, 15); + + /* diagonals */ + G(ctx->s, 0, 5, 10, 15); + G(ctx->s, 1, 6, 11, 12); + G(ctx->s, 2, 7, 8, 13); + G(ctx->s, 3, 4, 9, 14); + } + +#undef G +#undef P +} + +static void init(norx32_ctx *ctx, + const uint8_t key[16], + const uint8_t nonce[8]) +{ + /* 1. Basic setup */ + ctx->s[0] = read32_le(nonce + 0); + ctx->s[1] = read32_le(nonce + 4); + ctx->s[2] = 0xb707322f; + ctx->s[3] = 0xa0c7c90d; + + ctx->s[4] = read32_le(key + 0); + ctx->s[5] = read32_le(key + 4); + ctx->s[6] = read32_le(key + 8); + ctx->s[7] = read32_le(key + 12); + + ctx->s[8] = 0xa3d8d930; + ctx->s[9] = 0x3fa8b72c; + ctx->s[10] = 0xed84eb49; + ctx->s[11] = 0xedca4787; + + ctx->s[12] = 0x335463eb; + ctx->s[13] = 0xf994220b; + ctx->s[14] = 0xbe0bf5c9; + ctx->s[15] = 0xd7c49104; + + /* 2. Parameter integration + * w = 32 + * l = 4 + * p = 1 + * t = 128 + */ + ctx->s[12] ^= WORD_BITS; + ctx->s[13] ^= ROUNDS; + ctx->s[14] ^= DEGREE; + ctx->s[15] ^= TAG_BITS; + + permute(ctx); +} + +/* Input domain separation constant for next step, and final permutation of + * preceeding step. */ +static void switch_domain(norx32_ctx *ctx, uint32_t constant) +{ + ctx->s[15] ^= constant; + permute(ctx); +} + +typedef struct +{ + norx32_ctx *ctx; + uint32_t type; +} blockctx; + +static void input_block_final(void *vctx, const uint8_t *data) +{ + blockctx *bctx = vctx; + norx32_ctx *ctx = bctx->ctx; + + /* just xor-in data. */ + for (int i = 0; i < RATE_WORDS; i++) + { + ctx->s[i] ^= read32_le(data); + data += WORD_BYTES; + } +} + +static void input_block(void *vctx, const uint8_t *data) +{ + /* Process block, then prepare for the next one. */ + blockctx *bctx = vctx; + input_block_final(vctx, data); + switch_domain(bctx->ctx, bctx->type); +} + +static void input(norx32_ctx *ctx, uint32_t type, + const uint8_t *buf, size_t nbuf) +{ + uint8_t partial[RATE_BYTES]; + size_t npartial = 0; + blockctx bctx = { ctx, type }; + + /* Process input. */ + cf_blockwise_accumulate(partial, &npartial, sizeof partial, + buf, nbuf, + input_block, + &bctx); + + /* Now pad partial. This contains the trailing portion of buf. */ + memset(partial + npartial, 0, sizeof(partial) - npartial); + partial[npartial] = 0x01; + partial[sizeof(partial) - 1] ^= 0x80; + + input_block_final(&bctx, partial); +} + +static void do_header(norx32_ctx *ctx, const uint8_t *buf, size_t nbuf) +{ + if (nbuf) + { + switch_domain(ctx, DOMAIN_HEADER); + input(ctx, DOMAIN_HEADER, buf, nbuf); + } +} + +static void do_trailer(norx32_ctx *ctx, const uint8_t *buf, size_t nbuf) +{ + if (nbuf) + { + switch_domain(ctx, DOMAIN_TRAILER); + input(ctx, DOMAIN_TRAILER, buf, nbuf); + } +} + +static void body_block_encrypt(norx32_ctx *ctx, + const uint8_t plain[RATE_BYTES], + uint8_t cipher[RATE_BYTES]) +{ + for (int i = 0; i < RATE_WORDS; i++) + { + ctx->s[i] ^= read32_le(plain); + write32_le(ctx->s[i], cipher); + plain += WORD_BYTES; + cipher += WORD_BYTES; + } +} + +static void encrypt_body(norx32_ctx *ctx, + const uint8_t *plain, uint8_t *cipher, size_t nbytes) +{ + if (nbytes == 0) + return; + + /* Process full blocks: easy */ + while (nbytes >= RATE_BYTES) + { + switch_domain(ctx, DOMAIN_PAYLOAD); + body_block_encrypt(ctx, plain, cipher); + plain += RATE_BYTES; + cipher += RATE_BYTES; + nbytes -= RATE_BYTES; + } + + /* Final padded block. */ + uint8_t partial[RATE_BYTES]; + memset(partial, 0, sizeof partial); + memcpy(partial, plain, nbytes); + partial[nbytes] ^= 0x01; + partial[sizeof(partial) - 1] ^= 0x80; + + switch_domain(ctx, DOMAIN_PAYLOAD); + body_block_encrypt(ctx, partial, partial); + + memcpy(cipher, partial, nbytes); +} + +static void body_block_decrypt(norx32_ctx *ctx, + const uint8_t cipher[RATE_BYTES], + uint8_t plain[RATE_BYTES], + size_t start, size_t end) +{ + for (size_t i = start; i < end; i++) + { + uint32_t ct = read32_le(cipher); + write32_le(ctx->s[i] ^ ct, plain); + ctx->s[i] = ct; + plain += WORD_BYTES; + cipher += WORD_BYTES; + } +} + +static void undo_padding(norx32_ctx *ctx, size_t bytes) +{ + assert(bytes < RATE_BYTES); + ctx->s[bytes / WORD_BYTES] ^= 0x01 << ((bytes % WORD_BYTES) * 8); + ctx->s[RATE_WORDS - 1] ^= 0x80000000; +} + +static void decrypt_body(norx32_ctx *ctx, + const uint8_t *cipher, uint8_t *plain, size_t nbytes) +{ + if (nbytes == 0) + return; + + /* Process full blocks. */ + while (nbytes >= RATE_BYTES) + { + switch_domain(ctx, DOMAIN_PAYLOAD); + body_block_decrypt(ctx, cipher, plain, 0, RATE_WORDS); + plain += RATE_BYTES; + cipher += RATE_BYTES; + nbytes -= RATE_BYTES; + } + + /* Then partial blocks. */ + size_t offset = 0; + switch_domain(ctx, DOMAIN_PAYLOAD); + + undo_padding(ctx, nbytes); + + /* In units of whole words. */ + while (nbytes >= WORD_BYTES) + { + body_block_decrypt(ctx, cipher, plain, offset, offset + 1); + plain += WORD_BYTES; + cipher += WORD_BYTES; + nbytes -= WORD_BYTES; + offset += 1; + } + + /* And then, finally, bytewise. */ + uint8_t tmp[WORD_BYTES]; + write32_le(ctx->s[offset], tmp); + + for (size_t i = 0; i < nbytes; i++) + { + uint8_t c = cipher[i]; + plain[i] = tmp[i] ^ c; + tmp[i] = c; + } + + ctx->s[offset] = read32_le(tmp); +} + +static void get_tag(norx32_ctx *ctx, uint8_t tag[16]) +{ + switch_domain(ctx, DOMAIN_TAG); + permute(ctx); + write32_le(ctx->s[0], tag + 0); + write32_le(ctx->s[1], tag + 4); + write32_le(ctx->s[2], tag + 8); + write32_le(ctx->s[3], tag + 12); +} + +void cf_norx32_encrypt(const uint8_t key[16], + const uint8_t nonce[8], + const uint8_t *header, size_t nheader, + const uint8_t *plaintext, size_t nbytes, + const uint8_t *trailer, size_t ntrailer, + uint8_t *ciphertext, + uint8_t tag[16]) +{ + norx32_ctx ctx; + + init(&ctx, key, nonce); + do_header(&ctx, header, nheader); + encrypt_body(&ctx, plaintext, ciphertext, nbytes); + do_trailer(&ctx, trailer, ntrailer); + get_tag(&ctx, tag); + + mem_clean(&ctx, sizeof ctx); +} + +int cf_norx32_decrypt(const uint8_t key[16], + const uint8_t nonce[8], + const uint8_t *header, size_t nheader, + const uint8_t *ciphertext, size_t nbytes, + const uint8_t *trailer, size_t ntrailer, + const uint8_t tag[16], + uint8_t *plaintext) +{ + norx32_ctx ctx; + uint8_t ourtag[16]; + + init(&ctx, key, nonce); + do_header(&ctx, header, nheader); + decrypt_body(&ctx, ciphertext, plaintext, nbytes); + do_trailer(&ctx, trailer, ntrailer); + get_tag(&ctx, ourtag); + + int err = 0; + + if (!mem_eq(ourtag, tag, sizeof ourtag)) + { + err = 1; + mem_clean(plaintext, nbytes); + mem_clean(ourtag, sizeof ourtag); + } + + mem_clean(&ctx, sizeof ctx); + return err; +} diff --git a/deps/picoquic/picotls/deps/cifra/src/norx.h b/deps/picoquic/picotls/deps/cifra/src/norx.h new file mode 100644 index 0000000..aee7bfe --- /dev/null +++ b/deps/picoquic/picotls/deps/cifra/src/norx.h @@ -0,0 +1,84 @@ +/* + * cifra - embedded cryptography library + * Written in 2014 by Joseph Birr-Pixton + * + * To the extent possible under law, the author(s) have dedicated all + * copyright and related and neighboring rights to this software to the + * public domain worldwide. This software is distributed without any + * warranty. + * + * You should have received a copy of the CC0 Public Domain Dedication + * along with this software. If not, see + * . + */ + +#ifndef NORX_H +#define NORX_H + +#include +#include + +/** + * The NORX AEAD cipher + * ==================== + * This is an implementation of NORX32-4-1 with a one-shot + * interface. NORX is a CAESAR candidate with a core similar + * to ChaCha20 and a sponge structure like Keccak. + * + * This is NORX v2.0. It is not compatible with earlier + * versions. + * + * NORX32 uses a 128-bit key. Each encryption requires a + * 64-bit nonce. An encryption processes one sequence of + * additional data ('header'), followed by encryption of + * the plaintext, followed by processing a second sequence + * of additional data ('trailer'). It outputs a 128-bit + * tag. + */ + +/* .. c:function:: $DECL + * NORX32-4-1 one-shot encryption interface. + * + * :param key: key material. + * :param nonce: per-message nonce. + * :param header: header buffer. + * :param nheader: number of header bytes. + * :param plaintext: plaintext bytes to be encrypted. + * :param nbytes: number of plaintext/ciphertext bytes. + * :param trailer: trailer buffer. + * :param ntrailer: number of trailer bytes. + * :param ciphertext: ciphertext output buffer, nbytes in length. + * :param tag: authentication tag output buffer. + */ +void cf_norx32_encrypt(const uint8_t key[16], + const uint8_t nonce[8], + const uint8_t *header, size_t nheader, + const uint8_t *plaintext, size_t nbytes, + const uint8_t *trailer, size_t ntrailer, + uint8_t *ciphertext, + uint8_t tag[16]); +/* .. c:function:: $DECL + * NORX32-4-1 one-shot decryption interface. + * + * :return: 0 on success, non-zero on error. Plaintext is zeroed on error. + * + * :param key: key material. + * :param nonce: per-message nonce. + * :param header: header buffer. + * :param nheader: number of header bytes. + * :param ciphertext: ciphertext bytes to be decrypted. + * :param nbytes: number of plaintext/ciphertext bytes. + * :param trailer: trailer buffer. + * :param ntrailer: number of trailer bytes. + * :param plaintext: plaintext output buffer, nbytes in length. + * :param tag: authentication tag output buffer. + */ +int cf_norx32_decrypt(const uint8_t key[16], + const uint8_t nonce[8], + const uint8_t *header, size_t nheader, + const uint8_t *ciphertext, size_t nbytes, + const uint8_t *trailer, size_t ntrailer, + const uint8_t tag[16], + uint8_t *plaintext); + +#endif diff --git a/deps/picoquic/picotls/deps/cifra/src/ocb.c b/deps/picoquic/picotls/deps/cifra/src/ocb.c new file mode 100644 index 0000000..3d244d4 --- /dev/null +++ b/deps/picoquic/picotls/deps/cifra/src/ocb.c @@ -0,0 +1,404 @@ +/* + * cifra - embedded cryptography library + * Written in 2016 by Joseph Birr-Pixton + * + * To the extent possible under law, the author(s) have dedicated all + * copyright and related and neighboring rights to this software to the + * public domain worldwide. This software is distributed without any + * warranty. + * + * You should have received a copy of the CC0 Public Domain Dedication + * along with this software. If not, see + * . + */ + +#include "handy.h" +#include "prp.h" +#include "modes.h" +#include "blockwise.h" +#include "bitops.h" +#include "gf128.h" +#include "tassert.h" + +#include + +/* How many L_n values to compute at schedule time. */ +#define MAX_L 4 + +/* We and RFC7253 assume 128-bit blocks. */ +#define BLOCK 16 + +typedef struct +{ + const cf_prp *prp; + void *prpctx; /* Our PRP */ + uint8_t *out; /* Output pointer for block processing */ + cf_gf128 L_star; /* Zero block ciphertext */ + cf_gf128 L_dollar; /* L_$ is double of L_* */ + cf_gf128 L[MAX_L]; /* L[0] is double of L_$, L[1] is double of L[0], etc. */ + cf_gf128 offset; /* Offset_i */ + cf_gf128 checksum; /* Checksum_i */ + uint32_t i; /* Block index, 1-based */ +} ocb; + +typedef struct +{ + ocb *o; /* OCB context (contains PRP, etc.) */ + cf_gf128 sum; /* Current Sum_i */ + cf_gf128 offset; /* Current Offset_i */ + uint32_t i; /* Block index, 1-based */ +} ocb_hash; + +static void ocb_init(ocb *o, const cf_prp *prp, void *prpctx, + const uint8_t *nonce, size_t nnonce, + size_t ntag) +{ + o->prp = prp; + o->prpctx = prpctx; + + assert(o->prp->blocksz == BLOCK); + + /* L_* = ENCIPHER(K, zeros(128)) */ + uint8_t L_star_bytes[BLOCK] = { 0 }; + prp->encrypt(prpctx, L_star_bytes, L_star_bytes); + cf_gf128_frombytes_be(L_star_bytes, o->L_star); + + /* L_$ = double(L_*) */ + cf_gf128_double(o->L_star, o->L_dollar); + + /* L_0 = double(L_$) etc. */ + cf_gf128_double(o->L_dollar, o->L[0]); + + for (int i = 1; i < MAX_L; i++) + cf_gf128_double(o->L[i - 1], o->L[i]); + + /* Compute nonce-dependent and per-encryption vars */ + assert(nnonce > 0 && nnonce < BLOCK); + uint8_t full_nonce[BLOCK] = { 0 }; + full_nonce[0] = ((ntag * 8) & 0x7f) << 1; + full_nonce[BLOCK - 1 - nnonce] |= 0x01; + memcpy(full_nonce + BLOCK - nnonce, nonce, nnonce); + uint8_t bottom = full_nonce[BLOCK - 1] & 0x3f; + + /* Make Ktop */ + full_nonce[BLOCK - 1] &= 0xc0; + uint8_t Ktop[BLOCK + 8]; + prp->encrypt(prpctx, full_nonce, Ktop); + + /* Stretch Ktop */ + for (int i = 0; i < 8; i++) + Ktop[i + BLOCK] = Ktop[i] ^ Ktop[i + 1]; + + /* Outputs */ + uint8_t offset[BLOCK]; + copy_bytes_unaligned(offset, Ktop, BLOCK, bottom); + cf_gf128_frombytes_be(offset, o->offset); + memset(o->checksum, 0, sizeof o->checksum); +} + +static void ocb_start_cipher(ocb *o, uint8_t *output) +{ + o->i = 1; + o->out = output; +} + +static void ocb_add_Ln(ocb *o, uint32_t n, cf_gf128 out) +{ + /* Do we have a precomputed L term? */ + if (n < MAX_L) + { + cf_gf128_add(o->L[n], out, out); + return; + } + + /* Compute more terms of L. */ + cf_gf128 accum; + memcpy(accum, o->L[MAX_L - 1], sizeof accum); + + for (uint32_t i = MAX_L - 1; i < n; i++) + { + cf_gf128 next; + cf_gf128_double(accum, next); + memcpy(accum, next, sizeof accum); + } + + cf_gf128_add(accum, out, out); +} + +static void ocb_hash_init(ocb_hash *h) +{ + memset(h->offset, 0, sizeof h->offset); + memset(h->sum, 0, sizeof h->sum); + h->i = 1; +} + +static void ocb_hash_sum(ocb *o, const uint8_t *block, + cf_gf128 sum, const cf_gf128 offset) +{ + uint8_t offset_bytes[BLOCK]; + cf_gf128_tobytes_be(offset, offset_bytes); + + uint8_t block_tmp[BLOCK]; + xor_bb(block_tmp, block, offset_bytes, sizeof block_tmp); + o->prp->encrypt(o->prpctx, block_tmp, block_tmp); + + cf_gf128 tmp; + cf_gf128_frombytes_be(block_tmp, tmp); + cf_gf128_add(sum, tmp, sum); +} + +static void ocb_hash_block(void *vctx, const uint8_t *block) +{ + ocb_hash *h = vctx; + + /* Offset_i = Offset_{i - 1} xor L{ntz(i)} */ + ocb_add_Ln(h->o, count_trailing_zeroes(h->i), h->offset); + + /* Sum_i = Sum_{i - 1} xor ENCIPHER(K, A_i xor Offset_i) */ + ocb_hash_sum(h->o, block, h->sum, h->offset); + + h->i++; +} + +static void ocb_process_header(ocb *o, const uint8_t *header, size_t nheader, + uint8_t out[BLOCK]) +{ + ocb_hash ctx = { o }; + ocb_hash_init(&ctx); + + uint8_t partial[BLOCK]; + size_t npartial = 0; + + cf_blockwise_accumulate(partial, &npartial, + o->prp->blocksz, + header, nheader, + ocb_hash_block, + &ctx); + + if (npartial) + { + /* Offset_* = Offset_m xor L_* */ + cf_gf128_add(ctx.offset, o->L_star, ctx.offset); + + /* CipherInput = (A_* || 1 || zeros(127 - bitlen(A_*))) xor Offset_* */ + memset(partial + npartial, 0, sizeof(partial) - npartial); + partial[npartial] = 0x80; + + /* Sum = Sum_m xor ENCIPHER(K, CipherInput) */ + ocb_hash_sum(ctx.o, partial, ctx.sum, ctx.offset); + } + + cf_gf128_tobytes_be(ctx.sum, out); + mem_clean(&ctx, sizeof ctx); +} + +static void ocb_encrypt_block(void *vctx, const uint8_t *block) +{ + ocb *o = vctx; + + /* Offset_i = Offset_{i - 1} xor L{ntz(i)} */ + ocb_add_Ln(o, count_trailing_zeroes(o->i), o->offset); + + /* C_i = Offset_i xor ENCIPHER(K, P_i xor Offset_i) */ + uint8_t offset_bytes[BLOCK]; + cf_gf128_tobytes_be(o->offset, offset_bytes); + + uint8_t block_tmp[BLOCK]; + xor_bb(block_tmp, block, offset_bytes, sizeof block_tmp); + o->prp->encrypt(o->prpctx, block_tmp, block_tmp); + xor_bb(o->out, block_tmp, offset_bytes, sizeof block_tmp); + o->out += sizeof block_tmp; + + /* Checksum_i = Checksum_{i - 1} xor P_i */ + cf_gf128 P; + cf_gf128_frombytes_be(block, P); + cf_gf128_add(o->checksum, P, o->checksum); + + o->i++; +} + +void cf_ocb_encrypt(const cf_prp *prp, void *prpctx, + const uint8_t *plain, size_t nplain, + const uint8_t *header, size_t nheader, + const uint8_t *nonce, size_t nnonce, + uint8_t *cipher, /* the same size as nplain */ + uint8_t *tag, size_t ntag) +{ + ocb o; + ocb_init(&o, prp, prpctx, nonce, nnonce, ntag); + + /* Process blocks. The blockwise machinery takes care of + * splitting the input into 128-bit blocks, and calling + * a function on each one. */ + uint8_t partial[BLOCK]; + size_t npartial = 0; + + ocb_start_cipher(&o, cipher); + cf_blockwise_accumulate(partial, &npartial, + prp->blocksz, + plain, nplain, + ocb_encrypt_block, + &o); + + /* Move along plain and cipher. */ + plain += (o.out - cipher); + cipher = o.out; + + /* If we have remaining data to pad and process, + * it's in partial. */ + if (npartial) + { + /* Offset_* = Offset_m xor L_* */ + cf_gf128_add(o.offset, o.L_star, o.offset); + + /* Pad = ENCIPHER(K, Offset_*) */ + uint8_t pad[BLOCK]; + cf_gf128_tobytes_be(o.offset, pad); + o.prp->encrypt(o.prpctx, pad, pad); + + /* C_* = P_* xor Pad[1..bitlen(P_*)] */ + xor_bb(cipher, partial, pad, npartial); + mem_clean(pad, sizeof pad); + + /* Checksum_* = Checksum_m xor (P_* || 1 || zeros(127 - bitlen(P_*))) */ + memset(partial + npartial, 0, sizeof(partial) - npartial); + partial[npartial] = 0x80; + + cf_gf128 last_block; + cf_gf128_frombytes_be(partial, last_block); + cf_gf128_add(o.checksum, last_block, o.checksum); + mem_clean(last_block, sizeof last_block); + } + + /* Compute: Tag = ENCIPHER(K, Checksum_m xor Offset_m xor L_$) xor HASH(K, A) */ + cf_gf128 full_tag; + for (size_t i = 0; i < 4; i++) + full_tag[i] = o.checksum[i] ^ o.offset[i] ^ o.L_dollar[i]; + + /* Convert tag to bytes for encryption */ + uint8_t tag_bytes[BLOCK]; + cf_gf128_tobytes_be(full_tag, tag_bytes); + + /* ENCIPHER(...) */ + o.prp->encrypt(o.prpctx, tag_bytes, tag_bytes); + + /* Compute HASH(K, A). */ + uint8_t hash_a[BLOCK]; + ocb_process_header(&o, header, nheader, hash_a); + + /* ... xor HASH(K, A) */ + xor_bb(tag_bytes, tag_bytes, hash_a, sizeof tag_bytes); + + /* Copy out tag to caller. */ + memcpy(tag, tag_bytes, ntag); + + mem_clean(&o, sizeof o); +} + +static void ocb_decrypt_block(void *vctx, const uint8_t *block) +{ + ocb *o = vctx; + + /* Offset_i = Offset_{i - 1} xor L{ntz(i)} */ + ocb_add_Ln(o, count_trailing_zeroes(o->i), o->offset); + + /* P_i = Offset_i xor DECIPHER(K, C_i xor Offset_i) */ + uint8_t offset_bytes[BLOCK]; + cf_gf128_tobytes_be(o->offset, offset_bytes); + + uint8_t block_tmp[BLOCK]; + xor_bb(block_tmp, block, offset_bytes, sizeof block_tmp); + o->prp->decrypt(o->prpctx, block_tmp, block_tmp); + xor_bb(o->out, block_tmp, offset_bytes, sizeof block_tmp); + + /* Checksum_i = Checksum_{i - 1} xor P_i */ + cf_gf128 P; + cf_gf128_frombytes_be(o->out, P); + o->out += sizeof block_tmp; + cf_gf128_add(o->checksum, P, o->checksum); + + o->i++; +} + +int cf_ocb_decrypt(const cf_prp *prp, void *prpctx, + const uint8_t *cipher, size_t ncipher, + const uint8_t *header, size_t nheader, + const uint8_t *nonce, size_t nnonce, + const uint8_t *tag, size_t ntag, + uint8_t *plain) +{ + ocb o; + ocb_init(&o, prp, prpctx, nonce, nnonce, ntag); + + /* Do blockwise decryption */ + uint8_t partial[BLOCK]; + size_t npartial = 0; + + ocb_start_cipher(&o, plain); + cf_blockwise_accumulate(partial, &npartial, + prp->blocksz, + cipher, ncipher, + ocb_decrypt_block, + &o); + + if (npartial) + { + /* Offset_* = Offset_m xor L_* */ + cf_gf128_add(o.offset, o.L_star, o.offset); + + /* Pad = ENCIPHER(K, Offset_*) */ + uint8_t pad[BLOCK]; + cf_gf128_tobytes_be(o.offset, pad); + o.prp->encrypt(o.prpctx, pad, pad); + + /* P_* = C_* xor Pad[1..bitlen(C_*)] */ + xor_bb(partial, partial, pad, npartial); + mem_clean(pad, sizeof pad); + + memcpy(o.out, partial, npartial); + + /* Checksum_* = Checksum_m xor (P_* || 1 || zeros(127 - bitlen(P_*))) */ + memset(partial + npartial, 0, sizeof(partial) - npartial); + partial[npartial] = 0x80; + + cf_gf128 last_block; + cf_gf128_frombytes_be(partial, last_block); + cf_gf128_add(o.checksum, last_block, o.checksum); + mem_clean(last_block, sizeof last_block); + } + + /* Compute: Tag = ENCIPHER(K, Checksum_m xor Offset_m xor L_$) xor HASH(K, A) */ + cf_gf128 full_tag; + for (size_t i = 0; i < 4; i++) + full_tag[i] = o.checksum[i] ^ o.offset[i] ^ o.L_dollar[i]; + + /* Convert tag to bytes for encryption */ + uint8_t tag_bytes[BLOCK]; + cf_gf128_tobytes_be(full_tag, tag_bytes); + + /* ENCIPHER(...) */ + o.prp->encrypt(o.prpctx, tag_bytes, tag_bytes); + + /* Compute HASH(K, A). */ + uint8_t hash_a[BLOCK]; + ocb_process_header(&o, header, nheader, hash_a); + + /* ... xor HASH(K, A) */ + xor_bb(tag_bytes, tag_bytes, hash_a, sizeof tag_bytes); + + /* Check against caller's tag. */ + int err; + + if (mem_eq(tag, tag_bytes, ntag)) + { + err = 0; + } else { + err = 1; + mem_clean(plain, ncipher); + } + + mem_clean(&o, sizeof o); + mem_clean(tag_bytes, sizeof tag_bytes); + mem_clean(full_tag, sizeof full_tag); + return err; +} diff --git a/deps/picoquic/picotls/deps/cifra/src/pbkdf2.c b/deps/picoquic/picotls/deps/cifra/src/pbkdf2.c new file mode 100644 index 0000000..fce6a22 --- /dev/null +++ b/deps/picoquic/picotls/deps/cifra/src/pbkdf2.c @@ -0,0 +1,84 @@ +/* + * cifra - embedded cryptography library + * Written in 2014 by Joseph Birr-Pixton + * + * To the extent possible under law, the author(s) have dedicated all + * copyright and related and neighboring rights to this software to the + * public domain worldwide. This software is distributed without any + * warranty. + * + * You should have received a copy of the CC0 Public Domain Dedication + * along with this software. If not, see + * . + */ + +#include "pbkdf2.h" +#include "hmac.h" +#include "bitops.h" +#include "handy.h" +#include "tassert.h" + +#include + +static void F(const cf_hmac_ctx *startctx, + uint32_t counter, + const uint8_t *salt, size_t nsalt, + uint32_t iterations, + uint8_t *out) +{ + uint8_t U[CF_MAXHASH]; + size_t hashsz = startctx->hash->hashsz; + + uint8_t countbuf[4]; + write32_be(counter, countbuf); + + /* First iteration: + * U_1 = PRF(P, S || INT_32_BE(i)) + */ + cf_hmac_ctx ctx = *startctx; + cf_hmac_update(&ctx, salt, nsalt); + cf_hmac_update(&ctx, countbuf, sizeof countbuf); + cf_hmac_finish(&ctx, U); + memcpy(out, U, hashsz); + + /* Subsequent iterations: + * U_c = PRF(P, U_{c-1}) + */ + for (uint32_t i = 1; i < iterations; i++) + { + ctx = *startctx; + cf_hmac_update(&ctx, U, hashsz); + cf_hmac_finish(&ctx, U); + xor_bb(out, out, U, hashsz); + } +} + +void cf_pbkdf2_hmac(const uint8_t *pw, size_t npw, + const uint8_t *salt, size_t nsalt, + uint32_t iterations, + uint8_t *out, size_t nout, + const cf_chash *hash) +{ + uint32_t counter = 1; + uint8_t block[CF_MAXHASH]; + + assert(iterations); + assert(out && nout); + assert(hash); + + /* Starting point for inner loop. */ + cf_hmac_ctx ctx; + cf_hmac_init(&ctx, hash, pw, npw); + + while (nout) + { + F(&ctx, counter, salt, nsalt, iterations, block); + + size_t taken = MIN(nout, hash->hashsz); + memcpy(out, block, taken); + out += taken; + nout -= taken; + counter++; + } +} + diff --git a/deps/picoquic/picotls/deps/cifra/src/pbkdf2.h b/deps/picoquic/picotls/deps/cifra/src/pbkdf2.h new file mode 100644 index 0000000..2f30484 --- /dev/null +++ b/deps/picoquic/picotls/deps/cifra/src/pbkdf2.h @@ -0,0 +1,47 @@ +/* + * cifra - embedded cryptography library + * Written in 2014 by Joseph Birr-Pixton + * + * To the extent possible under law, the author(s) have dedicated all + * copyright and related and neighboring rights to this software to the + * public domain worldwide. This software is distributed without any + * warranty. + * + * You should have received a copy of the CC0 Public Domain Dedication + * along with this software. If not, see + * . + */ + +#ifndef PBKDF2_H +#define PBKDF2_H + +#include +#include + +#include "chash.h" + +/** + * PBKDF2-HMAC + * =========== + * This is PBKDF2 as described by PKCS#5/RFC2898 with HMAC as the PRF. + */ + +/* .. c:function:: $DECL + * This computes PBKDF2-HMAC with the given hash functon. + * + * :param pw: password input buffer. + * :param npw: password length. + * :param salt: salt input buffer. + * :param nsalt: salt length. + * :param iterations: non-zero iteration count. Tune this for performance/security tradeoff. + * :param out: key material output buffer. `nout` bytes are written here. + * :param nout: key material length. + * :param hash: hash function description. + */ +void cf_pbkdf2_hmac(const uint8_t *pw, size_t npw, + const uint8_t *salt, size_t nsalt, + uint32_t iterations, + uint8_t *out, size_t nout, + const cf_chash *hash); + +#endif diff --git a/deps/picoquic/picotls/deps/cifra/src/poly1305.c b/deps/picoquic/picotls/deps/cifra/src/poly1305.c new file mode 100644 index 0000000..6c3f13d --- /dev/null +++ b/deps/picoquic/picotls/deps/cifra/src/poly1305.c @@ -0,0 +1,221 @@ +/* + * cifra - embedded cryptography library + * Written in 2014 by Joseph Birr-Pixton + * + * To the extent possible under law, the author(s) have dedicated all + * copyright and related and neighboring rights to this software to the + * public domain worldwide. This software is distributed without any + * warranty. + * + * You should have received a copy of the CC0 Public Domain Dedication + * along with this software. If not, see + * . + */ + +#include "poly1305.h" +#include "bitops.h" +#include "handy.h" +#include "blockwise.h" + +#include +#include + +void cf_poly1305_init(cf_poly1305 *ctx, + const uint8_t r[16], + const uint8_t s[16]) +{ + memset(ctx, 0, sizeof *ctx); + + ctx->r[0] = r[0]; + ctx->r[1] = r[1]; + ctx->r[2] = r[2]; + ctx->r[3] = r[3] & 0x0f; + ctx->r[4] = r[4] & 0xfc; + ctx->r[5] = r[5]; + ctx->r[6] = r[6]; + ctx->r[7] = r[7] & 0x0f; + ctx->r[8] = r[8] & 0xfc; + ctx->r[9] = r[9]; + ctx->r[10] = r[10]; + ctx->r[11] = r[11] & 0x0f; + ctx->r[12] = r[12] & 0xfc; + ctx->r[13] = r[13]; + ctx->r[14] = r[14]; + ctx->r[15] = r[15] & 0x0f; + ctx->r[16] = 0; + + memcpy(ctx->s, s, 16); +} + +static void poly1305_add(uint32_t h[17], + const uint32_t x[17]) +{ + uint32_t carry = 0; + int i; + + for (i = 0; i < 17; i++) + { + carry += h[i] + x[i]; + h[i] = carry & 0xff; + carry >>= 8; + } +} + +/* Minimal reduction/carry chain. */ +static void poly1305_min_reduce(uint32_t x[17]) +{ + uint32_t carry = 0; + int i; + for (i = 0; i < 16; i++) + { + carry += x[i]; + x[i] = carry & 0xff; + carry >>= 8; + } + + /* 2 ** 130 - 5 = 0x3fffffffffffffffffffffffffffffffb + * ^ + * So 2 bits of carry are put into top word. + * Remaining bits get multiplied by 5 and carried back + * into bottom */ + carry += x[16]; + x[16] = carry & 0x03; + carry = 5 * (carry >> 2); + + for (i = 0; i < 16; i++) + { + carry += x[i]; + x[i] = carry & 0xff; + carry >>= 8; + } + + x[16] += carry; +} + +/* This is - 2 ** 130 - 5 in twos complement. */ +static const uint32_t negative_1305[17] = { + 0x05, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0xfc +}; + +static void poly1305_full_reduce(uint32_t x[17]) +{ + uint32_t xsub[17]; + size_t i; + + for (i = 0; i < 17; i++) + xsub[i] = x[i]; + + poly1305_add(xsub, negative_1305); + + /* If x - (2 ** 130 - 5) is negative, then + * x didn't need reduction: we discard the results. + * Do this in a side-channel silent way. */ + uint32_t negative_mask = mask_u32(xsub[16] & 0x80, 0x80); + uint32_t positive_mask = negative_mask ^ 0xffffffff; + + for (i = 0; i < 17; i++) + x[i] = (x[i] & negative_mask) | (xsub[i] & positive_mask); +} + +static void poly1305_mul(uint32_t x[17], + const uint32_t y[17]) +{ + uint32_t r[17]; + int i; + + for (i = 0; i < 17; i++) + { + uint32_t accum = 0; + int j; + + for (j = 0; j <= i; j++) + accum += x[j] * y[i - j]; + + /* Add in carries. These get shifted 130 bits + * to the right, with a combination of byte indexing + * and shifting (136 bits right, then 6 bits left). + * + * nb. 5 << 6 is made up of two parts: + * 5: reduction of 2 ** 130 leaves a multiple 5 + * shift 6 places left + * 17 * 8: byte indexing shift (136 bits) + * 130: desired shift + */ + for (j = i + 1; j < 17; j++) + accum += (5 << 6) * x[j] * y[i + 17 - j]; + + r[i] = accum; + } + + poly1305_min_reduce(r); + + for (i = 0; i < 17; i++) + x[i] = r[i]; +} + +static void poly1305_block(cf_poly1305 *ctx, + const uint32_t c[17]) +{ + poly1305_add(ctx->h, c); + poly1305_mul(ctx->h, ctx->r); +} + +static void poly1305_whole_block(void *vctx, + const uint8_t *buf) +{ + cf_poly1305 *ctx = vctx; + uint32_t c[17]; + int i; + + for (i = 0; i < 16; i++) + c[i] = buf[i]; + + c[16] = 1; + poly1305_block(ctx, c); +} + +static void poly1305_last_block(cf_poly1305 *ctx) +{ + uint32_t c[17] = { 0 }; + size_t i; + + for (i = 0; i < ctx->npartial; i++) + c[i] = ctx->partial[i]; + + c[ctx->npartial] = 1; + poly1305_block(ctx, c); +} + +void cf_poly1305_update(cf_poly1305 *ctx, + const uint8_t *buf, + size_t nbytes) +{ + cf_blockwise_accumulate(ctx->partial, &ctx->npartial, + sizeof ctx->partial, + buf, nbytes, + poly1305_whole_block, + ctx); +} + +void cf_poly1305_finish(cf_poly1305 *ctx, + uint8_t out[16]) +{ + if (ctx->npartial) + poly1305_last_block(ctx); + + uint32_t s[17]; + size_t i; + for (i = 0; i < 16; i++) + s[i] = ctx->s[i]; + s[16] = 0; + + poly1305_full_reduce(ctx->h); + poly1305_add(ctx->h, s); + + for (i = 0; i < 16; i++) + out[i] = ctx->h[i]; + + mem_clean(ctx, sizeof *ctx); +} + diff --git a/deps/picoquic/picotls/deps/cifra/src/poly1305.h b/deps/picoquic/picotls/deps/cifra/src/poly1305.h new file mode 100644 index 0000000..b318e28 --- /dev/null +++ b/deps/picoquic/picotls/deps/cifra/src/poly1305.h @@ -0,0 +1,91 @@ +/* + * cifra - embedded cryptography library + * Written in 2014 by Joseph Birr-Pixton + * + * To the extent possible under law, the author(s) have dedicated all + * copyright and related and neighboring rights to this software to the + * public domain worldwide. This software is distributed without any + * warranty. + * + * You should have received a copy of the CC0 Public Domain Dedication + * along with this software. If not, see + * . + */ + +#ifndef POLY1305_H +#define POLY1305_H + +#include +#include + +/** + * Poly1305 + * ======== + * This is an incremental interface to computing the poly1305 + * single shot MAC. + * + * Note: construct Poly1305-AES with this by taking a 16 byte + * nonce and encrypting it, and then using the result as an + * input to this function. + */ + +/* .. c:type:: cf_poly1305 + * Poly1305 incremental interface context. + * + * .. c:member:: cf_poly1305.h + * Current accumulator. + * + * .. c:member:: cf_poly1305.r + * Block multiplier. + * + * .. c:member:: cf_poly1305.s + * Final XOR offset. + * + * .. c:member:: cf_poly1305.partial + * Unprocessed input. + * + * .. c:member:: cf_poly1305.npartial + * Number of bytes of unprocessed input. + * + */ +typedef struct +{ + uint32_t h[17]; + uint32_t r[17]; + uint8_t s[16]; + uint8_t partial[16]; + size_t npartial; +} cf_poly1305; + +/* .. c:function:: $DECL + * Sets up `ctx` ready to compute a new MAC. + * + * In Poly1305-AES, `r` is the second half of the 32-byte key. + * `s` is a nonce encrypted under the first half of the key. + * + * :param ctx: context (written) + * :param r: MAC key. + * :param s: preprocessed nonce. + * + */ +void cf_poly1305_init(cf_poly1305 *ctx, + const uint8_t r[16], + const uint8_t s[16]); + +/* .. c:function:: $DECL + * Processes `nbytes` at `data`. Copies the data if there isn't enough to make + * a full block. + */ +void cf_poly1305_update(cf_poly1305 *ctx, + const uint8_t *data, + size_t nbytes); + +/* .. c:function:: $DECL + * Finishes the operation, writing 16 bytes to `out`. + * + * This destroys `ctx`. + */ +void cf_poly1305_finish(cf_poly1305 *ctx, + uint8_t out[16]); + +#endif diff --git a/deps/picoquic/picotls/deps/cifra/src/poly1305.py b/deps/picoquic/picotls/deps/cifra/src/poly1305.py new file mode 100644 index 0000000..41a1b9d --- /dev/null +++ b/deps/picoquic/picotls/deps/cifra/src/poly1305.py @@ -0,0 +1,127 @@ +rs = (0xee,0xa6,0xa7,0x25,0x1c,0x1e,0x72,0x91 +,0x6d,0x11,0xc2,0xcb,0x21,0x4d,0x3c,0x25 +,0x25,0x39,0x12,0x1d,0x8e,0x23,0x4e,0x65 +,0x2d,0x65,0x1f,0xa4,0xc8,0xcf,0xf8,0x80) + +msg = (0x8e,0x99,0x3b,0x9f,0x48,0x68,0x12,0x73 + ,0xc2,0x96,0x50,0xba,0x32,0xfc,0x76,0xce + ,0x48,0x33,0x2e,0xa7,0x16,0x4d,0x96,0xa4 + ,0x47,0x6f,0xb8,0xc5,0x31,0xa1,0x18,0x6a + ,0xc0,0xdf,0xc1,0x7c,0x98,0xdc,0xe8,0x7b + ,0x4d,0xa7,0xf0,0x11,0xec,0x48,0xc9,0x72 + ,0x71,0xd2,0xc2,0x0f,0x9b,0x92,0x8f,0xe2 + ,0x27,0x0d,0x6f,0xb8,0x63,0xd5,0x17,0x38 + ,0xb4,0x8e,0xee,0xe3,0x14,0xa7,0xcc,0x8a + ,0xb9,0x32,0x16,0x45,0x48,0xe5,0x26,0xae + ,0x90,0x22,0x43,0x68,0x51,0x7a,0xcf,0xea + ,0xbd,0x6b,0xb3,0x73,0x2b,0xc0,0xe9,0xda + ,0x99,0x83,0x2b,0x61,0xca,0x01,0xb6,0xde + ,0x56,0x24,0x4a,0x9e,0x88,0xd5,0xf9,0xb3 + ,0x79,0x73,0xf6,0x22,0xa4,0x3d,0x14,0xa6 + ,0x59,0x9b,0x1f,0x65,0x4c,0xb4,0x5a,0x74 + ,0xe3,0x55,0xa5) + +tag = (0xf3,0xff,0xc7,0x70,0x3f,0x94,0x00,0xe5 +,0x2a,0x7d,0xfb,0x4b,0x3d,0x33,0x05,0xd9) + +print len(msg) + +WORD = 0xffffffff + +def add(x, y): + u = 0 + r = [0] * 17 + for i in range(17): + u += (x[i] + y[i]) & WORD + r[i] = u & 0xff + u >>= 8 + return r + +def reduce(x): + r = list(x) + u = 0 + + for i in range(16): + u += r[i] + r[i] = u & 0xff + u >>= 8 + u += r[16] + r[16] = u & 3 + u = 5 * (u >> 2) + + for i in range(16): + u += r[i] + r[i] = u & 0xff + u >>= 8 + u += r[16] + r[16] = u + + return r + +def modmul(x, y): + r = [0] * 17 + for i in range(17): + u = 0 + for j in range(i + 1): + u += (x[j] * y[i - j]) & WORD + for j in range(i + 1, 17): + u += (320 * x[j] * y[i + 17 - j]) & WORD + r[i] = u + return reduce(r) + +def dump(why, v): + print '%s = %s' % (why, ' '.join('%08x' % x for x in v)) + +def freeze(x): + # -2^130 - 5 in twos complement + negative_130_5 = (5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 252) + + r = add(x, negative_130_5) + dump('minusp', r) + negative = bool(x[16] >> 7) + if negative: + return r + else: + return x + +def poly1305(msg, rs): + r, s = list(rs[:16]), list(rs[16:]) + + r[3] &= 15 + r[4] &= 252 + r[7] &= 15 + r[8] &= 252 + r[11] &= 15 + r[12] &= 252 + r[15] &= 15 + r.append(0) + + h = [0] * 17 + dump('r-init', r) + dump('h-init', h) + + block = 0 + + for offs in range(0, len(msg), 16): + print '--- block %d ---' % block + block += 1 + c = list(msg[offs:offs+16]) + c.append(1) + while len(c) != 17: c.append(0) + dump('c', c) + + h = add(h, c) + dump('after-add', h) + h = modmul(h, r) + dump('after-mul', h) + + dump('end-block', h) + h = freeze(h) + dump('h', h) + s.append(0) + h = add(h, s) + dump('final', h) + return h[:16] + +r = poly1305(msg, rs) +print repr([hex(x) for x in r]) diff --git a/deps/picoquic/picotls/deps/cifra/src/prp.h b/deps/picoquic/picotls/deps/cifra/src/prp.h new file mode 100644 index 0000000..1aa7258 --- /dev/null +++ b/deps/picoquic/picotls/deps/cifra/src/prp.h @@ -0,0 +1,64 @@ +/* + * cifra - embedded cryptography library + * Written in 2014 by Joseph Birr-Pixton + * + * To the extent possible under law, the author(s) have dedicated all + * copyright and related and neighboring rights to this software to the + * public domain worldwide. This software is distributed without any + * warranty. + * + * You should have received a copy of the CC0 Public Domain Dedication + * along with this software. If not, see + * . + */ + +#ifndef PRP_H +#define PRP_H + +#include +#include + +/** + * General block cipher description + * ================================ + * This allows us to implement block cipher modes which can work + * with different block ciphers. + */ + +/* .. c:type:: cf_prp_block + * Block processing function type. + * + * The `in` and `out` blocks may alias. + * + * :rtype: void + * :param ctx: block cipher-specific context object. + * :param in: input block. + * :param out: output block. + */ +typedef void (*cf_prp_block)(void *ctx, const uint8_t *in, uint8_t *out); + +/* .. c:type:: cf_prp + * Describes an PRP in a general way. + * + * .. c:member:: cf_prp.blocksz + * Block size in bytes. Must be no more than :c:macro:`CF_MAXBLOCK`. + * + * .. c:member:: cf_prp.encrypt + * Block encryption function. + * + * .. c:member:: cf_prp.decrypt + * Block decryption function. + */ +typedef struct +{ + size_t blocksz; + cf_prp_block encrypt; + cf_prp_block decrypt; +} cf_prp; + +/* .. c:macro:: CF_MAXBLOCK + * The maximum block cipher blocksize we support, in bytes. + */ +#define CF_MAXBLOCK 16 + +#endif diff --git a/deps/picoquic/picotls/deps/cifra/src/salsa20.c b/deps/picoquic/picotls/deps/cifra/src/salsa20.c new file mode 100644 index 0000000..34bd156 --- /dev/null +++ b/deps/picoquic/picotls/deps/cifra/src/salsa20.c @@ -0,0 +1,164 @@ +/* + * cifra - embedded cryptography library + * Written in 2014 by Joseph Birr-Pixton + * + * To the extent possible under law, the author(s) have dedicated all + * copyright and related and neighboring rights to this software to the + * public domain worldwide. This software is distributed without any + * warranty. + * + * You should have received a copy of the CC0 Public Domain Dedication + * along with this software. If not, see + * . + */ + +#include "salsa20.h" +#include "bitops.h" +#include "blockwise.h" + +#include +#include + +void cf_salsa20_core(const uint8_t key0[16], + const uint8_t key1[16], + const uint8_t nonce[16], + const uint8_t constant[16], + uint8_t out[64]) +{ + /* unpack sequence is: + * + * c0 + * key0 + * c1 + * nonce + * c2 + * key1 + * c3 + * + * where c0, c1, c2, c3 = constant + */ + + uint32_t z0, z1, z2, z3, z4, z5, z6, z7, + z8, z9, za, zb, zc, zd, ze, zf; + + uint32_t x0 = z0 = read32_le(constant + 0), + x1 = z1 = read32_le(key0 + 0), + x2 = z2 = read32_le(key0 + 4), + x3 = z3 = read32_le(key0 + 8), + x4 = z4 = read32_le(key0 + 12), + x5 = z5 = read32_le(constant + 4), + x6 = z6 = read32_le(nonce + 0), + x7 = z7 = read32_le(nonce + 4), + x8 = z8 = read32_le(nonce + 8), + x9 = z9 = read32_le(nonce + 12), + xa = za = read32_le(constant + 8), + xb = zb = read32_le(key1 + 0), + xc = zc = read32_le(key1 + 4), + xd = zd = read32_le(key1 + 8), + xe = ze = read32_le(key1 + 12), + xf = zf = read32_le(constant + 12); + +#define QUARTER(v0, v1, v2, v3) \ + v1 ^= rotl32(v0 + v3, 7); \ + v2 ^= rotl32(v1 + v0, 9); \ + v3 ^= rotl32(v2 + v1, 13);\ + v0 ^= rotl32(v3 + v2, 18) + +#define ROW \ + QUARTER(z0, z1, z2, z3); \ + QUARTER(z5, z6, z7, z4); \ + QUARTER(za, zb, z8, z9); \ + QUARTER(zf, zc, zd, ze) + +#define COLUMN\ + QUARTER(z0, z4, z8, zc); \ + QUARTER(z5, z9, zd, z1); \ + QUARTER(za, ze, z2, z6); \ + QUARTER(zf, z3, z7, zb) + + for (int i = 0; i < 10; i++) + { + COLUMN; + ROW; + } + + x0 += z0; + x1 += z1; + x2 += z2; + x3 += z3; + x4 += z4; + x5 += z5; + x6 += z6; + x7 += z7; + x8 += z8; + x9 += z9; + xa += za; + xb += zb; + xc += zc; + xd += zd; + xe += ze; + xf += zf; + + write32_le(x0, out + 0); + write32_le(x1, out + 4); + write32_le(x2, out + 8); + write32_le(x3, out + 12); + write32_le(x4, out + 16); + write32_le(x5, out + 20); + write32_le(x6, out + 24); + write32_le(x7, out + 28); + write32_le(x8, out + 32); + write32_le(x9, out + 36); + write32_le(xa, out + 40); + write32_le(xb, out + 44); + write32_le(xc, out + 48); + write32_le(xd, out + 52); + write32_le(xe, out + 56); + write32_le(xf, out + 60); +} + +static const uint8_t *salsa20_tau = (const uint8_t *) "expand 16-byte k"; +static const uint8_t *salsa20_sigma = (const uint8_t *) "expand 32-byte k"; + +void cf_salsa20_init(cf_salsa20_ctx *ctx, const uint8_t *key, size_t nkey, const uint8_t nonce[8]) +{ + switch (nkey) + { + case 16: + memcpy(ctx->key0, key, 16); + memcpy(ctx->key1, key, 16); + ctx->constant = salsa20_tau; + break; + case 32: + memcpy(ctx->key0, key, 16); + memcpy(ctx->key1, key + 16, 16); + ctx->constant = salsa20_sigma; + break; + default: + abort(); + } + + memset(ctx->nonce, 0, sizeof ctx->nonce); + memcpy(ctx->nonce + 8, nonce, 8); + ctx->nblock = 0; + ctx->ncounter = 8; +} + +static void cf_salsa20_next_block(void *vctx, uint8_t *out) +{ + cf_salsa20_ctx *ctx = vctx; + cf_salsa20_core(ctx->key0, + ctx->key1, + ctx->nonce, + ctx->constant, + out); + incr_le(ctx->nonce, ctx->ncounter); +} + +void cf_salsa20_cipher(cf_salsa20_ctx *ctx, const uint8_t *input, uint8_t *output, size_t bytes) +{ + cf_blockwise_xor(ctx->block, &ctx->nblock, 64, + input, output, bytes, + cf_salsa20_next_block, + ctx); +} diff --git a/deps/picoquic/picotls/deps/cifra/src/salsa20.h b/deps/picoquic/picotls/deps/cifra/src/salsa20.h new file mode 100644 index 0000000..462d019 --- /dev/null +++ b/deps/picoquic/picotls/deps/cifra/src/salsa20.h @@ -0,0 +1,140 @@ +/* + * cifra - embedded cryptography library + * Written in 2014 by Joseph Birr-Pixton + * + * To the extent possible under law, the author(s) have dedicated all + * copyright and related and neighboring rights to this software to the + * public domain worldwide. This software is distributed without any + * warranty. + * + * You should have received a copy of the CC0 Public Domain Dedication + * along with this software. If not, see + * . + */ + +#ifndef SALSA20_H +#define SALSA20_H + +#include +#include + +/** + * The Salsa20/Chacha20 stream ciphers + * =================================== + * + * These are similar stream ciphers by djb. + * + * A reduced round variant of Salsa20 (Salsa20/12) + * was selected as a finalist of the eSTREAM stream + * cipher competition. This implementation does + * the full 20 rounds. + * + * ChaCha20 is fundamentally like Salsa20, but + * has a tweaked round function to improve security + * margin without damaging performance. + */ + +/* Salsa20 core transform. */ +void cf_salsa20_core(const uint8_t key0[16], + const uint8_t key1[16], + const uint8_t nonce[16], + const uint8_t constant[16], + uint8_t out[64]); + +/* Chacha20 core transform. */ +void cf_chacha20_core(const uint8_t key0[16], + const uint8_t key1[16], + const uint8_t nonce[16], + const uint8_t constant[16], + uint8_t out[64]); + +/* .. c:type:: cf_salsa20_ctx + * Incremental interface to Salsa20. + * + * .. c:member:: cf_salsa20_ctx.key0 + * Half of key material. + * + * .. c:member:: cf_salsa20_ctx.key1 + * Half of key material. + * + * .. c:member:: cf_salsa20_ctx.nonce + * Nonce and counter block. + * + * .. c:member:: cf_salsa20_ctx.constant + * Per-key-length constants. + * + * .. c:member:: cf_salsa20_ctx.block + * Buffer for unused key stream material. + * + * .. c:member:: cf_salsa20_ctx.nblock + * Number of bytes at end of `block` that can be used as key stream. + * + */ +typedef struct +{ + uint8_t key0[16], key1[16]; + uint8_t nonce[16]; + const uint8_t *constant; + uint8_t block[64]; + size_t nblock; + size_t ncounter; +} cf_salsa20_ctx, cf_chacha20_ctx; + +/* .. c:type:: cf_chacha20_ctx + * Incremental interface to Chacha20. This structure + * is identical to :c:type:`cf_salsa20_ctx`. + */ + +/* .. c:function:: $DECL + * Salsa20 initialisation function. + * + * :param ctx: salsa20 context. + * :param key: key material. + * :param nkey: length of key in bytes, either 16 or 32. + * :param nonce: per-message nonce. + */ +void cf_salsa20_init(cf_salsa20_ctx *ctx, const uint8_t *key, size_t nkey, const uint8_t nonce[8]); + +/* .. c:function:: $DECL + * Chacha20 initialisation function. + * + * :param ctx: chacha20 context (written). + * :param key: key material. + * :param nkey: length of key in bytes, either 16 or 32. + * :param nonce: per-message nonce. + */ +void cf_chacha20_init(cf_chacha20_ctx *ctx, const uint8_t *key, size_t nkey, const uint8_t nonce[8]); + +/* .. c:function:: $DECL + * Chacha20 initialisation function. This version gives full control over the whole + * initial nonce value, and the size of the counter. The counter is always at the front + * of the nonce. + * + * :param ctx: chacha20 context (written). + * :param key: key material. + * :param nkey: length of key in bytes, either 16 or 32. + * :param nonce: per-message nonce. `ncounter` bytes at the start are the block counter. + * :param ncounter: length, in bytes, of the counter portion of the nonce. + */ +void cf_chacha20_init_custom(cf_chacha20_ctx *ctx, const uint8_t *key, size_t nkey, + const uint8_t nonce[16], size_t ncounter); + +/* .. c:function:: $DECL + * Salsa20 encryption/decryption function. + * + * :param ctx: salsa20 context. + * :param input: input data buffer (read), `count` bytes long. + * :param output: output data buffer (written), `count` bytes long. + */ +void cf_salsa20_cipher(cf_salsa20_ctx *ctx, const uint8_t *input, uint8_t *output, size_t count); + +/* .. c:function:: $DECL + * Chacha20 encryption/decryption function. + * + * :param ctx: chacha20 context. + * :param input: input data buffer (read), `count` bytes long. + * :param output: output data buffer (written), `count` bytes long. + */ +void cf_chacha20_cipher(cf_chacha20_ctx *ctx, const uint8_t *input, uint8_t *output, size_t count); + +#endif diff --git a/deps/picoquic/picotls/deps/cifra/src/sha1.c b/deps/picoquic/picotls/deps/cifra/src/sha1.c new file mode 100644 index 0000000..8b7d02f --- /dev/null +++ b/deps/picoquic/picotls/deps/cifra/src/sha1.c @@ -0,0 +1,150 @@ +/* + * cifra - embedded cryptography library + * Written in 2014 by Joseph Birr-Pixton + * + * To the extent possible under law, the author(s) have dedicated all + * copyright and related and neighboring rights to this software to the + * public domain worldwide. This software is distributed without any + * warranty. + * + * You should have received a copy of the CC0 Public Domain Dedication + * along with this software. If not, see + * . + */ + +#include + +#include "sha1.h" +#include "blockwise.h" +#include "bitops.h" +#include "handy.h" +#include "tassert.h" + +void cf_sha1_init(cf_sha1_context *ctx) +{ + memset(ctx, 0, sizeof *ctx); + ctx->H[0] = 0x67452301; + ctx->H[1] = 0xefcdab89; + ctx->H[2] = 0x98badcfe; + ctx->H[3] = 0x10325476; + ctx->H[4] = 0xc3d2e1f0; +} + +static void sha1_update_block(void *vctx, const uint8_t *inp) +{ + cf_sha1_context *ctx = vctx; + + /* This is a 16-word window into the whole W array. */ + uint32_t W[16]; + + uint32_t a = ctx->H[0], + b = ctx->H[1], + c = ctx->H[2], + d = ctx->H[3], + e = ctx->H[4], + Wt; + + for (size_t t = 0; t < 80; t++) + { + /* For W[0..16] we process the input into W. + * For W[16..79] we compute the next W value: + * + * W[t] = (W[t - 3] ^ W[t - 8] ^ W[t - 14] ^ W[t - 16]) <<< 1 + * + * But all W indices are reduced mod 16 into our window. + */ + if (t < 16) + { + W[t] = Wt = read32_be(inp); + inp += 4; + } else { + Wt = W[(t - 3) % 16] ^ W[(t - 8) % 16] ^ W[(t - 14) % 16] ^ W[(t - 16) % 16]; + Wt = rotl32(Wt, 1); + W[t % 16] = Wt; + } + + uint32_t f, k; + + if (t <= 19) + { + f = (b & c) | (~b & d); + k = 0x5a827999; + } else if (t <= 39) { + f = b ^ c ^ d; + k = 0x6ed9eba1; + } else if (t <= 59) { + f = (b & c) | (b & d) | (c & d); + k = 0x8f1bbcdc; + } else { + f = b ^ c ^ d; + k = 0xca62c1d6; + } + + uint32_t temp = rotl32(a, 5) + f + e + k + Wt; + e = d; + d = c; + c = rotl32(b, 30); + b = a; + a = temp; + } + + ctx->H[0] += a; + ctx->H[1] += b; + ctx->H[2] += c; + ctx->H[3] += d; + ctx->H[4] += e; + + ctx->blocks++; +} + +void cf_sha1_update(cf_sha1_context *ctx, const void *data, size_t nbytes) +{ + cf_blockwise_accumulate(ctx->partial, &ctx->npartial, sizeof ctx->partial, + data, nbytes, + sha1_update_block, ctx); +} + +void cf_sha1_digest(const cf_sha1_context *ctx, uint8_t hash[CF_SHA1_HASHSZ]) +{ + cf_sha1_context ours = *ctx; + cf_sha1_digest_final(&ours, hash); +} + +void cf_sha1_digest_final(cf_sha1_context *ctx, uint8_t hash[CF_SHA1_HASHSZ]) +{ + uint64_t digested_bytes = ctx->blocks; + digested_bytes = digested_bytes * CF_SHA1_BLOCKSZ + ctx->npartial; + uint64_t digested_bits = digested_bytes * 8; + + size_t padbytes = CF_SHA1_BLOCKSZ - ((digested_bytes + 8) % CF_SHA1_BLOCKSZ); + + /* Hash 0x80 00 ... block first. */ + cf_blockwise_acc_pad(ctx->partial, &ctx->npartial, sizeof ctx->partial, + 0x80, 0x00, 0x00, padbytes, + sha1_update_block, ctx); + + /* Now hash length. */ + uint8_t buf[8]; + write64_be(digested_bits, buf); + cf_sha1_update(ctx, buf, 8); + + /* We ought to have got our padding calculation right! */ + assert(ctx->npartial == 0); + + write32_be(ctx->H[0], hash + 0); + write32_be(ctx->H[1], hash + 4); + write32_be(ctx->H[2], hash + 8); + write32_be(ctx->H[3], hash + 12); + write32_be(ctx->H[4], hash + 16); + + memset(ctx, 0, sizeof *ctx); +} + +const cf_chash cf_sha1 = { + .hashsz = CF_SHA1_HASHSZ, + .blocksz = CF_SHA1_BLOCKSZ, + .init = (cf_chash_init) cf_sha1_init, + .update = (cf_chash_update) cf_sha1_update, + .digest = (cf_chash_digest) cf_sha1_digest +}; + diff --git a/deps/picoquic/picotls/deps/cifra/src/sha1.h b/deps/picoquic/picotls/deps/cifra/src/sha1.h new file mode 100644 index 0000000..4ca0e91 --- /dev/null +++ b/deps/picoquic/picotls/deps/cifra/src/sha1.h @@ -0,0 +1,91 @@ +/* + * cifra - embedded cryptography library + * Written in 2014 by Joseph Birr-Pixton + * + * To the extent possible under law, the author(s) have dedicated all + * copyright and related and neighboring rights to this software to the + * public domain worldwide. This software is distributed without any + * warranty. + * + * You should have received a copy of the CC0 Public Domain Dedication + * along with this software. If not, see + * . + */ + +#ifndef SHA1_H +#define SHA1_H + +#include +#include + +#include "chash.h" + +/** + * SHA1 + * ==== + * + * You shouldn't use this for anything new. + */ + +/* .. c:macro:: CF_SHA1_HASHSZ + * The output size of SHA1: 20 bytes. */ +#define CF_SHA1_HASHSZ 20 + +/* .. c:macro:: CF_SHA1_BLOCKSZ + * The block size of SHA1: 64 bytes. */ +#define CF_SHA1_BLOCKSZ 64 + +/* .. c:type:: cf_sha1_context + * Incremental SHA1 hashing context. + * + * .. c:member:: cf_sha1_context.H + * Intermediate values. + * + * .. c:member:: cf_sha1_context.partial + * Unprocessed input. + * + * .. c:member:: cf_sha1_context.npartial + * Number of bytes of unprocessed input. + * + * .. c:member:: cf_sha1_context.blocks + * Number of full blocks processed. + */ +typedef struct +{ + uint32_t H[5]; /* State. */ + uint8_t partial[CF_SHA1_BLOCKSZ]; /* Partial block of input. */ + uint32_t blocks; /* Number of full blocks processed into H. */ + size_t npartial; /* Number of bytes in prefix of partial. */ +} cf_sha1_context; + +/* .. c:function:: $DECL + * Sets up `ctx` ready to hash a new message. + */ +extern void cf_sha1_init(cf_sha1_context *ctx); + +/* .. c:function:: $DECL + * Hashes `nbytes` at `data`. Copies the data if there isn't enough to make + * a full block. + */ +extern void cf_sha1_update(cf_sha1_context *ctx, const void *data, size_t nbytes); + +/* .. c:function:: $DECL + * Finishes the hash operation, writing `CF_SHA1_HASHSZ` bytes to `hash`. + * + * This leaves `ctx` unchanged. + */ +extern void cf_sha1_digest(const cf_sha1_context *ctx, uint8_t hash[CF_SHA1_HASHSZ]); + +/* .. c:function:: $DECL + * Finishes the hash operation, writing `CF_SHA1_HASHSZ` bytes to `hash`. + * + * This destroys `ctx`, but uses less stack than :c:func:`cf_sha1_digest`. + */ +extern void cf_sha1_digest_final(cf_sha1_context *ctx, uint8_t hash[CF_SHA1_HASHSZ]); + +/* .. c:var:: cf_sha1 + * Abstract interface to SHA1. See :c:type:`cf_chash` for more information. + */ +extern const cf_chash cf_sha1; + +#endif diff --git a/deps/picoquic/picotls/deps/cifra/src/sha2.h b/deps/picoquic/picotls/deps/cifra/src/sha2.h new file mode 100644 index 0000000..c4ed24b --- /dev/null +++ b/deps/picoquic/picotls/deps/cifra/src/sha2.h @@ -0,0 +1,235 @@ +/* + * cifra - embedded cryptography library + * Written in 2014 by Joseph Birr-Pixton + * + * To the extent possible under law, the author(s) have dedicated all + * copyright and related and neighboring rights to this software to the + * public domain worldwide. This software is distributed without any + * warranty. + * + * You should have received a copy of the CC0 Public Domain Dedication + * along with this software. If not, see + * . + */ + +#ifndef SHA2_H +#define SHA2_H + +#include +#include + +#include "chash.h" + +/** + * SHA224/SHA256 + * ============= + */ + +/* .. c:macro:: CF_SHA224_HASHSZ + * The output size of SHA224: 28 bytes. */ +#define CF_SHA224_HASHSZ 28 + +/* .. c:macro:: CF_SHA224_BLOCKSZ + * The block size of SHA224: 64 bytes. */ +#define CF_SHA224_BLOCKSZ 64 + +/* .. c:macro:: CF_SHA256_HASHSZ + * The output size of SHA256: 32 bytes. */ +#define CF_SHA256_HASHSZ 32 + +/* .. c:macro:: CF_SHA256_BLOCKSZ + * The block size of SHA256: 64 bytes. */ +#define CF_SHA256_BLOCKSZ 64 + +/* .. c:type:: cf_sha256_context + * Incremental SHA256 hashing context. + * + * .. c:member:: cf_sha256_context.H + * Intermediate values. + * + * .. c:member:: cf_sha256_context.partial + * Unprocessed input. + * + * .. c:member:: cf_sha256_context.npartial + * Number of bytes of unprocessed input. + * + * .. c:member:: cf_sha256_context.blocks + * Number of full blocks processed. + */ +typedef struct +{ + uint32_t H[8]; /* State. */ + uint8_t partial[CF_SHA256_BLOCKSZ]; /* Partial block of input. */ + uint32_t blocks; /* Number of full blocks processed into H. */ + size_t npartial; /* Number of bytes in prefix of partial. */ +} cf_sha256_context; + +/* .. c:function:: $DECL + * Sets up `ctx` ready to hash a new message. + */ +extern void cf_sha256_init(cf_sha256_context *ctx); + +/* .. c:function:: $DECL + * Hashes `nbytes` at `data`. Copies the data if there isn't enough to make + * a full block. + */ +extern void cf_sha256_update(cf_sha256_context *ctx, const void *data, size_t nbytes); + +/* .. c:function:: $DECL + * Finishes the hash operation, writing `CF_SHA256_HASHSZ` bytes to `hash`. + * + * This leaves `ctx` unchanged. + */ +extern void cf_sha256_digest(const cf_sha256_context *ctx, uint8_t hash[CF_SHA256_HASHSZ]); + +/* .. c:function:: $DECL + * Finishes the hash operation, writing `CF_SHA256_HASHSZ` bytes to `hash`. + * + * This destroys `ctx`, but uses less stack than :c:func:`cf_sha256_digest`. + */ +extern void cf_sha256_digest_final(cf_sha256_context *ctx, uint8_t hash[CF_SHA256_HASHSZ]); + +/* .. c:function:: $DECL + * Sets up `ctx` ready to hash a new message. + * + * nb. SHA224 uses SHA256's underlying types. + */ +extern void cf_sha224_init(cf_sha256_context *ctx); + +/* .. c:function:: $DECL + * Hashes `nbytes` at `data`. Copies the data if there isn't enough to make + * a full block. + */ +extern void cf_sha224_update(cf_sha256_context *ctx, const void *data, size_t nbytes); + +/* .. c:function:: $DECL + * Finishes the hash operation, writing `CF_SHA224_HASHSZ` bytes to `hash`. + * + * This leaves `ctx` unchanged. + */ +extern void cf_sha224_digest(const cf_sha256_context *ctx, uint8_t hash[CF_SHA224_HASHSZ]); + +/* .. c:function:: $DECL + * Finishes the hash operation, writing `CF_SHA224_HASHSZ` bytes to `hash`. + * + * This destroys `ctx`, but uses less stack than :c:func:`cf_sha224_digest`. + */ +extern void cf_sha224_digest_final(cf_sha256_context *ctx, uint8_t hash[CF_SHA224_HASHSZ]); + +/* .. c:var:: cf_sha224 + * Abstract interface to SHA224. See :c:type:`cf_chash` for more information. + */ +extern const cf_chash cf_sha224; + +/* .. c:var:: cf_sha256 + * Abstract interface to SHA256. See :c:type:`cf_chash` for more information. + */ +extern const cf_chash cf_sha256; + +/** + * SHA384/SHA512 + * ============= + */ + +/* .. c:macro:: CF_SHA384_HASHSZ + * The output size of SHA384: 48 bytes. */ +#define CF_SHA384_HASHSZ 48 + +/* .. c:macro:: CF_SHA384_BLOCKSZ + * The block size of SHA384: 128 bytes. */ +#define CF_SHA384_BLOCKSZ 128 + +/* .. c:macro:: CF_SHA512_HASHSZ + * The output size of SHA512: 64 bytes. */ +#define CF_SHA512_HASHSZ 64 + +/* .. c:macro:: CF_SHA512_BLOCKSZ + * The block size of SHA512: 128 bytes. */ +#define CF_SHA512_BLOCKSZ 128 + +/* .. c:type:: cf_sha512_context + * Incremental SHA512 hashing context. + * + * .. c:member:: cf_sha512_context.H + * Intermediate values. + * + * .. c:member:: cf_sha512_context.partial + * Unprocessed input. + * + * .. c:member:: cf_sha512_context.npartial + * Number of bytes of unprocessed input. + * + * .. c:member:: cf_sha512_context.blocks + * Number of full blocks processed. + */ +typedef struct +{ + uint64_t H[8]; + uint8_t partial[CF_SHA512_BLOCKSZ]; + uint32_t blocks; + size_t npartial; +} cf_sha512_context; + +/* .. c:function:: $DECL + * Sets up `ctx` ready to hash a new message. + */ +extern void cf_sha512_init(cf_sha512_context *ctx); + +/* .. c:function:: $DECL + * Hashes `nbytes` at `data`. Copies the data if there isn't enough to make + * a full block. + */ +extern void cf_sha512_update(cf_sha512_context *ctx, const void *data, size_t nbytes); + +/* .. c:function:: $DECL + * Finishes the hash operation, writing `CF_SHA512_HASHSZ` bytes to `hash`. + * + * This leaves `ctx` unchanged. + */ +extern void cf_sha512_digest(const cf_sha512_context *ctx, uint8_t hash[CF_SHA512_HASHSZ]); + +/* .. c:function:: $DECL + * Finishes the hash operation, writing `CF_SHA512_HASHSZ` bytes to `hash`. + * + * This destroys `ctx`, but uses less stack than :c:func:`cf_sha512_digest`. + */ +extern void cf_sha512_digest_final(cf_sha512_context *ctx, uint8_t hash[CF_SHA512_HASHSZ]); + +/* .. c:function:: $DECL + * Sets up `ctx` ready to hash a new message. + * + * nb. SHA384 uses SHA512's underlying types. + */ +extern void cf_sha384_init(cf_sha512_context *ctx); + +/* .. c:function:: $DECL + * Hashes `nbytes` at `data`. Copies the data if there isn't enough to make + * a full block. + */ +extern void cf_sha384_update(cf_sha512_context *ctx, const void *data, size_t nbytes); + +/* .. c:function:: $DECL + * Finishes the hash operation, writing `CF_SHA384_HASHSZ` bytes to `hash`. + * + * This leaves `ctx` unchanged. + */ +extern void cf_sha384_digest(const cf_sha512_context *ctx, uint8_t hash[CF_SHA384_HASHSZ]); + +/* .. c:function:: $DECL + * Finishes the hash operation, writing `CF_SHA384_HASHSZ` bytes to `hash`. + * + * This destroys `ctx`, but uses less stack than :c:func:`cf_sha384_digest`. + */ +extern void cf_sha384_digest_final(cf_sha512_context *ctx, uint8_t hash[CF_SHA384_HASHSZ]); + +/* .. c:var:: cf_sha384 + * Abstract interface to SHA384. See :c:type:`cf_chash` for more information. + */ +extern const cf_chash cf_sha384; + +/* .. c:var:: cf_sha512 + * Abstract interface to SHA512. See :c:type:`cf_chash` for more information. + */ +extern const cf_chash cf_sha512; + +#endif diff --git a/deps/picoquic/picotls/deps/cifra/src/sha256.c b/deps/picoquic/picotls/deps/cifra/src/sha256.c new file mode 100644 index 0000000..8ecef16 --- /dev/null +++ b/deps/picoquic/picotls/deps/cifra/src/sha256.c @@ -0,0 +1,232 @@ +/* + * cifra - embedded cryptography library + * Written in 2014 by Joseph Birr-Pixton + * + * To the extent possible under law, the author(s) have dedicated all + * copyright and related and neighboring rights to this software to the + * public domain worldwide. This software is distributed without any + * warranty. + * + * You should have received a copy of the CC0 Public Domain Dedication + * along with this software. If not, see + * . + */ + +#include + +#include "sha2.h" +#include "blockwise.h" +#include "bitops.h" +#include "handy.h" +#include "tassert.h" + +static const uint32_t K[64] = { + 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, + 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, + 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, + 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, + 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, + 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, + 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, + 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, + 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, + 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, + 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, + 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, + 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, + 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, + 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, + 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 +}; + +# define CH(x, y, z) (((x) & (y)) ^ (~(x) & (z))) +# define MAJ(x, y, z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z))) +# define BSIG0(x) (rotr32((x), 2) ^ rotr32((x), 13) ^ rotr32((x), 22)) +# define BSIG1(x) (rotr32((x), 6) ^ rotr32((x), 11) ^ rotr32((x), 25)) +# define SSIG0(x) (rotr32((x), 7) ^ rotr32((x), 18) ^ ((x) >> 3)) +# define SSIG1(x) (rotr32((x), 17) ^ rotr32((x), 19) ^ ((x) >> 10)) + +void cf_sha256_init(cf_sha256_context *ctx) +{ + memset(ctx, 0, sizeof *ctx); + ctx->H[0] = 0x6a09e667; + ctx->H[1] = 0xbb67ae85; + ctx->H[2] = 0x3c6ef372; + ctx->H[3] = 0xa54ff53a; + ctx->H[4] = 0x510e527f; + ctx->H[5] = 0x9b05688c; + ctx->H[6] = 0x1f83d9ab; + ctx->H[7] = 0x5be0cd19; +} + +void cf_sha224_init(cf_sha256_context *ctx) +{ + memset(ctx, 0, sizeof *ctx); + ctx->H[0] = 0xc1059ed8; + ctx->H[1] = 0x367cd507; + ctx->H[2] = 0x3070dd17; + ctx->H[3] = 0xf70e5939; + ctx->H[4] = 0xffc00b31; + ctx->H[5] = 0x68581511; + ctx->H[6] = 0x64f98fa7; + ctx->H[7] = 0xbefa4fa4; +} + +static void sha256_update_block(void *vctx, const uint8_t *inp) +{ + cf_sha256_context *ctx = vctx; + + /* This is a 16-word window into the whole W array. */ + uint32_t W[16]; + + uint32_t a = ctx->H[0], + b = ctx->H[1], + c = ctx->H[2], + d = ctx->H[3], + e = ctx->H[4], + f = ctx->H[5], + g = ctx->H[6], + h = ctx->H[7], + Wt; + + size_t t; + for (t = 0; t < 64; t++) + { + /* For W[0..16] we process the input into W. + * For W[16..64] we compute the next W value: + * + * W[t] = SSIG1(W[t - 2]) + W[t - 7] + SSIG0(W[t - 15]) + W[t - 16]; + * + * But all W indices are reduced mod 16 into our window. + */ + if (t < 16) + { + W[t] = Wt = read32_be(inp); + inp += 4; + } else { + Wt = SSIG1(W[(t - 2) % 16]) + + W[(t - 7) % 16] + + SSIG0(W[(t - 15) % 16]) + + W[(t - 16) % 16]; + W[t % 16] = Wt; + } + + uint32_t T1 = h + BSIG1(e) + CH(e, f, g) + K[t] + Wt; + uint32_t T2 = BSIG0(a) + MAJ(a, b, c); + h = g; + g = f; + f = e; + e = d + T1; + d = c; + c = b; + b = a; + a = T1 + T2; + } + + ctx->H[0] += a; + ctx->H[1] += b; + ctx->H[2] += c; + ctx->H[3] += d; + ctx->H[4] += e; + ctx->H[5] += f; + ctx->H[6] += g; + ctx->H[7] += h; + + ctx->blocks++; +} + +void cf_sha256_update(cf_sha256_context *ctx, const void *data, size_t nbytes) +{ + cf_blockwise_accumulate(ctx->partial, &ctx->npartial, sizeof ctx->partial, + data, nbytes, + sha256_update_block, ctx); +} + +void cf_sha224_update(cf_sha256_context *ctx, const void *data, size_t nbytes) +{ + cf_sha256_update(ctx, data, nbytes); +} + +void cf_sha256_digest(const cf_sha256_context *ctx, uint8_t hash[CF_SHA256_HASHSZ]) +{ + /* We copy the context, so the finalisation doesn't effect the caller's + * context. This means the caller can do: + * + * x = init() + * x.update('hello') + * h1 = x.digest() + * x.update(' world') + * h2 = x.digest() + * + * to get h1 = H('hello') and h2 = H('hello world') + * + * This wouldn't work if we applied MD-padding to *ctx. + */ + + cf_sha256_context ours = *ctx; + cf_sha256_digest_final(&ours, hash); +} + +void cf_sha256_digest_final(cf_sha256_context *ctx, uint8_t hash[CF_SHA256_HASHSZ]) +{ + uint64_t digested_bytes = ctx->blocks; + digested_bytes = digested_bytes * CF_SHA256_BLOCKSZ + ctx->npartial; + uint64_t digested_bits = digested_bytes * 8; + + size_t padbytes = CF_SHA256_BLOCKSZ - ((digested_bytes + 8) % CF_SHA256_BLOCKSZ); + + /* Hash 0x80 00 ... block first. */ + cf_blockwise_acc_pad(ctx->partial, &ctx->npartial, sizeof ctx->partial, + 0x80, 0x00, 0x00, padbytes, + sha256_update_block, ctx); + + /* Now hash length. */ + uint8_t buf[8]; + write64_be(digested_bits, buf); + cf_sha256_update(ctx, buf, 8); + + /* We ought to have got our padding calculation right! */ + assert(ctx->npartial == 0); + + write32_be(ctx->H[0], hash + 0); + write32_be(ctx->H[1], hash + 4); + write32_be(ctx->H[2], hash + 8); + write32_be(ctx->H[3], hash + 12); + write32_be(ctx->H[4], hash + 16); + write32_be(ctx->H[5], hash + 20); + write32_be(ctx->H[6], hash + 24); + write32_be(ctx->H[7], hash + 28); + + memset(ctx, 0, sizeof *ctx); +} + +void cf_sha224_digest(const cf_sha256_context *ctx, uint8_t hash[CF_SHA224_HASHSZ]) +{ + uint8_t full[CF_SHA256_HASHSZ]; + cf_sha256_digest(ctx, full); + memcpy(hash, full, CF_SHA224_HASHSZ); +} + +void cf_sha224_digest_final(cf_sha256_context *ctx, uint8_t hash[CF_SHA224_HASHSZ]) +{ + uint8_t full[CF_SHA256_HASHSZ]; + cf_sha256_digest_final(ctx, full); + memcpy(hash, full, CF_SHA224_HASHSZ); +} + +const cf_chash cf_sha224 = { + .hashsz = CF_SHA224_HASHSZ, + .blocksz = CF_SHA256_BLOCKSZ, + .init = (cf_chash_init) cf_sha224_init, + .update = (cf_chash_update) cf_sha224_update, + .digest = (cf_chash_digest) cf_sha224_digest +}; + +const cf_chash cf_sha256 = { + .hashsz = CF_SHA256_HASHSZ, + .blocksz = CF_SHA256_BLOCKSZ, + .init = (cf_chash_init) cf_sha256_init, + .update = (cf_chash_update) cf_sha256_update, + .digest = (cf_chash_digest) cf_sha256_digest +}; + diff --git a/deps/picoquic/picotls/deps/cifra/src/sha3.c b/deps/picoquic/picotls/deps/cifra/src/sha3.c new file mode 100644 index 0000000..f130747 --- /dev/null +++ b/deps/picoquic/picotls/deps/cifra/src/sha3.c @@ -0,0 +1,444 @@ +/* + * cifra - embedded cryptography library + * Written in 2014 by Joseph Birr-Pixton + * + * To the extent possible under law, the author(s) have dedicated all + * copyright and related and neighboring rights to this software to the + * public domain worldwide. This software is distributed without any + * warranty. + * + * You should have received a copy of the CC0 Public Domain Dedication + * along with this software. If not, see + * . + */ + +#include + +#include "sha3.h" +#include "blockwise.h" +#include "handy.h" +#include "bitops.h" +#include "tassert.h" + +/* The round constants, pre-interleaved. See bitinter.py */ +static const cf_sha3_bi round_constants[24] = { + { 0x00000001, 0x00000000 }, { 0x00000000, 0x00000089 }, + { 0x00000000, 0x8000008b }, { 0x00000000, 0x80008080 }, + { 0x00000001, 0x0000008b }, { 0x00000001, 0x00008000 }, + { 0x00000001, 0x80008088 }, { 0x00000001, 0x80000082 }, + { 0x00000000, 0x0000000b }, { 0x00000000, 0x0000000a }, + { 0x00000001, 0x00008082 }, { 0x00000000, 0x00008003 }, + { 0x00000001, 0x0000808b }, { 0x00000001, 0x8000000b }, + { 0x00000001, 0x8000008a }, { 0x00000001, 0x80000081 }, + { 0x00000000, 0x80000081 }, { 0x00000000, 0x80000008 }, + { 0x00000000, 0x00000083 }, { 0x00000000, 0x80008003 }, + { 0x00000001, 0x80008088 }, { 0x00000000, 0x80000088 }, + { 0x00000001, 0x00008000 }, { 0x00000000, 0x80008082 } +}; + +static const uint8_t rotation_constants[5][5] = { + { 0, 1, 62, 28, 27, }, + { 36, 44, 6, 55, 20, }, + { 3, 10, 43, 25, 39, }, + { 41, 45, 15, 21, 8, }, + { 18, 2, 61, 56, 14, } +}; + +/* --- Bit interleaving and uninterleaving --- */ +/* See bitinter.py for models of these bit twiddles. The originals + * come from "Hacker's Delight" by Henry Warren, where they are named + * shuffle2 and unshuffle. + * See: + * http://www.hackersdelight.org/hdcodetxt/shuffle.c.txt + * + * The overriding aim is to change bit ordering: + * AaBbCcDd -> ABCDabcd + * and back. Once they're in the shuffled form, we can extract + * odd/even bits by taking the half words from each pair. + */ + +static inline uint32_t shuffle_out(uint32_t x) +{ + uint32_t t; + t = (x ^ (x >> 1)) & 0x22222222; x = x ^ t ^ (t << 1); + t = (x ^ (x >> 2)) & 0x0c0c0c0c; x = x ^ t ^ (t << 2); + t = (x ^ (x >> 4)) & 0x00f000f0; x = x ^ t ^ (t << 4); + t = (x ^ (x >> 8)) & 0x0000ff00; x = x ^ t ^ (t << 8); + return x; +} + +/* Convert ABCDabcd -> AaBbCcDd. */ +static inline uint32_t shuffle_in(uint32_t x) +{ + uint32_t t; + t = (x ^ (x >> 8)) & 0x0000ff00; x = x ^ t ^ (t << 8); + t = (x ^ (x >> 4)) & 0x00f000f0; x = x ^ t ^ (t << 4); + t = (x ^ (x >> 2)) & 0x0c0c0c0c; x = x ^ t ^ (t << 2); + t = (x ^ (x >> 1)) & 0x22222222; x = x ^ t ^ (t << 1); + return x; +} + +static inline void read64_bi(cf_sha3_bi *out, const uint8_t data[8]) +{ + uint32_t lo = read32_le(data + 0), + hi = read32_le(data + 4); + + lo = shuffle_out(lo); + hi = shuffle_out(hi); + + out->odd = (lo & 0x0000ffff) | (hi << 16); + out->evn = (lo >> 16) | (hi & 0xffff0000); +} + +static inline void write64_bi(const cf_sha3_bi *bi, uint8_t data[8]) +{ + uint32_t lo = (bi->odd & 0x0000ffff) | (bi->evn << 16), + hi = (bi->odd >> 16) | (bi->evn & 0xffff0000); + + lo = shuffle_in(lo); + hi = shuffle_in(hi); + + write32_le(lo, data + 0); + write32_le(hi, data + 4); +} + +static inline void rotl_bi_1(cf_sha3_bi *out, const cf_sha3_bi *in) +{ + /* in bit-interleaved representation, a rotation of 1 + * is a swap plus a single rotation of the odd word. */ + out->odd = rotl32(in->evn, 1); + out->evn = in->odd; +} + +static inline void rotl_bi_n(cf_sha3_bi *out, const cf_sha3_bi *in, uint8_t rot) +{ + uint8_t half = rot >> 1; + + /* nb. rot is a constant, so this isn't a branch leak. */ + if (rot & 1) + { + out->odd = rotl32(in->evn, half + 1); + out->evn = rotl32(in->odd, half); + } else { + out->evn = rotl32(in->evn, half); + out->odd = rotl32(in->odd, half); + } +} + +/* --- */ + +static void sha3_init(cf_sha3_context *ctx, uint16_t rate_bits, uint16_t capacity_bits) +{ + mem_clean(ctx, sizeof *ctx); + ctx->rate = rate_bits / 8; + ctx->capacity = capacity_bits / 8; +} + +static void absorb(cf_sha3_context *ctx, const uint8_t *data, uint16_t sz) +{ + uint16_t lanes = sz / 8; + + for (uint16_t x = 0, y = 0, i = 0; i < lanes; i++) + { + cf_sha3_bi bi; + read64_bi(&bi, data); + ctx->A[x][y].odd ^= bi.odd; + ctx->A[x][y].evn ^= bi.evn; + data += 8; + + x++; + if (x == 5) + { + y++; + x = 0; + } + } +} + +/* Integers [-1,20] mod 5. To avoid a divmod. Indices + * are constants; not data-dependant. */ +static const uint8_t mod5_table[] = { + 4, + 0, + 1, 2, 3, 4, 0, 1, 2, 3, 4, 0, + 1, 2, 3, 4, 0, 1, 2, 3, 4, 0 +}; + +#define MOD5(x) (mod5_table[(x) + 1]) + +static void theta(cf_sha3_context *ctx) +{ + cf_sha3_bi C[5], D[5]; + + for (int x = 0; x < 5; x++) + { + C[x].odd = ctx->A[x][0].odd ^ ctx->A[x][1].odd ^ ctx->A[x][2].odd ^ ctx->A[x][3].odd ^ ctx->A[x][4].odd; + C[x].evn = ctx->A[x][0].evn ^ ctx->A[x][1].evn ^ ctx->A[x][2].evn ^ ctx->A[x][3].evn ^ ctx->A[x][4].evn; + } + + for (int x = 0; x < 5; x++) + { + cf_sha3_bi r; + rotl_bi_1(&r, &C[MOD5(x + 1)]); + D[x].odd = C[MOD5(x - 1)].odd ^ r.odd; + D[x].evn = C[MOD5(x - 1)].evn ^ r.evn; + + for (int y = 0; y < 5; y++) + { + ctx->A[x][y].odd ^= D[x].odd; + ctx->A[x][y].evn ^= D[x].evn; + } + } +} + +static void rho_pi_chi(cf_sha3_context *ctx) +{ + cf_sha3_bi B[5][5] = { { { 0 } } }; + + for (int x = 0; x < 5; x++) + for (int y = 0; y < 5; y++) + rotl_bi_n(&B[y][MOD5(2 * x + 3 * y)], &ctx->A[x][y], rotation_constants[y][x]); + + for (int x = 0; x < 5; x++) + { + unsigned x1 = MOD5(x + 1); + unsigned x2 = MOD5(x + 2); + + for (int y = 0; y < 5; y++) + { + ctx->A[x][y].odd = B[x][y].odd ^ ((~ B[x1][y].odd) & B[x2][y].odd); + ctx->A[x][y].evn = B[x][y].evn ^ ((~ B[x1][y].evn) & B[x2][y].evn); + } + } +} + +static void permute(cf_sha3_context *ctx) +{ + for (int r = 0; r < 24; r++) + { + theta(ctx); + rho_pi_chi(ctx); + + /* iota */ + ctx->A[0][0].odd ^= round_constants[r].odd; + ctx->A[0][0].evn ^= round_constants[r].evn; + } +} + +static void extract(cf_sha3_context *ctx, uint8_t *out, size_t nbytes) +{ + uint16_t lanes = (nbytes + 7) / 8; + + for (uint16_t x = 0, y = 0, i = 0; i < lanes; i++) + { + if (nbytes >= 8) + { + write64_bi(&ctx->A[x][y], out); + out += 8; + nbytes -= 8; + } else { + uint8_t buf[8]; + write64_bi(&ctx->A[x][y], buf); + memcpy(out, buf, nbytes); + out += nbytes; + nbytes = 0; + } + + x++; + if (x == 5) + { + y++; + x = 0; + } + } +} + +static void squeeze(cf_sha3_context *ctx, uint8_t *out, size_t nbytes) +{ + while (nbytes) + { + size_t take = MIN(nbytes, ctx->rate); + extract(ctx, out, take); + out += take; + nbytes -= take; + + assert(nbytes == 0); +#if 0 + /* Note: if we ever have |H| >= rate, we need to permute + * after each rate-length block. + * + * This cannot currently happen. */ + if (nbytes) + permute(ctx); +#endif + } +} + +static void sha3_block(void *vctx, const uint8_t *data) +{ + cf_sha3_context *ctx = vctx; + + absorb(ctx, data, ctx->rate); + permute(ctx); +} + +static void sha3_update(cf_sha3_context *ctx, const void *data, size_t nbytes) +{ + cf_blockwise_accumulate(ctx->partial, &ctx->npartial, ctx->rate, + data, nbytes, + sha3_block, ctx); +} + +/* Padding and domain separation constants. + * + * FIPS 202 specifies that 0b01 is appended to hash function + * input, and 0b1111 is appended to SHAKE input. + * + * This is done in internal (little endian) bit ordering, and + * we convolve it with the leftmost (first) padding bit, so: + * + * Hash: 0b110 + * SHAKE: 0b11111 + */ + +#define DOMAIN_HASH_PAD 0x06 +#define DOMAIN_SHAKE_PAD 0x1f + +static void pad(cf_sha3_context *ctx, uint8_t domain, size_t npad) +{ + assert(npad >= 1); + + cf_blockwise_acc_pad(ctx->partial, &ctx->npartial, ctx->rate, + domain, 0x00, 0x80, + npad, + sha3_block, ctx); +} + +static void pad_and_squeeze(cf_sha3_context *ctx, uint8_t *out, size_t nout) +{ + pad(ctx, DOMAIN_HASH_PAD, ctx->rate - ctx->npartial); + assert(ctx->npartial == 0); + + squeeze(ctx, out, nout); + mem_clean(ctx, sizeof *ctx); +} + +/* SHA3-224 */ +void cf_sha3_224_init(cf_sha3_context *ctx) +{ + sha3_init(ctx, 1152, 448); +} + +void cf_sha3_224_update(cf_sha3_context *ctx, const void *data, size_t nbytes) +{ + sha3_update(ctx, data, nbytes); +} + +void cf_sha3_224_digest(const cf_sha3_context *ctx, uint8_t hash[CF_SHA3_224_HASHSZ]) +{ + cf_sha3_context ours = *ctx; + cf_sha3_224_digest_final(&ours, hash); +} + +void cf_sha3_224_digest_final(cf_sha3_context *ctx, uint8_t hash[CF_SHA3_224_HASHSZ]) +{ + pad_and_squeeze(ctx, hash, CF_SHA3_224_HASHSZ); +} + +const cf_chash cf_sha3_224 = { + .hashsz = CF_SHA3_224_HASHSZ, + .blocksz = CF_SHA3_224_BLOCKSZ, + .init = (cf_chash_init) cf_sha3_224_init, + .update = (cf_chash_update) cf_sha3_224_update, + .digest = (cf_chash_digest) cf_sha3_224_digest +}; + +/* SHA3-256 */ +void cf_sha3_256_init(cf_sha3_context *ctx) +{ + sha3_init(ctx, 1088, 512); +} + +void cf_sha3_256_update(cf_sha3_context *ctx, const void *data, size_t nbytes) +{ + sha3_update(ctx, data, nbytes); +} + +void cf_sha3_256_digest(const cf_sha3_context *ctx, uint8_t hash[CF_SHA3_256_HASHSZ]) +{ + cf_sha3_context ours = *ctx; + cf_sha3_256_digest_final(&ours, hash); +} + +void cf_sha3_256_digest_final(cf_sha3_context *ctx, uint8_t hash[CF_SHA3_256_HASHSZ]) +{ + pad_and_squeeze(ctx, hash, CF_SHA3_256_HASHSZ); +} + +const cf_chash cf_sha3_256 = { + .hashsz = CF_SHA3_256_HASHSZ, + .blocksz = CF_SHA3_256_BLOCKSZ, + .init = (cf_chash_init) cf_sha3_256_init, + .update = (cf_chash_update) cf_sha3_256_update, + .digest = (cf_chash_digest) cf_sha3_256_digest +}; + +/* SHA3-384 */ +void cf_sha3_384_init(cf_sha3_context *ctx) +{ + sha3_init(ctx, 832, 768); +} + +void cf_sha3_384_update(cf_sha3_context *ctx, const void *data, size_t nbytes) +{ + sha3_update(ctx, data, nbytes); +} + +void cf_sha3_384_digest(const cf_sha3_context *ctx, uint8_t hash[CF_SHA3_384_HASHSZ]) +{ + cf_sha3_context ours = *ctx; + cf_sha3_384_digest_final(&ours, hash); +} + +void cf_sha3_384_digest_final(cf_sha3_context *ctx, uint8_t hash[CF_SHA3_384_HASHSZ]) +{ + pad_and_squeeze(ctx, hash, CF_SHA3_384_HASHSZ); +} + +const cf_chash cf_sha3_384 = { + .hashsz = CF_SHA3_384_HASHSZ, + .blocksz = CF_SHA3_384_BLOCKSZ, + .init = (cf_chash_init) cf_sha3_384_init, + .update = (cf_chash_update) cf_sha3_384_update, + .digest = (cf_chash_digest) cf_sha3_384_digest +}; + +/* SHA3-512 */ +void cf_sha3_512_init(cf_sha3_context *ctx) +{ + sha3_init(ctx, 576, 1024); +} + +void cf_sha3_512_update(cf_sha3_context *ctx, const void *data, size_t nbytes) +{ + sha3_update(ctx, data, nbytes); +} + +void cf_sha3_512_digest(const cf_sha3_context *ctx, uint8_t hash[CF_SHA3_512_HASHSZ]) +{ + cf_sha3_context ours = *ctx; + cf_sha3_512_digest_final(&ours, hash); +} + +void cf_sha3_512_digest_final(cf_sha3_context *ctx, uint8_t hash[CF_SHA3_512_HASHSZ]) +{ + pad_and_squeeze(ctx, hash, CF_SHA3_512_HASHSZ); +} + +const cf_chash cf_sha3_512 = { + .hashsz = CF_SHA3_512_HASHSZ, + .blocksz = CF_SHA3_512_BLOCKSZ, + .init = (cf_chash_init) cf_sha3_512_init, + .update = (cf_chash_update) cf_sha3_512_update, + .digest = (cf_chash_digest) cf_sha3_512_digest +}; diff --git a/deps/picoquic/picotls/deps/cifra/src/sha3.h b/deps/picoquic/picotls/deps/cifra/src/sha3.h new file mode 100644 index 0000000..44895dc --- /dev/null +++ b/deps/picoquic/picotls/deps/cifra/src/sha3.h @@ -0,0 +1,180 @@ +/* + * cifra - embedded cryptography library + * Written in 2014 by Joseph Birr-Pixton + * + * To the extent possible under law, the author(s) have dedicated all + * copyright and related and neighboring rights to this software to the + * public domain worldwide. This software is distributed without any + * warranty. + * + * You should have received a copy of the CC0 Public Domain Dedication + * along with this software. If not, see + * . + */ + +#ifndef SHA3_H +#define SHA3_H + +#include +#include + +#include "chash.h" + +/** + * SHA3/Keccak + * =========== + * This implementation is compatible with FIPS 202, + * rather than the original Keccak submission. + * + */ + +/* .. c:macro:: CF_SHA3_224_HASHSZ + * The output size of SHA3-224: 28 bytes. */ +#define CF_SHA3_224_HASHSZ 28 + +/* .. c:macro:: CF_SHA3_256_HASHSZ + * The output size of SHA3-256: 32 bytes. */ +#define CF_SHA3_256_HASHSZ 32 + +/* .. c:macro:: CF_SHA3_384_HASHSZ + * The output size of SHA3-384: 48 bytes. */ +#define CF_SHA3_384_HASHSZ 48 + +/* .. c:macro:: CF_SHA3_512_HASHSZ + * The output size of SHA3-512: 64 bytes. */ +#define CF_SHA3_512_HASHSZ 64 + +/* .. c:macro:: CF_SHA3_224_BLOCKSZ + * The block size of SHA3-224. */ +#define CF_SHA3_224_BLOCKSZ 144 + +/* .. c:macro:: CF_SHA3_256_BLOCKSZ + * The block size of SHA3-256. */ +#define CF_SHA3_256_BLOCKSZ 136 + +/* .. c:macro:: CF_SHA3_384_BLOCKSZ + * The block size of SHA3-384. */ +#define CF_SHA3_384_BLOCKSZ 104 + +/* .. c:macro:: CF_SHA3_512_BLOCKSZ + * The block size of SHA3-512. */ +#define CF_SHA3_512_BLOCKSZ 72 + +/* We use bit-interleaved internal representation. This + * stores a 64 bit quantity in two 32 bit words: one word + * contains odd bits, the other even. This means 64-bit rotations + * are cheaper to compute. */ +typedef struct +{ + uint32_t odd, evn; +} cf_sha3_bi; + +/* .. c:type:: cf_sha3_context + * Incremental SHA3 hashing context. + * + * .. c:member:: cf_sha3_context.A + * Intermediate state. + * + * .. c:member:: cf_sha3_context.partial + * Unprocessed input. + * + * .. c:member:: cf_sha3_context.npartial + * Number of bytes of unprocessed input. + * + * .. c:member:: cf_sha3_context.rate + * Sponge absorption rate. + * + * .. c:member:: cf_sha3_context.rate + * Sponge capacity. + */ +typedef struct +{ + /* State is a 5x5 block of 64-bit values, for Keccak-f[1600]. */ + cf_sha3_bi A[5][5]; + uint8_t partial[CF_SHA3_224_BLOCKSZ]; + size_t npartial; + uint16_t rate, capacity; /* rate and capacity, in bytes. */ +} cf_sha3_context; + + +/* -- _init functions -- */ + +/* .. c:function:: $DECL */ +extern void cf_sha3_224_init(cf_sha3_context *ctx); + +/* .. c:function:: $DECL */ +extern void cf_sha3_256_init(cf_sha3_context *ctx); + +/* .. c:function:: $DECL */ +extern void cf_sha3_384_init(cf_sha3_context *ctx); + +/* .. c:function:: $DECL + * Sets up `ctx` ready to hash a new message. + */ +extern void cf_sha3_512_init(cf_sha3_context *ctx); + +/* -- _update functions -- */ + +/* .. c:function:: $DECL */ +extern void cf_sha3_224_update(cf_sha3_context *ctx, const void *data, size_t nbytes); + +/* .. c:function:: $DECL */ +extern void cf_sha3_256_update(cf_sha3_context *ctx, const void *data, size_t nbytes); + +/* .. c:function:: $DECL */ +extern void cf_sha3_384_update(cf_sha3_context *ctx, const void *data, size_t nbytes); + +/* .. c:function:: $DECL + * Hashes `nbytes` at `data`. Copies the data for processing later if there + * isn't enough to make a full block. + */ +extern void cf_sha3_512_update(cf_sha3_context *ctx, const void *data, size_t nbytes); + +/* -- _digest functions -- */ + +/* .. c:function:: $DECL */ +extern void cf_sha3_224_digest(const cf_sha3_context *ctx, uint8_t hash[CF_SHA3_224_HASHSZ]); + +/* .. c:function:: $DECL */ +extern void cf_sha3_256_digest(const cf_sha3_context *ctx, uint8_t hash[CF_SHA3_256_HASHSZ]); + +/* .. c:function:: $DECL */ +extern void cf_sha3_384_digest(const cf_sha3_context *ctx, uint8_t hash[CF_SHA3_384_HASHSZ]); + +/* .. c:function:: $DECL + * Finishes the hashing operation, writing result to `hash`. + * + * This leaves `ctx` unchanged. + */ +extern void cf_sha3_512_digest(const cf_sha3_context *ctx, uint8_t hash[CF_SHA3_512_HASHSZ]); + +/* -- _digest_final functions -- */ + +/* .. c:function:: $DECL */ +extern void cf_sha3_224_digest_final(cf_sha3_context *ctx, uint8_t hash[CF_SHA3_224_HASHSZ]); + +/* .. c:function:: $DECL */ +extern void cf_sha3_256_digest_final(cf_sha3_context *ctx, uint8_t hash[CF_SHA3_256_HASHSZ]); + +/* .. c:function:: $DECL */ +extern void cf_sha3_384_digest_final(cf_sha3_context *ctx, uint8_t hash[CF_SHA3_384_HASHSZ]); + +/* .. c:function:: $DECL + * Finishes the hashing operation, writing result to `hash`. + * + * This destroys the contents of `ctx`. + */ +extern void cf_sha3_512_digest_final(cf_sha3_context *ctx, uint8_t hash[CF_SHA3_512_HASHSZ]); + +/* .. c:var:: cf_sha3_224 + * .. c:var:: cf_sha3_256 + * .. c:var:: cf_sha3_384 + * .. c:var:: cf_sha3_512 + * Abstract interface to SHA3 functions. See :c:type:`cf_chash` for more information. + */ +extern const cf_chash cf_sha3_224; +extern const cf_chash cf_sha3_256; +extern const cf_chash cf_sha3_384; +extern const cf_chash cf_sha3_512; + +#endif diff --git a/deps/picoquic/picotls/deps/cifra/src/sha512.c b/deps/picoquic/picotls/deps/cifra/src/sha512.c new file mode 100644 index 0000000..cb46b30 --- /dev/null +++ b/deps/picoquic/picotls/deps/cifra/src/sha512.c @@ -0,0 +1,249 @@ +/* + * cifra - embedded cryptography library + * Written in 2014 by Joseph Birr-Pixton + * + * To the extent possible under law, the author(s) have dedicated all + * copyright and related and neighboring rights to this software to the + * public domain worldwide. This software is distributed without any + * warranty. + * + * You should have received a copy of the CC0 Public Domain Dedication + * along with this software. If not, see + * . + */ + +#include + +#include "sha2.h" +#include "blockwise.h" +#include "bitops.h" +#include "handy.h" +#include "tassert.h" + +static const uint64_t K[80] = { + UINT64_C(0x428a2f98d728ae22), UINT64_C(0x7137449123ef65cd), + UINT64_C(0xb5c0fbcfec4d3b2f), UINT64_C(0xe9b5dba58189dbbc), + UINT64_C(0x3956c25bf348b538), UINT64_C(0x59f111f1b605d019), + UINT64_C(0x923f82a4af194f9b), UINT64_C(0xab1c5ed5da6d8118), + UINT64_C(0xd807aa98a3030242), UINT64_C(0x12835b0145706fbe), + UINT64_C(0x243185be4ee4b28c), UINT64_C(0x550c7dc3d5ffb4e2), + UINT64_C(0x72be5d74f27b896f), UINT64_C(0x80deb1fe3b1696b1), + UINT64_C(0x9bdc06a725c71235), UINT64_C(0xc19bf174cf692694), + UINT64_C(0xe49b69c19ef14ad2), UINT64_C(0xefbe4786384f25e3), + UINT64_C(0x0fc19dc68b8cd5b5), UINT64_C(0x240ca1cc77ac9c65), + UINT64_C(0x2de92c6f592b0275), UINT64_C(0x4a7484aa6ea6e483), + UINT64_C(0x5cb0a9dcbd41fbd4), UINT64_C(0x76f988da831153b5), + UINT64_C(0x983e5152ee66dfab), UINT64_C(0xa831c66d2db43210), + UINT64_C(0xb00327c898fb213f), UINT64_C(0xbf597fc7beef0ee4), + UINT64_C(0xc6e00bf33da88fc2), UINT64_C(0xd5a79147930aa725), + UINT64_C(0x06ca6351e003826f), UINT64_C(0x142929670a0e6e70), + UINT64_C(0x27b70a8546d22ffc), UINT64_C(0x2e1b21385c26c926), + UINT64_C(0x4d2c6dfc5ac42aed), UINT64_C(0x53380d139d95b3df), + UINT64_C(0x650a73548baf63de), UINT64_C(0x766a0abb3c77b2a8), + UINT64_C(0x81c2c92e47edaee6), UINT64_C(0x92722c851482353b), + UINT64_C(0xa2bfe8a14cf10364), UINT64_C(0xa81a664bbc423001), + UINT64_C(0xc24b8b70d0f89791), UINT64_C(0xc76c51a30654be30), + UINT64_C(0xd192e819d6ef5218), UINT64_C(0xd69906245565a910), + UINT64_C(0xf40e35855771202a), UINT64_C(0x106aa07032bbd1b8), + UINT64_C(0x19a4c116b8d2d0c8), UINT64_C(0x1e376c085141ab53), + UINT64_C(0x2748774cdf8eeb99), UINT64_C(0x34b0bcb5e19b48a8), + UINT64_C(0x391c0cb3c5c95a63), UINT64_C(0x4ed8aa4ae3418acb), + UINT64_C(0x5b9cca4f7763e373), UINT64_C(0x682e6ff3d6b2b8a3), + UINT64_C(0x748f82ee5defb2fc), UINT64_C(0x78a5636f43172f60), + UINT64_C(0x84c87814a1f0ab72), UINT64_C(0x8cc702081a6439ec), + UINT64_C(0x90befffa23631e28), UINT64_C(0xa4506cebde82bde9), + UINT64_C(0xbef9a3f7b2c67915), UINT64_C(0xc67178f2e372532b), + UINT64_C(0xca273eceea26619c), UINT64_C(0xd186b8c721c0c207), + UINT64_C(0xeada7dd6cde0eb1e), UINT64_C(0xf57d4f7fee6ed178), + UINT64_C(0x06f067aa72176fba), UINT64_C(0x0a637dc5a2c898a6), + UINT64_C(0x113f9804bef90dae), UINT64_C(0x1b710b35131c471b), + UINT64_C(0x28db77f523047d84), UINT64_C(0x32caab7b40c72493), + UINT64_C(0x3c9ebe0a15c9bebc), UINT64_C(0x431d67c49c100d4c), + UINT64_C(0x4cc5d4becb3e42b6), UINT64_C(0x597f299cfc657e2a), + UINT64_C(0x5fcb6fab3ad6faec), UINT64_C(0x6c44198c4a475817) +}; + +# define CH(x, y, z) (((x) & (y)) ^ (~(x) & (z))) +# define MAJ(x, y, z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z))) +# define BSIG0(x) (rotr64((x), 28) ^ rotr64((x), 34) ^ rotr64((x), 39)) +# define BSIG1(x) (rotr64((x), 14) ^ rotr64((x), 18) ^ rotr64((x), 41)) +# define SSIG0(x) (rotr64((x), 1) ^ rotr64((x), 8) ^ ((x) >> 7)) +# define SSIG1(x) (rotr64((x), 19) ^ rotr64((x), 61) ^ ((x) >> 6)) + +void cf_sha512_init(cf_sha512_context *ctx) +{ + memset(ctx, 0, sizeof *ctx); + ctx->H[0] = UINT64_C(0x6a09e667f3bcc908); + ctx->H[1] = UINT64_C(0xbb67ae8584caa73b); + ctx->H[2] = UINT64_C(0x3c6ef372fe94f82b); + ctx->H[3] = UINT64_C(0xa54ff53a5f1d36f1); + ctx->H[4] = UINT64_C(0x510e527fade682d1); + ctx->H[5] = UINT64_C(0x9b05688c2b3e6c1f); + ctx->H[6] = UINT64_C(0x1f83d9abfb41bd6b); + ctx->H[7] = UINT64_C(0x5be0cd19137e2179); +} + +void cf_sha384_init(cf_sha512_context *ctx) +{ + memset(ctx, 0, sizeof *ctx); + ctx->H[0] = UINT64_C(0xcbbb9d5dc1059ed8); + ctx->H[1] = UINT64_C(0x629a292a367cd507); + ctx->H[2] = UINT64_C(0x9159015a3070dd17); + ctx->H[3] = UINT64_C(0x152fecd8f70e5939); + ctx->H[4] = UINT64_C(0x67332667ffc00b31); + ctx->H[5] = UINT64_C(0x8eb44a8768581511); + ctx->H[6] = UINT64_C(0xdb0c2e0d64f98fa7); + ctx->H[7] = UINT64_C(0x47b5481dbefa4fa4); +} + +static void sha512_update_block(void *vctx, const uint8_t *inp) +{ + cf_sha512_context *ctx = vctx; + + uint64_t W[16]; + + uint64_t a = ctx->H[0], + b = ctx->H[1], + c = ctx->H[2], + d = ctx->H[3], + e = ctx->H[4], + f = ctx->H[5], + g = ctx->H[6], + h = ctx->H[7], + Wt; + + size_t t; + for (t = 0; t < 80; t++) + { + if (t < 16) + { + W[t] = Wt = read64_be(inp); + inp += 8; + } else { + Wt = SSIG1(W[(t - 2) % 16]) + + W[(t - 7) % 16] + + SSIG0(W[(t - 15) % 16]) + + W[(t - 16) % 16]; + W[t % 16] = Wt; + } + + uint64_t T1 = h + BSIG1(e) + CH(e, f, g) + K[t] + Wt; + uint64_t T2 = BSIG0(a) + MAJ(a, b, c); + h = g; + g = f; + f = e; + e = d + T1; + d = c; + c = b; + b = a; + a = T1 + T2; + } + + ctx->H[0] += a; + ctx->H[1] += b; + ctx->H[2] += c; + ctx->H[3] += d; + ctx->H[4] += e; + ctx->H[5] += f; + ctx->H[6] += g; + ctx->H[7] += h; + + ctx->blocks++; +} + +void cf_sha512_update(cf_sha512_context *ctx, const void *data, size_t nbytes) +{ + cf_blockwise_accumulate(ctx->partial, &ctx->npartial, sizeof ctx->partial, + data, nbytes, + sha512_update_block, ctx); +} + +void cf_sha384_update(cf_sha512_context *ctx, const void *data, size_t nbytes) +{ + cf_sha512_update(ctx, data, nbytes); +} + +void cf_sha512_digest(const cf_sha512_context *ctx, uint8_t hash[CF_SHA512_HASHSZ]) +{ + /* We copy the context, so the finalisation doesn't effect the caller's + * context. This means the caller can do: + * + * x = init() + * x.update('hello') + * h1 = x.digest() + * x.update(' world') + * h2 = x.digest() + * + * to get h1 = H('hello') and h2 = H('hello world') + * + * This wouldn't work if we applied MD-padding to *ctx. + */ + + cf_sha512_context ours = *ctx; + cf_sha512_digest_final(&ours, hash); +} + +void cf_sha512_digest_final(cf_sha512_context *ctx, uint8_t hash[CF_SHA512_HASHSZ]) +{ + uint64_t digested_bytes = ctx->blocks; + digested_bytes = digested_bytes * CF_SHA512_BLOCKSZ + ctx->npartial; + uint64_t digested_bits = digested_bytes * 8; + + size_t padbytes = CF_SHA512_BLOCKSZ - ((digested_bytes + 16) % CF_SHA512_BLOCKSZ); + + /* Hash 0x80 00 ... block first. */ + cf_blockwise_acc_pad(ctx->partial, &ctx->npartial, sizeof ctx->partial, + 0x80, 0x00, 0x00, padbytes, + sha512_update_block, ctx); + + /* Now hash length (this is 128 bits long). */ + uint8_t buf[8]; + write64_be(0, buf); + cf_sha512_update(ctx, buf, 8); + write64_be(digested_bits, buf); + cf_sha512_update(ctx, buf, 8); + + /* We ought to have got our padding calculation right! */ + assert(ctx->npartial == 0); + + write64_be(ctx->H[0], hash + 0); + write64_be(ctx->H[1], hash + 8); + write64_be(ctx->H[2], hash + 16); + write64_be(ctx->H[3], hash + 24); + write64_be(ctx->H[4], hash + 32); + write64_be(ctx->H[5], hash + 40); + write64_be(ctx->H[6], hash + 48); + write64_be(ctx->H[7], hash + 56); + memset(ctx, 0, sizeof *ctx); +} + +void cf_sha384_digest(const cf_sha512_context *ctx, uint8_t hash[CF_SHA384_HASHSZ]) +{ + uint8_t full[CF_SHA512_HASHSZ]; + cf_sha512_digest(ctx, full); + memcpy(hash, full, CF_SHA384_HASHSZ); +} + +void cf_sha384_digest_final(cf_sha512_context *ctx, uint8_t hash[CF_SHA384_HASHSZ]) +{ + uint8_t full[CF_SHA512_HASHSZ]; + cf_sha512_digest_final(ctx, full); + memcpy(hash, full, CF_SHA384_HASHSZ); +} + +const cf_chash cf_sha384 = { + .hashsz = CF_SHA384_HASHSZ, + .blocksz = CF_SHA384_BLOCKSZ, + .init = (cf_chash_init) cf_sha384_init, + .update = (cf_chash_update) cf_sha384_update, + .digest = (cf_chash_digest) cf_sha384_digest +}; + +const cf_chash cf_sha512 = { + .hashsz = CF_SHA512_HASHSZ, + .blocksz = CF_SHA512_BLOCKSZ, + .init = (cf_chash_init) cf_sha512_init, + .update = (cf_chash_update) cf_sha512_update, + .digest = (cf_chash_digest) cf_sha512_digest +}; + diff --git a/deps/picoquic/picotls/deps/cifra/src/tassert.h b/deps/picoquic/picotls/deps/cifra/src/tassert.h new file mode 100644 index 0000000..58ebb4c --- /dev/null +++ b/deps/picoquic/picotls/deps/cifra/src/tassert.h @@ -0,0 +1,32 @@ +/* + * cifra - embedded cryptography library + * Written in 2014 by Joseph Birr-Pixton + * + * To the extent possible under law, the author(s) have dedicated all + * copyright and related and neighboring rights to this software to the + * public domain worldwide. This software is distributed without any + * warranty. + * + * You should have received a copy of the CC0 Public Domain Dedication + * along with this software. If not, see + * . + */ + +#ifndef TASSERT_H +#define TASSERT_H + +/* Tiny assert + * ----------- + * + * This is an assert(3) definition which doesn't include any + * strings, but just branches to abort(3) on failure. + */ + +#ifndef FULL_FAT_ASSERT +# include +# define assert(expr) do { if (!(expr)) abort(); } while (0) +#else +# include +#endif + +#endif diff --git a/deps/picoquic/picotls/deps/cifra/src/testaes.c b/deps/picoquic/picotls/deps/cifra/src/testaes.c new file mode 100644 index 0000000..12543bb --- /dev/null +++ b/deps/picoquic/picotls/deps/cifra/src/testaes.c @@ -0,0 +1,258 @@ +/* + * cifra - embedded cryptography library + * Written in 2014 by Joseph Birr-Pixton + * + * To the extent possible under law, the author(s) have dedicated all + * copyright and related and neighboring rights to this software to the + * public domain worldwide. This software is distributed without any + * warranty. + * + * You should have received a copy of the CC0 Public Domain Dedication + * along with this software. If not, see + * . + */ + +#include "aes.h" +#include "modes.h" +#include "bitops.h" +#include "gf128.h" + +#include "handy.h" +#include "cutest.h" +#include "testutil.h" + +static void test_memclean(void) +{ + uint8_t buf[32], buf2[32]; + memset(buf, 0xff, sizeof buf); + mem_clean(buf + 1, sizeof buf - 2); + TEST_CHECK(buf[0] == 0xff); + TEST_CHECK(buf[1] == 0x00); + TEST_CHECK(buf[16] == 0x00); + TEST_CHECK(buf[30] == 0x00); + TEST_CHECK(buf[31] == 0xff); + + memcpy(buf2, buf, sizeof buf); + TEST_CHECK(buf2[0] == 0xff); + TEST_CHECK(buf2[1] == 0x00); + TEST_CHECK(buf2[16] == 0x00); + TEST_CHECK(buf2[30] == 0x00); + TEST_CHECK(buf2[31] == 0xff); + + memset(buf2, 0xee, sizeof buf); + TEST_CHECK(buf2[0] == 0xee); + TEST_CHECK(buf2[1] == 0xee); + TEST_CHECK(buf2[2] == 0xee); + TEST_CHECK(buf2[3] == 0xee); + TEST_CHECK(buf2[30] == 0xee); + TEST_CHECK(buf2[31] == 0xee); +} + +static void test_bitops_select(void) +{ + uint8_t tab8[8]; + uint32_t tab32[32]; + + for (size_t i = 0; i < 8; i++) + tab8[i] = 1 << i; + for (size_t i = 0; i < 32; i++) + tab32[i] = 1 << i; + + for (size_t i = 0; i < 8; i++) + { + TEST_CHECK(select_u8(i, tab8, 8) == tab8[i]); + } + + for (size_t i = 0; i < 32; i++) + { + TEST_CHECK(select_u32(i, tab32, 32) == tab32[i]); + } +} + +static void test_bitops_incr(void) +{ + uint8_t buf[4]; + +#define CHECK_BE(start, add, end) \ + { \ + write32_be((start), buf); \ + for (size_t i = 0; i < (add); i++) \ + incr_be(buf, sizeof buf); \ + TEST_CHECK(read32_be(buf) == (end)); \ + } + +#define CHECK_LE(start, add, end) \ + { \ + write32_le((start), buf); \ + for (size_t i = 0; i < (add); i++) \ + incr_le(buf, sizeof buf); \ + TEST_CHECK(read32_le(buf) == (end)); \ + } + + CHECK_BE(0, 1, 1); + CHECK_BE(0, 256, 256); + CHECK_BE(256, 256, 512); + CHECK_BE(0xffffffff, 1, 0); + + CHECK_LE(0, 1, 1); + CHECK_LE(0, 256, 256); + CHECK_LE(0x7fffffff, 1, 0x80000000); + CHECK_LE(0xffffffff, 1, 0); + +#undef CHECK_BE +#undef CHECK_LE +} + +static void test_bitops_unaligned(void) +{ + uint8_t in[4], out[4]; + +#define CHECK(outw, len, offs) \ + { \ + memset(out, 0, sizeof out); \ + copy_bytes_unaligned(out, in, len, offs); \ + TEST_CHECK(read32_be(out) == (outw)); \ + } + + write32_be(0x11223344, in); + + CHECK(0x11223344, 4, 0); + CHECK(0x22446600, 3, 1); + CHECK(0x4488cd00, 3, 2); + CHECK(0x89119a00, 3, 3); + CHECK(0x12233400, 3, 4); + CHECK(0x24466800, 3, 5); + CHECK(0x488cd100, 3, 6); + CHECK(0x9119a200, 3, 7); + CHECK(0x22334400, 3, 8); + CHECK(0x44660000, 2, 9); + CHECK(0x33440000, 2, 16); + +#undef CHECK +} + +static void test_expand(const uint8_t *key, size_t nkey, + const uint32_t *answer, size_t roundkeys) +{ + cf_aes_context ctx; + + cf_aes_init(&ctx, key, nkey); + + for (size_t i = 0; i < roundkeys; i++) + { + TEST_CHECK(ctx.ks[i] == answer[i]); + } +} + +static void test_expand_128(void) +{ + /* This is FIPS-197 appendix A.1. */ + const uint8_t key[] = { + 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, + 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c + }; + + const uint32_t answer[] = { + 0x2b7e1516, 0x28aed2a6, 0xabf71588, 0x09cf4f3c, 0xa0fafe17, 0x88542cb1, + 0x23a33939, 0x2a6c7605, 0xf2c295f2, 0x7a96b943, 0x5935807a, 0x7359f67f, + 0x3d80477d, 0x4716fe3e, 0x1e237e44, 0x6d7a883b, 0xef44a541, 0xa8525b7f, + 0xb671253b, 0xdb0bad00, 0xd4d1c6f8, 0x7c839d87, 0xcaf2b8bc, 0x11f915bc, + 0x6d88a37a, 0x110b3efd, 0xdbf98641, 0xca0093fd, 0x4e54f70e, 0x5f5fc9f3, + 0x84a64fb2, 0x4ea6dc4f, 0xead27321, 0xb58dbad2, 0x312bf560, 0x7f8d292f, + 0xac7766f3, 0x19fadc21, 0x28d12941, 0x575c006e, 0xd014f9a8, 0xc9ee2589, + 0xe13f0cc8, 0xb6630ca6 + }; + + test_expand(key, sizeof key, answer, ARRAYCOUNT(answer)); +} + +static void test_expand_192(void) +{ + /* This is FIPS-197 appendix A.2. */ + const uint8_t key[] = { + 0x8e, 0x73, 0xb0, 0xf7, 0xda, 0x0e, 0x64, 0x52, 0xc8, 0x10, 0xf3, 0x2b, + 0x80, 0x90, 0x79, 0xe5, 0x62, 0xf8, 0xea, 0xd2, 0x52, 0x2c, 0x6b, 0x7b + }; + + const uint32_t answer[] = { + 0x8e73b0f7, 0xda0e6452, 0xc810f32b, 0x809079e5, 0x62f8ead2, 0x522c6b7b, + 0xfe0c91f7, 0x2402f5a5, 0xec12068e, 0x6c827f6b, 0x0e7a95b9, 0x5c56fec2, + 0x4db7b4bd, 0x69b54118, 0x85a74796, 0xe92538fd, 0xe75fad44, 0xbb095386, + 0x485af057, 0x21efb14f, 0xa448f6d9, 0x4d6dce24, 0xaa326360, 0x113b30e6, + 0xa25e7ed5, 0x83b1cf9a, 0x27f93943, 0x6a94f767, 0xc0a69407, 0xd19da4e1, + 0xec1786eb, 0x6fa64971, 0x485f7032, 0x22cb8755, 0xe26d1352, 0x33f0b7b3, + 0x40beeb28, 0x2f18a259, 0x6747d26b, 0x458c553e, 0xa7e1466c, 0x9411f1df, + 0x821f750a, 0xad07d753, 0xca400538, 0x8fcc5006, 0x282d166a, 0xbc3ce7b5, + 0xe98ba06f, 0x448c773c, 0x8ecc7204, 0x01002202 + }; + + test_expand(key, sizeof key, answer, ARRAYCOUNT(answer)); +} + +static void test_expand_256(void) +{ + /* And this is A.3. */ + const uint8_t key[] = { + 0x60, 0x3d, 0xeb, 0x10, 0x15, 0xca, 0x71, 0xbe, 0x2b, 0x73, 0xae, 0xf0, + 0x85, 0x7d, 0x77, 0x81, 0x1f, 0x35, 0x2c, 0x07, 0x3b, 0x61, 0x08, 0xd7, + 0x2d, 0x98, 0x10, 0xa3, 0x09, 0x14, 0xdf, 0xf4 + }; + + const uint32_t answer[] = { + 0x603deb10, 0x15ca71be, 0x2b73aef0, 0x857d7781, 0x1f352c07, 0x3b6108d7, + 0x2d9810a3, 0x0914dff4, 0x9ba35411, 0x8e6925af, 0xa51a8b5f, 0x2067fcde, + 0xa8b09c1a, 0x93d194cd, 0xbe49846e, 0xb75d5b9a, 0xd59aecb8, 0x5bf3c917, + 0xfee94248, 0xde8ebe96, 0xb5a9328a, 0x2678a647, 0x98312229, 0x2f6c79b3, + 0x812c81ad, 0xdadf48ba, 0x24360af2, 0xfab8b464, 0x98c5bfc9, 0xbebd198e, + 0x268c3ba7, 0x09e04214, 0x68007bac, 0xb2df3316, 0x96e939e4, 0x6c518d80, + 0xc814e204, 0x76a9fb8a, 0x5025c02d, 0x59c58239, 0xde136967, 0x6ccc5a71, + 0xfa256395, 0x9674ee15, 0x5886ca5d, 0x2e2f31d7, 0x7e0af1fa, 0x27cf73c3, + 0x749c47ab, 0x18501dda, 0xe2757e4f, 0x7401905a, 0xcafaaae3, 0xe4d59b34, + 0x9adf6ace, 0xbd10190d, 0xfe4890d1, 0xe6188d0b, 0x046df344, 0x706c631e + }; + + test_expand(key, sizeof key, answer, ARRAYCOUNT(answer)); +} + +static void vector(const char *input, const char *output, + const char *key) +{ + uint8_t keybuf[32], inbuf[16], outbuf[16], tmp[16]; + size_t nkey = sizeof keybuf; + cf_aes_context ctx; + + nkey = unhex(keybuf, 32, key); + unhex(inbuf, 16, input); + unhex(outbuf, 16, output); + + cf_aes_init(&ctx, keybuf, nkey); + cf_aes_encrypt(&ctx, inbuf, tmp); + TEST_CHECK(memcmp(tmp, outbuf, 16) == 0); + + cf_aes_decrypt(&ctx, outbuf, tmp); + TEST_CHECK(memcmp(tmp, inbuf, 16) == 0); + cf_aes_finish(&ctx); +} + +static void test_vectors(void) +{ + vector("00112233445566778899aabbccddeeff", "69c4e0d86a7b0430d8cdb78070b4c55a", + "000102030405060708090a0b0c0d0e0f"); + vector("00112233445566778899aabbccddeeff", "dda97ca4864cdfe06eaf70a0ec0d7191", + "000102030405060708090a0b0c0d0e0f1011121314151617"); + vector("00112233445566778899aabbccddeeff", "8ea2b7ca516745bfeafc49904b496089", + "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f"); +} + +TEST_LIST = { + { "handy-memclean", test_memclean }, + { "bitops-select", test_bitops_select }, + { "bitops-incr", test_bitops_incr }, + { "bitops-unaligned", test_bitops_unaligned }, + { "key-expansion-128", test_expand_128 }, + { "key-expansion-192", test_expand_192 }, + { "key-expansion-256", test_expand_256 }, + { "vectors", test_vectors }, + { 0 } +}; + diff --git a/deps/picoquic/picotls/deps/cifra/src/testchacha20poly1305.c b/deps/picoquic/picotls/deps/cifra/src/testchacha20poly1305.c new file mode 100644 index 0000000..f1b53e6 --- /dev/null +++ b/deps/picoquic/picotls/deps/cifra/src/testchacha20poly1305.c @@ -0,0 +1,91 @@ +/* + * cifra - embedded cryptography library + * Written in 2014 by Joseph Birr-Pixton + * + * To the extent possible under law, the author(s) have dedicated all + * copyright and related and neighboring rights to this software to the + * public domain worldwide. This software is distributed without any + * warranty. + * + * You should have received a copy of the CC0 Public Domain Dedication + * along with this software. If not, see + * . + */ + +#include "chacha20poly1305.h" +#include "handy.h" +#include "cutest.h" +#include "testutil.h" + +static void vector(const char *keystr, + const char *noncestr, + const char *headerstr, + const char *plainstr, + const char *cipherstr, + const char *tagstr) +{ + /* inputs */ + uint8_t K[32], N[12], H[12], A[16]; + uint8_t C[265], P[265]; + + unhex(K, sizeof K, keystr); + unhex(N, sizeof N, noncestr); + size_t headerlen = unhex(H, sizeof H, headerstr); + size_t plainlen = unhex(P, sizeof P, plainstr); + size_t cipherlen = unhex(C, sizeof C, cipherstr); + unhex(A, sizeof A, tagstr); + + assert(cipherlen == plainlen); + + /* working data */ + uint8_t out[265], ourtag[16]; + + /* check encryption works. */ + cf_chacha20poly1305_encrypt(K, N, + H, headerlen, + P, plainlen, + out, ourtag); + + TEST_CHECK(memcmp(out, C, cipherlen) == 0); + TEST_CHECK(memcmp(ourtag, A, sizeof A) == 0); + + /* proper decryption */ + TEST_CHECK(0 == cf_chacha20poly1305_decrypt(K, N, + H, headerlen, + C, cipherlen, + A, out)); + TEST_CHECK(0 == memcmp(out, P, plainlen)); + + /* failure decryption */ + C[0] ^= 0xff; + + TEST_CHECK(1 == cf_chacha20poly1305_decrypt(K, N, + H, headerlen, + C, cipherlen, + A, out)); +} + +static void test_vectors(void) +{ + /* Test vector from section 2.8.2. */ + vector("808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f", + "070000004041424344454647", + "50515253c0c1c2c3c4c5c6c7", + "4c616469657320616e642047656e746c656d656e206f662074686520636c617373206f66202739393a204966204920636f756c64206f6666657220796f75206f6e6c79206f6e652074697020666f7220746865206675747572652c2073756e73637265656e20776f756c642062652069742e", + "d31a8d34648e60db7b86afbc53ef7ec2a4aded51296e08fea9e2b5a736ee62d63dbea45e8ca9671282fafb69da92728b1a71de0a9e060b2905d6a5b67ecd3b3692ddbd7f2d778b8c9803aee328091b58fab324e4fad675945585808b4831d7bc3ff4def08e4b7a9de576d26586cec64b6116", + "1ae10b594f09e26a7e902ecbd0600691"); + + /* Test vector from A.5. */ + vector("1c9240a5eb55d38af333888604f6b5f0473917c1402b80099dca5cbc207075c0", + "000000000102030405060708", + "f33388860000000000004e91", + "496e7465726e65742d4472616674732061726520647261667420646f63756d656e74732076616c696420666f722061206d6178696d756d206f6620736978206d6f6e74687320616e64206d617920626520757064617465642c207265706c616365642c206f72206f62736f6c65746564206279206f7468657220646f63756d656e747320617420616e792074696d652e20497420697320696e617070726f70726961746520746f2075736520496e7465726e65742d447261667473206173207265666572656e6365206d6174657269616c206f7220746f2063697465207468656d206f74686572207468616e206173202fe2809c776f726b20696e2070726f67726573732e2fe2809d", + "64a0861575861af460f062c79be643bd5e805cfd345cf389f108670ac76c8cb24c6cfc18755d43eea09ee94e382d26b0bdb7b73c321b0100d4f03b7f355894cf332f830e710b97ce98c8a84abd0b948114ad176e008d33bd60f982b1ff37c8559797a06ef4f0ef61c186324e2b3506383606907b6a7c02b0f9f6157b53c867e4b9166c767b804d46a59b5216cde7a4e99040c5a40433225ee282a1b0a06c523eaf4534d7f83fa1155b0047718cbc546a0d072b04b3564eea1b422273f548271a0bb2316053fa76991955ebd63159434ecebb4e466dae5a1073a6727627097a1049e617d91d361094fa68f0ff77987130305beaba2eda04df997b714d6c6f2c29a6ad5cb4022b02709b", + "eead9d67890cbb22392336fea1851f38"); +} + +TEST_LIST = { + { "vectors", test_vectors }, + { 0 } +}; + diff --git a/deps/picoquic/picotls/deps/cifra/src/testcurve25519.c b/deps/picoquic/picotls/deps/cifra/src/testcurve25519.c new file mode 100644 index 0000000..a0ab8db --- /dev/null +++ b/deps/picoquic/picotls/deps/cifra/src/testcurve25519.c @@ -0,0 +1,56 @@ +/* + * cifra - embedded cryptography library + * Written in 2014 by Joseph Birr-Pixton + * + * To the extent possible under law, the author(s) have dedicated all + * copyright and related and neighboring rights to this software to the + * public domain worldwide. This software is distributed without any + * warranty. + * + * You should have received a copy of the CC0 Public Domain Dedication + * along with this software. If not, see + * . + */ + +#include "curve25519.h" +#include "handy.h" +#include "cutest.h" +#include "testutil.h" + +static void test_base_mul(void) +{ + uint8_t secret[32]; + uint8_t public[32]; + uint8_t expect[32]; + + unhex(secret, 32, "77076d0a7318a57d3c16c17251b26645df4c2f87ebc0992ab177fba51db92c2a"); + unhex(expect, 32, "8520f0098930a754748b7ddcb43ef75a0dbf3a0d26381af4eba4a98eaa9b4e6a"); + cf_curve25519_mul_base(public, secret); + TEST_CHECK(memcmp(expect, public, 32) == 0); + + unhex(secret, 32, "5dab087e624a8a4b79e17f8b83800ee66f3bb1292618b6fd1c2f8b27ff88e0eb"); + unhex(expect, 32, "de9edb7d7b7dc1b4d35b61c2ece435373f8343c85b78674dadfc7e146f882b4f"); + cf_curve25519_mul_base(public, secret); + TEST_CHECK(memcmp(expect, public, 32) == 0); +} + +static void test_mul(void) +{ + uint8_t scalar[32]; + uint8_t public[32]; + uint8_t shared[32]; + uint8_t expect[32]; + + unhex(scalar, 32, "77076d0a7318a57d3c16c17251b26645df4c2f87ebc0992ab177fba51db92c2a"); + unhex(public, 32, "de9edb7d7b7dc1b4d35b61c2ece435373f8343c85b78674dadfc7e146f882b4f"); + unhex(expect, 32, "4a5d9d5ba4ce2de1728e3bf480350f25e07e21c947d19e3376f09b3c1e161742"); + cf_curve25519_mul(shared, scalar, public); + TEST_CHECK(memcmp(expect, shared, 32) == 0); +} + +TEST_LIST = { + { "base-mul", test_base_mul }, + { "mul", test_mul }, + { 0 } +}; + diff --git a/deps/picoquic/picotls/deps/cifra/src/testdrbg.c b/deps/picoquic/picotls/deps/cifra/src/testdrbg.c new file mode 100644 index 0000000..253c8c0 --- /dev/null +++ b/deps/picoquic/picotls/deps/cifra/src/testdrbg.c @@ -0,0 +1,206 @@ +/* + * cifra - embedded cryptography library + * Written in 2016 by Joseph Birr-Pixton + * + * To the extent possible under law, the author(s) have dedicated all + * copyright and related and neighboring rights to this software to the + * public domain worldwide. This software is distributed without any + * warranty. + * + * You should have received a copy of the CC0 Public Domain Dedication + * along with this software. If not, see + * . + */ + +#include "drbg.h" +#include "sha1.h" +#include "sha2.h" + +#include "handy.h" +#include "cutest.h" +#include "testutil.h" + +static void test_hashdrbg_sha256_vector(void) +{ + uint8_t entropy[32], nonce[16], persn[32], reseed[32], got[128], expect[128]; + + /* This is the first KAT from NIST's CAVP example + * file for SHA-256 with all inputs used; line 4360. */ + unhex(entropy, sizeof entropy, "b87bb4de5c148d964fc0cb612d69295671780b4270fe32bf389b6f49488efe13"); + unhex(nonce, sizeof nonce, "27eb37a0c695c4ee3c9b70b7f6b33492"); + unhex(persn, sizeof persn, "52321406ac8a9c266b1f8d811bb871269e5824b59a0234f01d358193523bbb7c"); + unhex(reseed, sizeof reseed, "7638267f534c4e6ee22cc6ca6ed824fd5d3d387c00b89dd791eb5ac9766385b8"); + + unhex(expect, sizeof expect, "de01c061651bab3cef2fc4ea89a56b6e86e74b2e9fd11ed671c97c813778a06a2c1f41b41e754a5257750c6bde9601da9d67d8d9564f4a8538b92516a2dacc496dee257b85393f2a01ad59aa3257f1b6da9566e3706d2d6d4a26e511b0c64d7dc223acb24827178afa43ca8d5a66f983d6929dc61564c4c14fc32d85765a23f7"); + + cf_hash_drbg_sha256 ctx; + cf_hash_drbg_sha256_init(&ctx, entropy, sizeof entropy, nonce, sizeof nonce, persn, sizeof persn); + cf_hash_drbg_sha256_reseed(&ctx, reseed, sizeof reseed, NULL, 0); + cf_hash_drbg_sha256_gen(&ctx, got, sizeof got); + cf_hash_drbg_sha256_gen(&ctx, got, sizeof got); + TEST_CHECK(memcmp(got, expect, sizeof got) == 0); + + /* This is line 5064 from Hash_DRBG.rsp */ + unhex(entropy, sizeof entropy, "63363377e41e86468deb0ab4a8ed683f6a134e47e014c700454e81e95358a569"); + unhex(nonce, sizeof nonce, "808aa38f2a72a62359915a9f8a04ca68"); + /* no persn */ + unhex(reseed, sizeof reseed, "e62b8a8ee8f141b6980566e3bfe3c04903dad4ac2cdf9f2280010a6739bc83d3"); + unhex(expect, sizeof expect, "04eec63bb231df2c630a1afbe724949d005a587851e1aa795e477347c8b056621c18bddcdd8d99fc5fc2b92053d8cfacfb0bb8831205fad1ddd6c071318a6018f03b73f5ede4d4d071f9de03fd7aea105d9299b8af99aa075bdb4db9aa28c18d174b56ee2a014d098896ff2282c955a81969e069fa8ce007a180183a07dfae17"); + + cf_hash_drbg_sha256_init(&ctx, entropy, sizeof entropy, nonce, sizeof nonce, NULL, 0); + cf_hash_drbg_sha256_reseed(&ctx, reseed, sizeof reseed, NULL, 0); + cf_hash_drbg_sha256_gen(&ctx, got, sizeof got); + cf_hash_drbg_sha256_gen(&ctx, got, sizeof got); + TEST_CHECK(memcmp(got, expect, sizeof got) == 0); +} + +static void test_hashdrbg_sha256_vector_addnl(void) +{ + uint8_t entropy[32], nonce[16], reseed[32], got[128], expect[128], addnl[32]; + + /* Hash_DRBG.rsp, line 5230. No personlisation string, but with additional data. */ + unhex(entropy, sizeof entropy, "9cfb7ad03be487a3b42be06e9ae44f283c2b1458cec801da2ae6532fcb56cc4c"); + unhex(nonce, sizeof nonce, "a20765538e8db31295747ec922c13a69"); + unhex(reseed, sizeof reseed, "96bc8014f90ebdf690db0e171b59cc46c75e2e9b8e1dc699c65c03ceb2f4d7dc"); + unhex(expect, sizeof expect, "71c1154a2a7a3552413970bf698aa02f14f8ea95e861f801f463be27868b1b14b1b4babd9eba5915a6414ab1104c8979b1918f3094925aeab0d07d2037e613b63cbd4f79d9f95c84b47ed9b77230a57515c211f48f4af6f5edb2c308b33905db308cf88f552c8912c49b34e66c026e67b302ca65b187928a1aba9a49edbfe190"); + + cf_hash_drbg_sha256 ctx; + cf_hash_drbg_sha256_init(&ctx, entropy, sizeof entropy, nonce, sizeof nonce, NULL, 0); + unhex(addnl, sizeof addnl, "6fea0894052dab3c44d503950c7c72bd7b87de87cb81d3bb51c32a62f742286d"); + cf_hash_drbg_sha256_reseed(&ctx, reseed, sizeof reseed, addnl, sizeof addnl); + unhex(addnl, sizeof addnl, "d3467c78563b74c13db7af36c2a964820f2a9b1b167474906508fdac9b2049a6"); + cf_hash_drbg_sha256_gen_additional(&ctx, addnl, sizeof addnl, got, sizeof got); + unhex(addnl, sizeof addnl, "5840a11cc9ebf77b963854726a826370ffdb2fc2b3d8479e1df5dcfa3dddd10b"); + cf_hash_drbg_sha256_gen_additional(&ctx, addnl, sizeof addnl, got, sizeof got); + TEST_CHECK(memcmp(got, expect, sizeof got) == 0); +} + +static void test_hmacdrbg_sha1_vector(void) +{ + uint8_t entropy[16], nonce[8], reseed[16], got[80], expect[80]; + + /* HMAC_DRBG.rsp, line 8. */ + unhex(entropy, sizeof entropy, "79349bbf7cdda5799557866621c91383"); + unhex(nonce, sizeof nonce, "1146733abf8c35c8"); + unhex(reseed, sizeof reseed, "c7215b5b96c48e9b338c74e3e99dfedf"); + unhex(expect, sizeof expect, "c6a16ab8d420706f0f34ab7fec5adca9d8ca3a133e159ca6ac43c6f8a2be22834a4c0a0affb10d7194f1c1a5cf7322ec1ae0964ed4bf122746e087fdb5b3e91b3493d5bb98faed49e85f130fc8a459b7"); + + cf_hmac_drbg ctx; + cf_hmac_drbg_init(&ctx, &cf_sha1, entropy, sizeof entropy, nonce, sizeof nonce, NULL, 0); + cf_hmac_drbg_reseed(&ctx, reseed, sizeof reseed, NULL, 0); + cf_hmac_drbg_gen(&ctx, got, sizeof got); + cf_hmac_drbg_gen(&ctx, got, sizeof got); + TEST_CHECK(memcmp(got, expect, sizeof got) == 0); +} + +static void test_hmacdrbg_sha1_vector_addnl(void) +{ + uint8_t entropy[16], nonce[8], reseed[16], got[80], expect[80], addnl[16]; + + /* HMAC_DRBG.rsp, line 174. */ + unhex(entropy, sizeof entropy, "7d7052a776fd2fb3d7191f733304ee8b"); + unhex(nonce, sizeof nonce, "be4a0ceedca80207"); + unhex(reseed, sizeof reseed, "49047e879d610955eed916e4060e00c9"); + unhex(expect, sizeof expect, "a736343844fc92511391db0addd9064dbee24c8976aa259a9e3b6368aa6de4c9bf3a0effcda9cb0e9dc33652ab58ecb7650ed80467f76a849fb1cfc1ed0a09f7155086064db324b1e124f3fc9e614fcb"); + + cf_hmac_drbg ctx; + cf_hmac_drbg_init(&ctx, &cf_sha1, entropy, sizeof entropy, nonce, sizeof nonce, NULL, 0); + unhex(addnl, sizeof addnl, "fd8bb33aab2f6cdfbc541811861d518d"); + cf_hmac_drbg_reseed(&ctx, reseed, sizeof reseed, addnl, sizeof addnl); + unhex(addnl, sizeof addnl, "99afe347540461ddf6abeb491e0715b4"); + cf_hmac_drbg_gen_additional(&ctx, addnl, sizeof addnl, got, sizeof got); + unhex(addnl, sizeof addnl, "02f773482dd7ae66f76e381598a64ef0"); + cf_hmac_drbg_gen_additional(&ctx, addnl, sizeof addnl, got, sizeof got); + TEST_CHECK(memcmp(got, expect, sizeof got) == 0); +} + +static void test_hmacdrbg_sha256_vector(void) +{ + uint8_t entropy[32], nonce[16], reseed[32], got[128], expect[128]; + + /* HMAC_DRBG.rsp, line 5064. */ + unhex(entropy, sizeof entropy, "06032cd5eed33f39265f49ecb142c511da9aff2af71203bffaf34a9ca5bd9c0d"); + unhex(nonce, sizeof nonce, "0e66f71edc43e42a45ad3c6fc6cdc4df"); + unhex(reseed, sizeof reseed, "01920a4e669ed3a85ae8a33b35a74ad7fb2a6bb4cf395ce00334a9c9a5a5d552"); + unhex(expect, sizeof expect, "76fc79fe9b50beccc991a11b5635783a83536add03c157fb30645e611c2898bb2b1bc215000209208cd506cb28da2a51bdb03826aaf2bd2335d576d519160842e7158ad0949d1a9ec3e66ea1b1a064b005de914eac2e9d4f2d72a8616a80225422918250ff66a41bd2f864a6a38cc5b6499dc43f7f2bd09e1e0f8f5885935124"); + + cf_hmac_drbg ctx; + cf_hmac_drbg_init(&ctx, &cf_sha256, entropy, sizeof entropy, nonce, sizeof nonce, NULL, 0); + cf_hmac_drbg_reseed(&ctx, reseed, sizeof reseed, NULL, 0); + cf_hmac_drbg_gen(&ctx, got, sizeof got); + cf_hmac_drbg_gen(&ctx, got, sizeof got); + TEST_CHECK(memcmp(got, expect, sizeof got) == 0); +} + +static void test_hmacdrbg_sha256_vector_addnl(void) +{ + uint8_t entropy[32], nonce[16], reseed[32], got[128], expect[128], addnl[32]; + + /* HMAC_DRBG.rsp, line 5230. */ + unhex(entropy, sizeof entropy, "05ac9fc4c62a02e3f90840da5616218c6de5743d66b8e0fbf833759c5928b53d"); + unhex(nonce, sizeof nonce, "2b89a17904922ed8f017a63044848545"); + unhex(reseed, sizeof reseed, "2791126b8b52ee1fd9392a0a13e0083bed4186dc649b739607ac70ec8dcecf9b"); + unhex(expect, sizeof expect, "02ddff5173da2fcffa10215b030d660d61179e61ecc22609b1151a75f1cbcbb4363c3a89299b4b63aca5e581e73c860491010aa35de3337cc6c09ebec8c91a6287586f3a74d9694b462d2720ea2e11bbd02af33adefb4a16e6b370fa0effd57d607547bdcfbb7831f54de7073ad2a7da987a0016a82fa958779a168674b56524"); + + cf_hmac_drbg ctx; + cf_hmac_drbg_init(&ctx, &cf_sha256, entropy, sizeof entropy, nonce, sizeof nonce, NULL, 0); + unhex(addnl, sizeof addnl, "43bac13bae715092cf7eb280a2e10a962faf7233c41412f69bc74a35a584e54c"); + cf_hmac_drbg_reseed(&ctx, reseed, sizeof reseed, addnl, sizeof addnl); + unhex(addnl, sizeof addnl, "3f2fed4b68d506ecefa21f3f5bb907beb0f17dbc30f6ffbba5e5861408c53a1e"); + cf_hmac_drbg_gen_additional(&ctx, addnl, sizeof addnl, got, sizeof got); + unhex(addnl, sizeof addnl, "529030df50f410985fde068df82b935ec23d839cb4b269414c0ede6cffea5b68"); + cf_hmac_drbg_gen_additional(&ctx, addnl, sizeof addnl, got, sizeof got); + TEST_CHECK(memcmp(got, expect, sizeof got) == 0); +} + +static void test_hmacdrbg_sha512_vector(void) +{ + uint8_t entropy[32], nonce[16], reseed[32], got[256], expect[256]; + + /* HMAC_DRBG.rsp, line 10120. */ + unhex(entropy, sizeof entropy, "48c121b18733af15c27e1dd9ba66a9a81a5579cdba0f5b657ec53c2b9e90bbf6"); + unhex(nonce, sizeof nonce, "bbb7c777428068fad9970891f879b1af"); + unhex(reseed, sizeof reseed, "e0ffefdadb9ccf990504d568bdb4d862cbe17ccce6e22dfcab8b4804fd21421a"); + unhex(expect, sizeof expect, "05da6aac7d980da038f65f392841476d37fe70fbd3e369d1f80196e66e54b8fadb1d60e1a0f3d4dc173769d75fc3410549d7a843270a54a068b4fe767d7d9a59604510a875ad1e9731c8afd0fd50b825e2c50d062576175106a9981be37e02ec7c5cd0a69aa0ca65bddaee1b0de532e10cfa1f5bf6a026e47379736a099d6750ab121dbe3622b841baf8bdcbe875c85ba4b586b8b5b57b0fecbec08c12ff2a9453c47c6e32a52103d972c62ab9affb8e728a31fcefbbccc556c0f0a35f4b10ace2d96b906e36cbb72233201e536d3e13b045187b417d2449cad1edd192e061f12d22147b0a176ea8d9c4c35404395b6502ef333a813b6586037479e0fa3c6a23"); + + cf_hmac_drbg ctx; + cf_hmac_drbg_init(&ctx, &cf_sha512, entropy, sizeof entropy, nonce, sizeof nonce, NULL, 0); + cf_hmac_drbg_reseed(&ctx, reseed, sizeof reseed, NULL, 0); + cf_hmac_drbg_gen(&ctx, got, sizeof got); + cf_hmac_drbg_gen(&ctx, got, sizeof got); + TEST_CHECK(memcmp(got, expect, sizeof got) == 0); +} + +static void test_hmacdrbg_sha512_vector_addnl(void) +{ + uint8_t entropy[32], nonce[16], reseed[32], got[256], expect[256], addnl[32]; + + /* HMAC_DRBG.rsp, line 10286. */ + unhex(entropy, sizeof entropy, "4686a959e17dfb96c294b09c0f7a60efb386416cfb4c8972bcc55e44a151607a"); + unhex(nonce, sizeof nonce, "5226543b4c89321bbfb0f11f18ee3462"); + unhex(reseed, sizeof reseed, "5ef50daaf29929047870235c17762f5df5d9ab1af656e0e215fcc6fd9fc0d85d"); + unhex(expect, sizeof expect, "b60d8803531b2b8583d17bdf3ac7c01f3c65cf9b069862b2d39b9024b34c172b712db0704acb078a1ab1aec0390dbaee2dec9be7b234e63da481fd469a92c77bc7bb2cfca586855520e0f9e9d47dcb9bdf2a2fdfa9f2b4342ef0ea582616b55477717cfd516d46d6383257743656f7cf8b38402ba795a8c9d35a4aa88bec623313dad6ead689d152b54074f183b2fee556f554db343626cea853718f18d386bc8bebb0c07b3c5e96ceb391ffceece88864dbd3be83a613562c5c417a24807d5f9332974f045e79a9ade36994af6cf9bbeeb71d0025fcb4ad50f121cbc2df7cd12ff5a50cddfd9a4bbc6d942d743c8b8fbebe00eeccea3d14e07ff8454fa715da"); + + cf_hmac_drbg ctx; + cf_hmac_drbg_init(&ctx, &cf_sha512, entropy, sizeof entropy, nonce, sizeof nonce, NULL, 0); + unhex(addnl, sizeof addnl, "d2383c3e528492269e6c3b3aaa2b54fbf48731f5aa52150ce7fc644679a5e7c6"); + cf_hmac_drbg_reseed(&ctx, reseed, sizeof reseed, addnl, sizeof addnl); + unhex(addnl, sizeof addnl, "c841e7a2d9d13bdb8644cd7f5d91d241a369e12dc6c9c2be50d1ed29484bff98"); + cf_hmac_drbg_gen_additional(&ctx, addnl, sizeof addnl, got, sizeof got); + unhex(addnl, sizeof addnl, "9054cf9216af66a788d3bf6757b8987e42d4e49b325e728dc645d5e107048245"); + cf_hmac_drbg_gen_additional(&ctx, addnl, sizeof addnl, got, sizeof got); + TEST_CHECK(memcmp(got, expect, sizeof got) == 0); +} + +TEST_LIST = { + { "hashdrbg-sha256", test_hashdrbg_sha256_vector }, + { "hashdrbg-sha256-addnl", test_hashdrbg_sha256_vector_addnl }, + { "hmacdrbg-sha1", test_hmacdrbg_sha1_vector }, + { "hmacdrbg-sha1-addnl", test_hmacdrbg_sha1_vector_addnl }, + { "hmacdrbg-sha256", test_hmacdrbg_sha256_vector }, + { "hmacdrbg-sha256-addnl", test_hmacdrbg_sha256_vector_addnl }, + { "hmacdrbg-sha512", test_hmacdrbg_sha512_vector }, + { "hmacdrbg-sha512-addnl", test_hmacdrbg_sha512_vector_addnl }, + { 0 } +}; + diff --git a/deps/picoquic/picotls/deps/cifra/src/testmodes.c b/deps/picoquic/picotls/deps/cifra/src/testmodes.c new file mode 100644 index 0000000..ed96dde --- /dev/null +++ b/deps/picoquic/picotls/deps/cifra/src/testmodes.c @@ -0,0 +1,890 @@ +/* + * cifra - embedded cryptography library + * Written in 2014 by Joseph Birr-Pixton + * + * To the extent possible under law, the author(s) have dedicated all + * copyright and related and neighboring rights to this software to the + * public domain worldwide. This software is distributed without any + * warranty. + * + * You should have received a copy of the CC0 Public Domain Dedication + * along with this software. If not, see + * . + */ + +#include "aes.h" +#include "modes.h" +#include "bitops.h" +#include "gf128.h" + +#include "handy.h" +#include "cutest.h" +#include "testutil.h" + +/* Some tests are too big for microcontrollers. */ +#if defined(CORTEX_M0) || defined(CORTEX_M3) || defined(CORTEX_M4) +# define MCU_TARGET 1 +#else +# define MCU_TARGET 0 +#endif + +static void test_cbc(void) +{ + uint8_t out[16]; + + const void *iv = "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f"; + const void *key = "\x2b\x7e\x15\x16\x28\xae\xd2\xa6\xab\xf7\x15\x88\x09\xcf\x4f\x3c"; + const void *inp = "\x6b\xc1\xbe\xe2\x2e\x40\x9f\x96\xe9\x3d\x7e\x11\x73\x93\x17\x2a"; + const void *expect = "\x76\x49\xab\xac\x81\x19\xb2\x46\xce\xe9\x8e\x9b\x12\xe9\x19\x7d"; + + cf_aes_context aes; + cf_aes_init(&aes, key, 16); + + cf_cbc cbc; + cf_cbc_init(&cbc, &cf_aes, &aes, iv); + cf_cbc_encrypt(&cbc, inp, out, 1); + TEST_CHECK(memcmp(out, expect, 16) == 0); + + uint8_t decrypt[16]; + cf_cbc_init(&cbc, &cf_aes, &aes, iv); + cf_cbc_decrypt(&cbc, out, decrypt, 1); + TEST_CHECK(memcmp(decrypt, inp, 16) == 0); +} + +static void cbcmac_vector(const void *tag_expect, size_t ntag, + const void *key, size_t nkey, + const void *msg, size_t nmsg) +{ + uint8_t tag[16]; + + cf_aes_context aes; + cf_aes_init(&aes, key, nkey); + + cf_cbcmac_stream cm; + cf_cbcmac_stream_init(&cm, &cf_aes, &aes); + cf_cbcmac_stream_update(&cm, msg, nmsg); + cf_cbcmac_stream_pad_final(&cm, tag); + + TEST_CHECK(sizeof tag == ntag); + TEST_CHECK(memcmp(tag, tag_expect, sizeof tag) == 0); +} + +static void test_cbcmac(void) +{ + cbcmac_vector("\xf0\xf1\x89\x75\xa0\x85\x9d\x13\xa4\x9d\x3d\xbf\xc6\xcd\x65\xd9", 16, "\x04\xf7\xf7\x78\x62\x1d\x1e\x2c\x86\x47\x82\x2a\x50\xd9\x8a\x83", 16, "\x83\x1b\xe7\x4b\x9b\xe6\x85\xc8\x38\xe2\x2a\x25\xa3\x11\xcb\x14\x79\x62\x35\xf5\x28\x98\xd0", 23); + cbcmac_vector("\x0d\x6a\x13\x8f\x75\xb7\x56\x94\xd5\x15\xc5\x55\x5e\xee\xdd\x92", 16, "\xff\x84\x5a\xfc\x51\xf2\x06\x35\xa4\x8f\x6c\xec\x9f\x78\x1f\x2e", 16, "\xc5\x85\x3e\x6b\x3f\x7e\xf5\x10\x93\x6e\x30\xd5\x54\x13\x5f\x0d\x55\x43\x92\x8c\x53\xfc\x2f\x81\xa3", 25); + cbcmac_vector("\x96\x81\x3d\xb1\x7e\xac\x06\xb9\x79\x42\xa7\x3a\x7c\x5a\x0a\xad", 16, "\x10\x77\x16\x47\x23\x2e\xda\x40\x23\xd7\xc5\xc9\xbb\x51\x2e\x93", 16, "\x06\x53\x5f\x70\xd9\x6c\x80\x50\x85\x6b\x02\x4f\x67\xae\x87\xde\xc8\xd2\x9d\xab\xb7\x1f\x55\x93\x51\x00\x0a\x3c\x8f\xfc\x63\x60", 32); + cbcmac_vector("\x20\xdd\xa5\xb1\xc1\x14\x00\x90\x97\x41\xef\x3b\xc6\xac\xe8\xec", 16, "\x5b\x39\xdb\x4b\xa4\x53\x1f\x97\xf9\xca\x4b\xdd\xed\x9b\x28\x53", 16, "\x49\x91\xb3\x35\x40\xda\x4d\x8a\xdf\xe9\x37\x4b\xb4\xe1\xc5", 15); + cbcmac_vector("\xc0\x2f\x8f\x0a\xba\x13\x4b\x6b\x16\x69\xfb\x58\x2f\xc1\xc8\x76", 16, "\xd0\x22\xc7\xe7\x85\xd2\xfc\xa4\xd6\x7f\xaa\x18\xb1\xa9\xfd\x9d\x7a\x47\x37\x09\x33\x43\x06\x32", 24, "\x2b\xa2\x8e\xa5\x62\xdd\x9c\x5e\x80\xcc\xaf\x80\x16\x77", 14); + cbcmac_vector("\x05\x79\x4b\x5f\xc8\xf2\xee\x87\x74\xcd\x88\x9f\x7c\x29\xeb\xa0", 16, "\xe4\x51\xdb\x26\x8e\x2a\x26\xd1\xbf\x78\x3e\xab\x5d\xc6\xf9\x3f\xb2\xc5\xe2\x5c\xe8\x61\x28\x3c", 24, "\xea\x14\xfa\xaa\x95\x48\x12\xcb", 8); + cbcmac_vector("\x6a\x14\x4b\xaa\x39\xf6\x19\x71\x62\x65\xd3\x4e\x53\xb4\xc6\x7c", 16, "\xff\x46\x38\x0f\x62\xa9\x37\x7f\xb2\x41\x88\x44\x39\x2a\x97\xf5\xb9\x9a\xc0\x37\xf9\xc6\x75\x3f", 24, "\x64\x04\x53\x4c\xa8\x0a\x60\xf6\x5e\x22\xb6\xc4\xd7\xf3\xa9\x33\xf9\x3e", 18); + cbcmac_vector("\xf7\x1d\x16\x5c\xba\xac\x0f\xf0\x1a\x12\x75\xf8\x5b\x6a\x8e\x15", 16, "\x67\xce\x47\x6c\x11\x0e\xa1\xbc\xf0\x81\x30\x2b\x5f\xe2\x3b\xbc\x34\xc5\x4d\x46\x01\xed\x49\x04", 24, "\x94\xb1\x25\x63\x49\x49\x46\x7e\x7a\xa0\x0e\xa1\x10\x25\x21\x9a\xc9\x1f\x0d\xed\xa1\x10\x30\x7e\x08\x84\xee\x09\xe8\x31\x53\x81", 32); + cbcmac_vector("\x22\xfb\x7e\x4c\x77\x12\x7c\xed\x2c\xaa\xf9\x8d\x9f\x35\x15\x60", 16, "\x1c\x50\xc0\x79\x7c\xd6\x7f\x89\x26\xd1\xc9\xb9\x85\xf9\xee\xaf\x18\x3f\x07\x0b\x3a\xd2\x5f\x7e\xfa\x08\x95\xfe\x98\xe3\x43\x91", 32, "\x7d\x1e\x7e\x19\x9a\xd4\xf4\x3f\xcf\xff\x55\xf7\xc9\x81\xe6\x13\xc0\x22\xab\x7f\x83\x92\x21\x72\x65\x79\x78\xcd\xf0\x8b\x36", 31); + cbcmac_vector("\x40\xc1\xef\xf3\xf4\x71\x54\x58\x77\x3c\xd3\x07\x96\xdf\xfd\x54", 16, "\x3c\x1e\xae\xa7\x4a\xf6\xee\x43\x9b\xd7\xa3\x76\x38\xd6\x08\x21\x60\xe6\x1b\x23\x2b\xf8\xa4\x5d\x05\xd5\xf4\x89\x04\x3e\x2d\x19", 32, "\xd2\xa3\x38\x1a\x82\xd6\xb6\xc2\x52\x93\x43\x1d\xdc\x1d\x73\xb5\x14\x82\x40\xfe\x00\xc3\x24\x52\x8d\x69\xc6\x11\x4e\x4c\xa9\x40\xcd\xfb\x29\x17", 36); + cbcmac_vector("\x69\x7c\x65\x95\xa2\x1f\xa2\xfa\x3a\xd3\x60\x68\x7a\xed\x68\x37", 16, "\xc2\xda\x01\xb4\x12\xa5\xcd\x1c\x75\xb5\x08\x5f\xd2\xee\x79\xc3\x47\xd9\xf9\x12\x86\x3d\x81\xd0\x42\x89\x75\x96\x58\x70\x47\x05", 32, "\x65\x22\x9b\x77\x15\xe5\x02\x54\x04\x90\xfb\xe2\xbf\x5a\x8e\xb0\xbf\x64\xff\x7f\xb7\xab\x7f\x18\x69\x7b", 26); + cbcmac_vector("\xf5\x2d\x65\x16\x84\x43\x0d\xe8\x1f\x29\x51\x06\xec\xf0\xa5\xd2", 16, "\x76\xff\xb3\x38\x5b\xca\x7c\x93\xc0\x12\xd7\xbc\xb3\xa3\xd0\xf2\x87\xa7\x0a\x91\x36\x76\xa7\x8d\x28\x47\x05\x8e\x75\xae\x5e\x3c", 32, "\x12\x90\x91\x65\x32\x37\xd0\x35\xf6\x40\x42\xa7\x4f\x61\xa9\x9c\x8f\xd6\x84\x9a\x86\x0e\x57\xe7\xe4", 25); +} + +static void test_ctr(void) +{ + uint8_t out[16]; + + const void *nonce = "\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff"; + const void *key = "\x2b\x7e\x15\x16\x28\xae\xd2\xa6\xab\xf7\x15\x88\x09\xcf\x4f\x3c"; + const void *inp = "\x6b\xc1\xbe\xe2\x2e\x40\x9f\x96\xe9\x3d\x7e\x11\x73\x93\x17\x2a"; + const void *expect = "\x87\x4d\x61\x91\xb6\x20\xe3\x26\x1b\xef\x68\x64\x99\x0d\xb6\xce"; + + cf_aes_context aes; + cf_aes_init(&aes, key, 16); + + cf_ctr ctr; + cf_ctr_init(&ctr, &cf_aes, &aes, nonce); + cf_ctr_cipher(&ctr, inp, out, 16); /* one piece */ + TEST_CHECK(memcmp(expect, out, 16) == 0); + + cf_ctr_init(&ctr, &cf_aes, &aes, nonce); + cf_ctr_cipher(&ctr, inp, out, 1); /* incremental (2 blocks) */ + cf_ctr_cipher(&ctr, inp, out, 16); + cf_ctr_cipher(&ctr, inp, out, 16); + + cf_ctr_init(&ctr, &cf_aes, &aes, nonce); + cf_ctr_cipher(&ctr, inp, out, 1); /* incremental */ + cf_ctr_cipher(&ctr, ((uint8_t *)inp) + 1, out + 1, 15); + TEST_CHECK(memcmp(expect, out, 16) == 0); + + uint8_t decrypt[16]; + cf_ctr_init(&ctr, &cf_aes, &aes, nonce); + cf_ctr_cipher(&ctr, out, decrypt, 16); + TEST_CHECK(memcmp(decrypt, inp, 16) == 0); + + /* Test we use the right number of blocks up. */ + uint8_t test_nonce[16], test_inp[16]; + memset(test_nonce, 0xff, 16); + memset(test_inp, 0x00, 16); + cf_ctr_init(&ctr, &cf_aes, &aes, test_nonce); + + /* Exercise cf_blockwise_xor code paths. */ + for (int i = 0; i < 1024; i++) + { + cf_ctr_cipher(&ctr, test_inp, out, i % 16); + } + + /* expected counter value is 1024 * 7.5 / 16 - 1: + * 479 = 0x1df + */ + + memset(test_nonce, 0, sizeof test_nonce); + test_nonce[15] = 0xdf; + test_nonce[14] = 0x01; + + TEST_CHECK(memcmp(test_nonce, ctr.nonce, 16) == 0); +} + +static void check_eax(const void *key, size_t nkey, + const void *msg, size_t nmsg, + const void *nonce, size_t nnonce, + const void *header, size_t nheader, + const void *expect_cipher, + const void *expect_tag, size_t ntag) +{ + uint8_t cipher[32]; + uint8_t tag[16]; + + assert(nmsg <= sizeof cipher); + assert(ntag <= ntag); + + cf_aes_context aes; + cf_aes_init(&aes, key, nkey); + + cf_eax_encrypt(&cf_aes, &aes, + msg, nmsg, + header, nheader, + nonce, nnonce, + cipher, + tag, ntag); + + TEST_CHECK(memcmp(expect_cipher, cipher, nmsg) == 0); + TEST_CHECK(memcmp(expect_tag, tag, ntag) == 0); + + int rc; + uint8_t tmp[sizeof cipher]; + rc = cf_eax_decrypt(&cf_aes, &aes, + cipher, nmsg, + header, nheader, + nonce, nnonce, + tag, ntag, + tmp); + TEST_CHECK(rc == 0); + TEST_CHECK(memcmp(tmp, msg, nmsg) == 0); + + tag[0] ^= 0xff; + rc = cf_eax_decrypt(&cf_aes, &aes, + cipher, nmsg, + header, nheader, + nonce, nnonce, + tag, ntag, + tmp); + TEST_CHECK(rc == 1); +} + +static void test_eax(void) +{ + /* Test vectors from paper. */ + check_eax("\x23\x39\x52\xDE\xE4\xD5\xED\x5F\x9B\x9C\x6D\x6F\xF8\x0F\xF4\x78", 16, + "", 0, + "\x62\xEC\x67\xF9\xC3\xA4\xA4\x07\xFC\xB2\xA8\xC4\x90\x31\xA8\xB3", 16, + "\x6B\xFB\x91\x4F\xD0\x7E\xAE\x6B", 8, + "", + "\xE0\x37\x83\x0E\x83\x89\xF2\x7B\x02\x5A\x2D\x65\x27\xE7\x9D\x01", 16); + + check_eax("\x91\x94\x5D\x3F\x4D\xCB\xEE\x0B\xF4\x5E\xF5\x22\x55\xF0\x95\xA4", 16, + "\xF7\xFB", 2, + "\xBE\xCA\xF0\x43\xB0\xA2\x3D\x84\x31\x94\xBA\x97\x2C\x66\xDE\xBD", 16, + "\xFA\x3B\xFD\x48\x06\xEB\x53\xFA", 8, + "\x19\xDD", + "\x5C\x4C\x93\x31\x04\x9D\x0B\xDA\xB0\x27\x74\x08\xF6\x79\x67\xE5", 16); + + check_eax("\x01\xF7\x4A\xD6\x40\x77\xF2\xE7\x04\xC0\xF6\x0A\xDA\x3D\xD5\x23", 16, + "\x1A\x47\xCB\x49\x33", 5, + "\x70\xC3\xDB\x4F\x0D\x26\x36\x84\x00\xA1\x0E\xD0\x5D\x2B\xFF\x5E", 16, + "\x23\x4A\x34\x63\xC1\x26\x4A\xC6", 8, + "\xD8\x51\xD5\xBA\xE0", + "\x3A\x59\xF2\x38\xA2\x3E\x39\x19\x9D\xC9\x26\x66\x26\xC4\x0F\x80", 16); + + check_eax("\xD0\x7C\xF6\xCB\xB7\xF3\x13\xBD\xDE\x66\xB7\x27\xAF\xD3\xC5\xE8", 16, + "\x48\x1C\x9E\x39\xB1", 5, + "\x84\x08\xDF\xFF\x3C\x1A\x2B\x12\x92\xDC\x19\x9E\x46\xB7\xD6\x17", 16, + "\x33\xCC\xE2\xEA\xBF\xF5\xA7\x9D", 8, + "\x63\x2A\x9D\x13\x1A", + "\xD4\xC1\x68\xA4\x22\x5D\x8E\x1F\xF7\x55\x93\x99\x74\xA7\xBE\xDE", 16); + + check_eax("\x35\xB6\xD0\x58\x00\x05\xBB\xC1\x2B\x05\x87\x12\x45\x57\xD2\xC2", 16, + "\x40\xD0\xC0\x7D\xA5\xE4", 6, + "\xFD\xB6\xB0\x66\x76\xEE\xDC\x5C\x61\xD7\x42\x76\xE1\xF8\xE8\x16", 16, + "\xAE\xB9\x6E\xAE\xBE\x29\x70\xE9", 8, + "\x07\x1D\xFE\x16\xC6\x75", + "\xCB\x06\x77\xE5\x36\xF7\x3A\xFE\x6A\x14\xB7\x4E\xE4\x98\x44\xDD", 16); + + check_eax("\xBD\x8E\x6E\x11\x47\x5E\x60\xB2\x68\x78\x4C\x38\xC6\x2F\xEB\x22", 16, + "\x4D\xE3\xB3\x5C\x3F\xC0\x39\x24\x5B\xD1\xFB\x7D", 12, + "\x6E\xAC\x5C\x93\x07\x2D\x8E\x85\x13\xF7\x50\x93\x5E\x46\xDA\x1B", 16, + "\xD4\x48\x2D\x1C\xA7\x8D\xCE\x0F", 8, + "\x83\x5B\xB4\xF1\x5D\x74\x3E\x35\x0E\x72\x84\x14", + "\xAB\xB8\x64\x4F\xD6\xCC\xB8\x69\x47\xC5\xE1\x05\x90\x21\x0A\x4F", 16); + + check_eax("\x7C\x77\xD6\xE8\x13\xBE\xD5\xAC\x98\xBA\xA4\x17\x47\x7A\x2E\x7D", 16, + "\x8B\x0A\x79\x30\x6C\x9C\xE7\xED\x99\xDA\xE4\xF8\x7F\x8D\xD6\x16\x36", 17, + "\x1A\x8C\x98\xDC\xD7\x3D\x38\x39\x3B\x2B\xF1\x56\x9D\xEE\xFC\x19", 16, + "\x65\xD2\x01\x79\x90\xD6\x25\x28", 8, + "\x02\x08\x3E\x39\x79\xDA\x01\x48\x12\xF5\x9F\x11\xD5\x26\x30\xDA\x30", + "\x13\x73\x27\xD1\x06\x49\xB0\xAA\x6E\x1C\x18\x1D\xB6\x17\xD7\xF2", 16); + + check_eax("\x5F\xFF\x20\xCA\xFA\xB1\x19\xCA\x2F\xC7\x35\x49\xE2\x0F\x5B\x0D", 16, + "\x1B\xDA\x12\x2B\xCE\x8A\x8D\xBA\xF1\x87\x7D\x96\x2B\x85\x92\xDD\x2D\x56", 18, + "\xDD\xE5\x9B\x97\xD7\x22\x15\x6D\x4D\x9A\xFF\x2B\xC7\x55\x98\x26", 16, + "\x54\xB9\xF0\x4E\x6A\x09\x18\x9A", 8, + "\x2E\xC4\x7B\x2C\x49\x54\xA4\x89\xAF\xC7\xBA\x48\x97\xED\xCD\xAE\x8C\xC3", + "\x3B\x60\x45\x05\x99\xBD\x02\xC9\x63\x82\x90\x2A\xEF\x7F\x83\x2A", 16); + + check_eax("\xA4\xA4\x78\x2B\xCF\xFD\x3E\xC5\xE7\xEF\x6D\x8C\x34\xA5\x61\x23", 16, + "\x6C\xF3\x67\x20\x87\x2B\x85\x13\xF6\xEA\xB1\xA8\xA4\x44\x38\xD5\xEF\x11", 18, + "\xB7\x81\xFC\xF2\xF7\x5F\xA5\xA8\xDE\x97\xA9\xCA\x48\xE5\x22\xEC", 16, + "\x89\x9A\x17\x58\x97\x56\x1D\x7E", 8, + "\x0D\xE1\x8F\xD0\xFD\xD9\x1E\x7A\xF1\x9F\x1D\x8E\xE8\x73\x39\x38\xB1\xE8", + "\xE7\xF6\xD2\x23\x16\x18\x10\x2F\xDB\x7F\xE5\x5F\xF1\x99\x17\x00", 16); + + check_eax("\x83\x95\xFC\xF1\xE9\x5B\xEB\xD6\x97\xBD\x01\x0B\xC7\x66\xAA\xC3", 16, + "\xCA\x40\xD7\x44\x6E\x54\x5F\xFA\xED\x3B\xD1\x2A\x74\x0A\x65\x9F\xFB\xBB\x3C\xEA\xB7", 21, + "\x22\xE7\xAD\xD9\x3C\xFC\x63\x93\xC5\x7E\xC0\xB3\xC1\x7D\x6B\x44", 16, + "\x12\x67\x35\xFC\xC3\x20\xD2\x5A", 8, + "\xCB\x89\x20\xF8\x7A\x6C\x75\xCF\xF3\x96\x27\xB5\x6E\x3E\xD1\x97\xC5\x52\xD2\x95\xA7", + "\xCF\xC4\x6A\xFC\x25\x3B\x46\x52\xB1\xAF\x37\x95\xB1\x24\xAB\x6E", 16); + + /* Test vector from bug #3 */ + check_eax("\x58\x94\x17\xB0\x32\x4B\x1B\x71\xD7\xA6\x75\x18\x52\x86\x7A\xE8", 16, + "\x00\x00\x1C\x40\x00\x00\x00\x48\x00\x00\x00\x73", 12, + "\x00\x01\x00\x00\xF6\x83", 6, + "", 0, + "\xD5\xD8\x99\x79\xAE\x79\xEB\xEE\x4E\x38\x5F\xA5", + "\x0E\xFB\x21\xFA\xD7\x14\xA2\x5B\x44\x14\x5F\x79\x22\x1A\x2C\x9A", 16); + +} + +static void check_cmac(const void *key, size_t nkey, + const void *msg, size_t nmsg, + const void *wanttag, size_t ntag) +{ + uint8_t gottag[16]; + + TEST_CHECK(cf_aes.blocksz == ntag); + + cf_aes_context aes; + cf_aes_init(&aes, key, nkey); + + cf_cmac cmac; + cf_cmac_init(&cmac, &cf_aes, &aes); + cf_cmac_sign(&cmac, msg, nmsg, gottag); + + TEST_CHECK(memcmp(gottag, wanttag, cf_aes.blocksz) == 0); +} + +static void test_cmac(void) +{ + /* These from SP800-38B */ + check_cmac("\x2b\x7e\x15\x16\x28\xae\xd2\xa6\xab\xf7\x15\x88\x09\xcf\x4f\x3c", 16, + "", 0, + "\xbb\x1d\x69\x29\xe9\x59\x37\x28\x7f\xa3\x7d\x12\x9b\x75\x67\x46", 16); + check_cmac("\x2b\x7e\x15\x16\x28\xae\xd2\xa6\xab\xf7\x15\x88\x09\xcf\x4f\x3c", 16, + "\x6b\xc1\xbe\xe2\x2e\x40\x9f\x96\xe9\x3d\x7e\x11\x73\x93\x17\x2a", 16, + "\x07\x0a\x16\xb4\x6b\x4d\x41\x44\xf7\x9b\xdd\x9d\xd0\x4a\x28\x7c", 16); + check_cmac("\x2b\x7e\x15\x16\x28\xae\xd2\xa6\xab\xf7\x15\x88\x09\xcf\x4f\x3c", 16, + "\x6b\xc1\xbe\xe2\x2e\x40\x9f\x96\xe9\x3d\x7e\x11\x73\x93\x17\x2a\xae\x2d\x8a\x57\x1e\x03\xac\x9c\x9e\xb7\x6f\xac\x45\xaf\x8e\x51\x30\xc8\x1c\x46\xa3\x5c\xe4\x11", 40, + "\xdf\xa6\x67\x47\xde\x9a\xe6\x30\x30\xca\x32\x61\x14\x97\xc8\x27", 16); + check_cmac("\x2b\x7e\x15\x16\x28\xae\xd2\xa6\xab\xf7\x15\x88\x09\xcf\x4f\x3c", 16, + "\x6b\xc1\xbe\xe2\x2e\x40\x9f\x96\xe9\x3d\x7e\x11\x73\x93\x17\x2a\xae\x2d\x8a\x57\x1e\x03\xac\x9c\x9e\xb7\x6f\xac\x45\xaf\x8e\x51\x30\xc8\x1c\x46\xa3\x5c\xe4\x11\xe5\xfb\xc1\x19\x1a\x0a\x52\xef\xf6\x9f\x24\x45\xdf\x4f\x9b\x17\xad\x2b\x41\x7b\xe6\x6c\x37\x10", 64, + "\x51\xf0\xbe\xbf\x7e\x3b\x9d\x92\xfc\x49\x74\x17\x79\x36\x3c\xfe", 16); + + check_cmac("\x8e\x73\xb0\xf7\xda\x0e\x64\x52\xc8\x10\xf3\x2b\x80\x90\x79\xe5\x62\xf8\xea\xd2\x52\x2c\x6b\x7b", 24, + "", 0, + "\xd1\x7d\xdf\x46\xad\xaa\xcd\xe5\x31\xca\xc4\x83\xde\x7a\x93\x67", 16); + check_cmac("\x8e\x73\xb0\xf7\xda\x0e\x64\x52\xc8\x10\xf3\x2b\x80\x90\x79\xe5\x62\xf8\xea\xd2\x52\x2c\x6b\x7b", 24, + "\x6b\xc1\xbe\xe2\x2e\x40\x9f\x96\xe9\x3d\x7e\x11\x73\x93\x17\x2a", 16, + "\x9e\x99\xa7\xbf\x31\xe7\x10\x90\x06\x62\xf6\x5e\x61\x7c\x51\x84", 16); + check_cmac("\x8e\x73\xb0\xf7\xda\x0e\x64\x52\xc8\x10\xf3\x2b\x80\x90\x79\xe5\x62\xf8\xea\xd2\x52\x2c\x6b\x7b", 24, + "\x6b\xc1\xbe\xe2\x2e\x40\x9f\x96\xe9\x3d\x7e\x11\x73\x93\x17\x2a\xae\x2d\x8a\x57\x1e\x03\xac\x9c\x9e\xb7\x6f\xac\x45\xaf\x8e\x51\x30\xc8\x1c\x46\xa3\x5c\xe4\x11", 40, + "\x8a\x1d\xe5\xbe\x2e\xb3\x1a\xad\x08\x9a\x82\xe6\xee\x90\x8b\x0e", 16); + check_cmac("\x8e\x73\xb0\xf7\xda\x0e\x64\x52\xc8\x10\xf3\x2b\x80\x90\x79\xe5\x62\xf8\xea\xd2\x52\x2c\x6b\x7b", 24, + "\x6b\xc1\xbe\xe2\x2e\x40\x9f\x96\xe9\x3d\x7e\x11\x73\x93\x17\x2a\xae\x2d\x8a\x57\x1e\x03\xac\x9c\x9e\xb7\x6f\xac\x45\xaf\x8e\x51\x30\xc8\x1c\x46\xa3\x5c\xe4\x11\xe5\xfb\xc1\x19\x1a\x0a\x52\xef\xf6\x9f\x24\x45\xdf\x4f\x9b\x17\xad\x2b\x41\x7b\xe6\x6c\x37\x10", 64, + "\xa1\xd5\xdf\x0e\xed\x79\x0f\x79\x4d\x77\x58\x96\x59\xf3\x9a\x11", 16); + + check_cmac("\x60\x3d\xeb\x10\x15\xca\x71\xbe\x2b\x73\xae\xf0\x85\x7d\x77\x81\x1f\x35\x2c\x07\x3b\x61\x08\xd7\x2d\x98\x10\xa3\x09\x14\xdf\xf4", 32, + "", 0, + "\x02\x89\x62\xf6\x1b\x7b\xf8\x9e\xfc\x6b\x55\x1f\x46\x67\xd9\x83", 16); + check_cmac("\x60\x3d\xeb\x10\x15\xca\x71\xbe\x2b\x73\xae\xf0\x85\x7d\x77\x81\x1f\x35\x2c\x07\x3b\x61\x08\xd7\x2d\x98\x10\xa3\x09\x14\xdf\xf4", 32, + "\x6b\xc1\xbe\xe2\x2e\x40\x9f\x96\xe9\x3d\x7e\x11\x73\x93\x17\x2a", 16, + "\x28\xa7\x02\x3f\x45\x2e\x8f\x82\xbd\x4b\xf2\x8d\x8c\x37\xc3\x5c", 16); + check_cmac("\x60\x3d\xeb\x10\x15\xca\x71\xbe\x2b\x73\xae\xf0\x85\x7d\x77\x81\x1f\x35\x2c\x07\x3b\x61\x08\xd7\x2d\x98\x10\xa3\x09\x14\xdf\xf4", 32, + "\x6b\xc1\xbe\xe2\x2e\x40\x9f\x96\xe9\x3d\x7e\x11\x73\x93\x17\x2a\xae\x2d\x8a\x57\x1e\x03\xac\x9c\x9e\xb7\x6f\xac\x45\xaf\x8e\x51\x30\xc8\x1c\x46\xa3\x5c\xe4\x11", 40, + "\xaa\xf3\xd8\xf1\xde\x56\x40\xc2\x32\xf5\xb1\x69\xb9\xc9\x11\xe6", 16); + check_cmac("\x60\x3d\xeb\x10\x15\xca\x71\xbe\x2b\x73\xae\xf0\x85\x7d\x77\x81\x1f\x35\x2c\x07\x3b\x61\x08\xd7\x2d\x98\x10\xa3\x09\x14\xdf\xf4", 32, + "\x6b\xc1\xbe\xe2\x2e\x40\x9f\x96\xe9\x3d\x7e\x11\x73\x93\x17\x2a\xae\x2d\x8a\x57\x1e\x03\xac\x9c\x9e\xb7\x6f\xac\x45\xaf\x8e\x51\x30\xc8\x1c\x46\xa3\x5c\xe4\x11\xe5\xfb\xc1\x19\x1a\x0a\x52\xef\xf6\x9f\x24\x45\xdf\x4f\x9b\x17\xad\x2b\x41\x7b\xe6\x6c\x37\x10", 64, + "\xe1\x99\x21\x90\x54\x9f\x6e\xd5\x69\x6a\x2c\x05\x6c\x31\x54\x10", 16); +} + +static void test_gf128_mul(void) +{ + uint8_t bout[16]; + + const void *bx = "\x03\x88\xda\xce\x60\xb6\xa3\x92\xf3\x28\xc2\xb9\x71\xb2\xfe\x78"; + const void *by = "\x66\xe9\x4b\xd4\xef\x8a\x2c\x3b\x88\x4c\xfa\x59\xca\x34\x2b\x2e"; + const void *bexpect = "\x5e\x2e\xc7\x46\x91\x70\x62\x88\x2c\x85\xb0\x68\x53\x53\xde\xb7"; + + cf_gf128 x, y, out; + cf_gf128_frombytes_be(bx, x); + cf_gf128_frombytes_be(by, y); + cf_gf128_mul(x, y, out); + cf_gf128_tobytes_be(out, bout); + TEST_CHECK(memcmp(bexpect, bout, 16) == 0); +} + +static void check_gcm(const void *key, size_t nkey, + const void *plain, size_t nplain, + const void *aad, size_t naad, + const void *iv, size_t niv, + const void *cipher_expect, size_t ncipher, + const void *tag_expect, size_t ntag) +{ + uint8_t plain_decrypt[64], + cipher[64], + tag[16]; + + assert(ncipher == nplain); + + cf_aes_context ctx; + cf_aes_init(&ctx, key, nkey); + + cf_gcm_encrypt(&cf_aes, &ctx, + plain, nplain, + aad, naad, + iv, niv, + cipher, + tag, ntag); + + TEST_CHECK(memcmp(tag, tag_expect, ntag) == 0); + TEST_CHECK(memcmp(cipher, cipher_expect, ncipher) == 0); + + int err = cf_gcm_decrypt(&cf_aes, &ctx, + cipher, ncipher, + aad, naad, + iv, niv, + tag, ntag, + plain_decrypt); + TEST_CHECK(err == 0); + TEST_CHECK(memcmp(plain_decrypt, plain, ncipher) == 0); + + tag[0] ^= 0xff; + err = cf_gcm_decrypt(&cf_aes, &ctx, + cipher, ncipher, + aad, naad, + iv, niv, + tag, ntag, + plain_decrypt); + TEST_CHECK(err == 1); +} + +static void test_gcm(void) +{ + check_gcm("\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 16, + "", 0, + "", 0, + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 12, + "", 0, + "\x58\xe2\xfc\xce\xfa\x7e\x30\x61\x36\x7f\x1d\x57\xa4\xe7\x45\x5a", 16); + check_gcm("\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 16, + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 16, + "", 0, + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 12, + "\x03\x88\xda\xce\x60\xb6\xa3\x92\xf3\x28\xc2\xb9\x71\xb2\xfe\x78", 16, + "\xab\x6e\x47\xd4\x2c\xec\x13\xbd\xf5\x3a\x67\xb2\x12\x57\xbd\xdf", 16); + check_gcm("\xfe\xff\xe9\x92\x86\x65\x73\x1c\x6d\x6a\x8f\x94\x67\x30\x83\x08", 16, + "\xd9\x31\x32\x25\xf8\x84\x06\xe5\xa5\x59\x09\xc5\xaf\xf5\x26\x9a" + "\x86\xa7\xa9\x53\x15\x34\xf7\xda\x2e\x4c\x30\x3d\x8a\x31\x8a\x72" + "\x1c\x3c\x0c\x95\x95\x68\x09\x53\x2f\xcf\x0e\x24\x49\xa6\xb5\x25" + "\xb1\x6a\xed\xf5\xaa\x0d\xe6\x57\xba\x63\x7b\x39\x1a\xaf\xd2\x55", 64, + "", 0, + "\xca\xfe\xba\xbe\xfa\xce\xdb\xad\xde\xca\xf8\x88", 12, + "\x42\x83\x1e\xc2\x21\x77\x74\x24\x4b\x72\x21\xb7\x84\xd0\xd4\x9c" + "\xe3\xaa\x21\x2f\x2c\x02\xa4\xe0\x35\xc1\x7e\x23\x29\xac\xa1\x2e" + "\x21\xd5\x14\xb2\x54\x66\x93\x1c\x7d\x8f\x6a\x5a\xac\x84\xaa\x05" + "\x1b\xa3\x0b\x39\x6a\x0a\xac\x97\x3d\x58\xe0\x91\x47\x3f\x59\x85", 64, + "\x4d\x5c\x2a\xf3\x27\xcd\x64\xa6\x2c\xf3\x5a\xbd\x2b\xa6\xfa\xb4", 16); + check_gcm("\xfe\xff\xe9\x92\x86\x65\x73\x1c\x6d\x6a\x8f\x94\x67\x30\x83\x08", 16, + "\xd9\x31\x32\x25\xf8\x84\x06\xe5\xa5\x59\x09\xc5\xaf\xf5\x26\x9a" + "\x86\xa7\xa9\x53\x15\x34\xf7\xda\x2e\x4c\x30\x3d\x8a\x31\x8a\x72" + "\x1c\x3c\x0c\x95\x95\x68\x09\x53\x2f\xcf\x0e\x24\x49\xa6\xb5\x25" + "\xb1\x6a\xed\xf5\xaa\x0d\xe6\x57\xba\x63\x7b\x39", 60, + "\xfe\xed\xfa\xce\xde\xad\xbe\xef\xfe\xed\xfa\xce\xde\xad\xbe\xef" + "\xab\xad\xda\xd2", 20, + "\xca\xfe\xba\xbe\xfa\xce\xdb\xad\xde\xca\xf8\x88", 12, + "\x42\x83\x1e\xc2\x21\x77\x74\x24\x4b\x72\x21\xb7\x84\xd0\xd4\x9c" + "\xe3\xaa\x21\x2f\x2c\x02\xa4\xe0\x35\xc1\x7e\x23\x29\xac\xa1\x2e" + "\x21\xd5\x14\xb2\x54\x66\x93\x1c\x7d\x8f\x6a\x5a\xac\x84\xaa\x05" + "\x1b\xa3\x0b\x39\x6a\x0a\xac\x97\x3d\x58\xe0\x91", 60, + "\x5b\xc9\x4f\xbc\x32\x21\xa5\xdb\x94\xfa\xe9\x5a\xe7\x12\x1a\x47", 16); + check_gcm("\xfe\xff\xe9\x92\x86\x65\x73\x1c\x6d\x6a\x8f\x94\x67\x30\x83\x08", 16, + "\xd9\x31\x32\x25\xf8\x84\x06\xe5\xa5\x59\x09\xc5\xaf\xf5\x26\x9a" + "\x86\xa7\xa9\x53\x15\x34\xf7\xda\x2e\x4c\x30\x3d\x8a\x31\x8a\x72" + "\x1c\x3c\x0c\x95\x95\x68\x09\x53\x2f\xcf\x0e\x24\x49\xa6\xb5\x25" + "\xb1\x6a\xed\xf5\xaa\x0d\xe6\x57\xba\x63\x7b\x39", 60, + "\xfe\xed\xfa\xce\xde\xad\xbe\xef\xfe\xed\xfa\xce\xde\xad\xbe\xef" + "\xab\xad\xda\xd2", 20, + "\xca\xfe\xba\xbe\xfa\xce\xdb\xad", 8, + "\x61\x35\x3b\x4c\x28\x06\x93\x4a\x77\x7f\xf5\x1f\xa2\x2a\x47\x55" + "\x69\x9b\x2a\x71\x4f\xcd\xc6\xf8\x37\x66\xe5\xf9\x7b\x6c\x74\x23" + "\x73\x80\x69\x00\xe4\x9f\x24\xb2\x2b\x09\x75\x44\xd4\x89\x6b\x42" + "\x49\x89\xb5\xe1\xeb\xac\x0f\x07\xc2\x3f\x45\x98", 60, + "\x36\x12\xd2\xe7\x9e\x3b\x07\x85\x56\x1b\xe1\x4a\xac\xa2\xfc\xcb", 16); + check_gcm("\xfe\xff\xe9\x92\x86\x65\x73\x1c\x6d\x6a\x8f\x94\x67\x30\x83\x08", 16, + "\xd9\x31\x32\x25\xf8\x84\x06\xe5\xa5\x59\x09\xc5\xaf\xf5\x26\x9a" + "\x86\xa7\xa9\x53\x15\x34\xf7\xda\x2e\x4c\x30\x3d\x8a\x31\x8a\x72" + "\x1c\x3c\x0c\x95\x95\x68\x09\x53\x2f\xcf\x0e\x24\x49\xa6\xb5\x25" + "\xb1\x6a\xed\xf5\xaa\x0d\xe6\x57\xba\x63\x7b\x39", 60, + "\xfe\xed\xfa\xce\xde\xad\xbe\xef\xfe\xed\xfa\xce\xde\xad\xbe\xef" + "\xab\xad\xda\xd2", 20, + "\x93\x13\x22\x5d\xf8\x84\x06\xe5\x55\x90\x9c\x5a\xff\x52\x69\xaa" + "\x6a\x7a\x95\x38\x53\x4f\x7d\xa1\xe4\xc3\x03\xd2\xa3\x18\xa7\x28" + "\xc3\xc0\xc9\x51\x56\x80\x95\x39\xfc\xf0\xe2\x42\x9a\x6b\x52\x54" + "\x16\xae\xdb\xf5\xa0\xde\x6a\x57\xa6\x37\xb3\x9b", 60, + "\x8c\xe2\x49\x98\x62\x56\x15\xb6\x03\xa0\x33\xac\xa1\x3f\xb8\x94" + "\xbe\x91\x12\xa5\xc3\xa2\x11\xa8\xba\x26\x2a\x3c\xca\x7e\x2c\xa7" + "\x01\xe4\xa9\xa4\xfb\xa4\x3c\x90\xcc\xdc\xb2\x81\xd4\x8c\x7c\x6f" + "\xd6\x28\x75\xd2\xac\xa4\x17\x03\x4c\x34\xae\xe5", 60, + "\x61\x9c\xc5\xae\xff\xfe\x0b\xfa\x46\x2a\xf4\x3c\x16\x99\xd0\x50", 16); + + check_gcm("\xfe\xff\xe9\x92\x86\x65\x73\x1c\x6d\x6a\x8f\x94\x67\x30\x83\x08", 16, + "\xd9\x31\x32\x25\xf8\x84\x06\xe5\xa5\x59\x09\xc5\xaf\xf5\x26\x9a" + "\x86\xa7\xa9\x53\x15\x34\xf7\xda\x2e\x4c\x30\x3d\x8a\x31\x8a\x72" + "\x1c\x3c\x0c\x95\x95\x68\x09\x53\x2f\xcf\x0e\x24\x49\xa6\xb5\x25" + "\xb1\x6a\xed\xf5\xaa\x0d\xe6\x57\xba\x63\x7b\x39", 60, + "\xfe\xed\xfa\xce\xde\xad\xbe\xef\xfe\xed\xfa\xce\xde\xad\xbe\xef" + "\xab\xad\xda\xd2", 20, + "\x93\x13\x22\x5d\xf8\x84\x06\xe5\x55\x90\x9c\x5a\xff\x52\x69\xaa" + "\x6a\x7a\x95\x38\x53\x4f\x7d\xa1\xe4\xc3\x03\xd2\xa3\x18\xa7\x28" + "\xc3\xc0\xc9\x51\x56\x80\x95\x39\xfc\xf0\xe2\x42\x9a\x6b\x52\x54" + "\x16\xae\xdb\xf5\xa0\xde\x6a\x57\xa6\x37\xb3\x9b", 60, + "\x8c\xe2\x49\x98\x62\x56\x15\xb6\x03\xa0\x33\xac\xa1\x3f\xb8\x94" + "\xbe\x91\x12\xa5\xc3\xa2\x11\xa8\xba\x26\x2a\x3c\xca\x7e\x2c\xa7" + "\x01\xe4\xa9\xa4\xfb\xa4\x3c\x90\xcc\xdc\xb2\x81\xd4\x8c\x7c\x6f" + "\xd6\x28\x75\xd2\xac\xa4\x17\x03\x4c\x34\xae\xe5", 60, + "\x61\x9c\xc5\xae\xff\xfe\x0b\xfa\x46\x2a\xf4\x3c\x16\x99\xd0\x50", 16); + check_gcm("\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 24, + "", 0, + "", 0, + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 12, + "", 0, + "\xcd\x33\xb2\x8a\xc7\x73\xf7\x4b\xa0\x0e\xd1\xf3\x12\x57\x24\x35", 16); + check_gcm("\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 24, + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 16, + "", 0, + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 12, + "\x98\xe7\x24\x7c\x07\xf0\xfe\x41\x1c\x26\x7e\x43\x84\xb0\xf6\x00", 16, + "\x2f\xf5\x8d\x80\x03\x39\x27\xab\x8e\xf4\xd4\x58\x75\x14\xf0\xfb", 16); + check_gcm("\xfe\xff\xe9\x92\x86\x65\x73\x1c\x6d\x6a\x8f\x94\x67\x30\x83\x08" + "\xfe\xff\xe9\x92\x86\x65\x73\x1c", 24, + "\xd9\x31\x32\x25\xf8\x84\x06\xe5\xa5\x59\x09\xc5\xaf\xf5\x26\x9a" + "\x86\xa7\xa9\x53\x15\x34\xf7\xda\x2e\x4c\x30\x3d\x8a\x31\x8a\x72" + "\x1c\x3c\x0c\x95\x95\x68\x09\x53\x2f\xcf\x0e\x24\x49\xa6\xb5\x25" + "\xb1\x6a\xed\xf5\xaa\x0d\xe6\x57\xba\x63\x7b\x39\x1a\xaf\xd2\x55", 64, + "", 0, + "\xca\xfe\xba\xbe\xfa\xce\xdb\xad\xde\xca\xf8\x88", 12, + "\x39\x80\xca\x0b\x3c\x00\xe8\x41\xeb\x06\xfa\xc4\x87\x2a\x27\x57" + "\x85\x9e\x1c\xea\xa6\xef\xd9\x84\x62\x85\x93\xb4\x0c\xa1\xe1\x9c" + "\x7d\x77\x3d\x00\xc1\x44\xc5\x25\xac\x61\x9d\x18\xc8\x4a\x3f\x47" + "\x18\xe2\x44\x8b\x2f\xe3\x24\xd9\xcc\xda\x27\x10\xac\xad\xe2\x56", 64, + "\x99\x24\xa7\xc8\x58\x73\x36\xbf\xb1\x18\x02\x4d\xb8\x67\x4a\x14", 16); +} + +static void check_ccm(const void *key, size_t nkey, + const void *header, size_t nheader, + const void *plain, size_t nplain, + const void *nonce, size_t nnonce, + const void *expect_cipher, size_t ncipher, + const void *expect_tag, size_t ntag) +{ + uint8_t cipher[32], tag[16], decrypted[32]; + + assert(ncipher == nplain); + + cf_aes_context ctx; + cf_aes_init(&ctx, key, nkey); + + cf_ccm_encrypt(&cf_aes, &ctx, + plain, nplain, 15 - nnonce, + header, nheader, + nonce, nnonce, + cipher, + tag, ntag); + + TEST_CHECK(memcmp(tag, expect_tag, ntag) == 0); + TEST_CHECK(memcmp(cipher, expect_cipher, ncipher) == 0); + + int err; + err = cf_ccm_decrypt(&cf_aes, &ctx, + expect_cipher, ncipher, 15 - nnonce, + header, nheader, + nonce, nnonce, + tag, ntag, + decrypted); + TEST_CHECK(err == 0); + TEST_CHECK(memcmp(decrypted, plain, nplain) == 0); + + tag[0] ^= 0xff; + + err = cf_ccm_decrypt(&cf_aes, &ctx, + expect_cipher, ncipher, 15 - nnonce, + header, nheader, + nonce, nnonce, + tag, ntag, + decrypted); + TEST_CHECK(err == 1); +} + +#if !MCU_TARGET +static void fill(uint8_t *buf, size_t len, uint8_t b) +{ + for (size_t i = 0; i < len; i++) + buf[i] = b++; +} + +static void test_ccm_long(void) +{ + /* This is example 4 from SP800-38C, to test the long AAD code path. */ + uint8_t header[0x10000]; + uint8_t key[16]; + uint8_t tag[14]; + uint8_t nonce[13]; + uint8_t plain[32], cipher[32]; + + fill(header, sizeof header, 0x00); + fill(key, sizeof key, 0x40); + fill(nonce, sizeof nonce, 0x10); + fill(plain, sizeof plain, 0x20); + + const void *expect_tag = "\xb4\xac\x6b\xec\x93\xe8\x59\x8e\x7f\x0d\xad\xbc\xea\x5b"; + const void *expect_cipher = "\x69\x91\x5d\xad\x1e\x84\xc6\x37\x6a\x68\xc2\x96\x7e\x4d\xab\x61\x5a\xe0\xfd\x1f\xae\xc4\x4c\xc4\x84\x82\x85\x29\x46\x3c\xcf\x72"; + + cf_aes_context ctx; + cf_aes_init(&ctx, key, sizeof key); + + cf_ccm_encrypt(&cf_aes, &ctx, + plain, sizeof plain, 15 - sizeof nonce, + header, sizeof header, + nonce, sizeof nonce, + cipher, + tag, sizeof tag); + + TEST_CHECK(memcmp(expect_tag, tag, sizeof tag) == 0); + TEST_CHECK(memcmp(expect_cipher, cipher, sizeof cipher) == 0); +} +#endif + +static void test_ccm(void) +{ + check_ccm("\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf", 16, + "\x00\x01\x02\x03\x04\x05\x06\x07", 8, + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e", 23, + "\x00\x00\x00\x03\x02\x01\x00\xa0\xa1\xa2\xa3\xa4\xa5", 13, + "\x58\x8c\x97\x9a\x61\xc6\x63\xd2\xf0\x66\xd0\xc2\xc0\xf9\x89\x80\x6d\x5f\x6b\x61\xda\xc3\x84", 23, + "\x17\xe8\xd1\x2c\xfd\xf9\x26\xe0", 8); + + check_ccm("\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f", 16, + "\x00\x01\x02\x03\x04\x05\x06\x07", 8, + "\x20\x21\x22\x23", 4, + "\x10\x11\x12\x13\x14\x15\x16", 7, + "\x71\x62\x01\x5b", 4, + "\x4d\xac\x25\x5d", 4); + + check_ccm("\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f", 16, + "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13", 20, + "\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37", 24, + "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b", 12, + "\xe3\xb2\x01\xa9\xf5\xb7\x1a\x7a\x9b\x1c\xea\xec\xcd\x97\xe7\x0b\x61\x76\xaa\xd9\xa4\x42\x8a\xa5", 24, + "\x48\x43\x92\xfb\xc1\xb0\x99\x51", 8); +} + +static void check_ocb(const void *key, size_t nkey, + const void *header, size_t nheader, + const void *plain, size_t nplain, + const void *nonce, size_t nnonce, + const void *expect_cipher, size_t ncipher, + const void *expect_tag, size_t ntag) +{ + uint8_t cipher[40], tag[16]; + + assert(ncipher == nplain); + assert(ncipher <= sizeof cipher); + assert(ntag <= sizeof tag); + + cf_aes_context ctx; + cf_aes_init(&ctx, key, nkey); + + cf_ocb_encrypt(&cf_aes, &ctx, + plain, nplain, + header, nheader, + nonce, nnonce, + cipher, + tag, ntag); + + TEST_CHECK(memcmp(tag, expect_tag, ntag) == 0); + TEST_CHECK(memcmp(cipher, expect_cipher, ncipher) == 0); + + uint8_t decrypted[40]; + int err; + err = cf_ocb_decrypt(&cf_aes, &ctx, + expect_cipher, ncipher, + header, nheader, + nonce, nnonce, + tag, ntag, + decrypted); + TEST_CHECK(err == 0); + TEST_CHECK(memcmp(decrypted, plain, nplain) == 0); + + tag[0] ^= 0xff; + + err = cf_ocb_decrypt(&cf_aes, &ctx, + expect_cipher, ncipher, + header, nheader, + nonce, nnonce, + tag, ntag, + decrypted); + TEST_CHECK(err == 1); +} + +static void test_ocb(void) +{ + check_ocb("\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F", 16, + "", 0, + "", 0, + "\xBB\xAA\x99\x88\x77\x66\x55\x44\x33\x22\x11\x00", 12, + "", 0, + "\x78\x54\x07\xBF\xFF\xC8\xAD\x9E\xDC\xC5\x52\x0A\xC9\x11\x1E\xE6", 16); + + check_ocb("\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F", 16, + "\x00\x01\x02\x03\x04\x05\x06\x07", 8, + "\x00\x01\x02\x03\x04\x05\x06\x07", 8, + "\xBB\xAA\x99\x88\x77\x66\x55\x44\x33\x22\x11\x01", 12, + "\x68\x20\xB3\x65\x7B\x6F\x61\x5A", 8, + "\x57\x25\xBD\xA0\xD3\xB4\xEB\x3A\x25\x7C\x9A\xF1\xF8\xF0\x30\x09", 16); + + check_ocb("\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F", 16, + "\x00\x01\x02\x03\x04\x05\x06\x07", 8, + "", 0, + "\xBB\xAA\x99\x88\x77\x66\x55\x44\x33\x22\x11\x02", 12, + "", 0, + "\x81\x01\x7F\x82\x03\xF0\x81\x27\x71\x52\xFA\xDE\x69\x4A\x0A\x00", 16); + + check_ocb("\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F", 16, + "", 0, + "\x00\x01\x02\x03\x04\x05\x06\x07", 8, + "\xBB\xAA\x99\x88\x77\x66\x55\x44\x33\x22\x11\x03", 12, + "\x45\xDD\x69\xF8\xF5\xAA\xE7\x24", 8, + "\x14\x05\x4C\xD1\xF3\x5D\x82\x76\x0B\x2C\xD0\x0D\x2F\x99\xBF\xA9", 16); + + check_ocb("\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F", 16, + "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F", 16, + "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F", 16, + "\xBB\xAA\x99\x88\x77\x66\x55\x44\x33\x22\x11\x04", 12, + "\x57\x1D\x53\x5B\x60\xB2\x77\x18\x8B\xE5\x14\x71\x70\xA9\xA2\x2C", 16, + "\x3A\xD7\xA4\xFF\x38\x35\xB8\xC5\x70\x1C\x1C\xCE\xC8\xFC\x33\x58", 16); + + check_ocb("\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F", 16, + "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F", 16, + "", 0, + "\xBB\xAA\x99\x88\x77\x66\x55\x44\x33\x22\x11\x05", 12, + "", 0, + "\x8C\xF7\x61\xB6\x90\x2E\xF7\x64\x46\x2A\xD8\x64\x98\xCA\x6B\x97", 16); + + check_ocb("\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F", 16, + "", 0, + "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F", 16, + "\xBB\xAA\x99\x88\x77\x66\x55\x44\x33\x22\x11\x06", 12, + "\x5C\xE8\x8E\xC2\xE0\x69\x27\x06\xA9\x15\xC0\x0A\xEB\x8B\x23\x96", 16, + "\xF4\x0E\x1C\x74\x3F\x52\x43\x6B\xDF\x06\xD8\xFA\x1E\xCA\x34\x3D", 16); + + check_ocb("\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F", 16, + "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F\x10\x11\x12\x13\x14\x15\x16\x17", 24, + "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F\x10\x11\x12\x13\x14\x15\x16\x17", 24, + "\xBB\xAA\x99\x88\x77\x66\x55\x44\x33\x22\x11\x07", 12, + "\x1C\xA2\x20\x73\x08\xC8\x7C\x01\x07\x56\x10\x4D\x88\x40\xCE\x19\x52\xF0\x96\x73\xA4\x48\xA1\x22", 24, + "\xC9\x2C\x62\x24\x10\x51\xF5\x73\x56\xD7\xF3\xC9\x0B\xB0\xE0\x7F", 16); + + check_ocb("\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F", 16, + "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F\x10\x11\x12\x13\x14\x15\x16\x17", 24, + "", 0, + "\xBB\xAA\x99\x88\x77\x66\x55\x44\x33\x22\x11\x08", 12, + "", 0, + "\x6D\xC2\x25\xA0\x71\xFC\x1B\x9F\x7C\x69\xF9\x3B\x0F\x1E\x10\xDE", 16); + + check_ocb("\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F", 16, + "", 0, + "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F\x10\x11\x12\x13\x14\x15\x16\x17", 24, + "\xBB\xAA\x99\x88\x77\x66\x55\x44\x33\x22\x11\x09", 12, + "\x22\x1B\xD0\xDE\x7F\xA6\xFE\x99\x3E\xCC\xD7\x69\x46\x0A\x0A\xF2\xD6\xCD\xED\x0C\x39\x5B\x1C\x3C", 24, + "\xE7\x25\xF3\x24\x94\xB9\xF9\x14\xD8\x5C\x0B\x1E\xB3\x83\x57\xFF", 16); + + check_ocb("\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F", 16, + "\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", 32, + "\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", 32, + "\xBB\xAA\x99\x88\x77\x66\x55\x44\x33\x22\x11\x0A", 12, + "\xBD\x6F\x6C\x49\x62\x01\xC6\x92\x96\xC1\x1E\xFD\x13\x8A\x46\x7A\xBD\x3C\x70\x79\x24\xB9\x64\xDE\xAF\xFC\x40\x31\x9A\xF5\xA4\x85", 32, + "\x40\xFB\xBA\x18\x6C\x55\x53\xC6\x8A\xD9\xF5\x92\xA7\x9A\x42\x40", 16); + + check_ocb("\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F", 16, + "\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", 32, + "", 0, + "\xBB\xAA\x99\x88\x77\x66\x55\x44\x33\x22\x11\x0B", 12, + "", 0, + "\xFE\x80\x69\x0B\xEE\x8A\x48\x5D\x11\xF3\x29\x65\xBC\x9D\x2A\x32", 16); + + check_ocb("\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F", 16, + "", 0, + "\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", 32, + "\xBB\xAA\x99\x88\x77\x66\x55\x44\x33\x22\x11\x0C", 12, + "\x29\x42\xBF\xC7\x73\xBD\xA2\x3C\xAB\xC6\xAC\xFD\x9B\xFD\x58\x35\xBD\x30\x0F\x09\x73\x79\x2E\xF4\x60\x40\xC5\x3F\x14\x32\xBC\xDF", 32, + "\xB5\xE1\xDD\xE3\xBC\x18\xA5\xF8\x40\xB5\x2E\x65\x34\x44\xD5\xDF", 16); + + check_ocb("\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F", 16, + "\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", 40, + "\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", 40, + "\xBB\xAA\x99\x88\x77\x66\x55\x44\x33\x22\x11\x0D", 12, + "\xD5\xCA\x91\x74\x84\x10\xC1\x75\x1F\xF8\xA2\xF6\x18\x25\x5B\x68\xA0\xA1\x2E\x09\x3F\xF4\x54\x60\x6E\x59\xF9\xC1\xD0\xDD\xC5\x4B\x65\xE8\x62\x8E\x56\x8B\xAD\x7A", 40, + "\xED\x07\xBA\x06\xA4\xA6\x94\x83\xA7\x03\x54\x90\xC5\x76\x9E\x60", 16); + + check_ocb("\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F", 16, + "\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", 40, + "", 0, + "\xBB\xAA\x99\x88\x77\x66\x55\x44\x33\x22\x11\x0E", 12, + "", 0, + "\xC5\xCD\x9D\x18\x50\xC1\x41\xE3\x58\x64\x99\x94\xEE\x70\x1B\x68", 16); + + check_ocb("\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F", 16, + "", 0, + "\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", 40, + "\xBB\xAA\x99\x88\x77\x66\x55\x44\x33\x22\x11\x0F", 12, + "\x44\x12\x92\x34\x93\xC5\x7D\x5D\xE0\xD7\x00\xF7\x53\xCC\xE0\xD1\xD2\xD9\x50\x60\x12\x2E\x9F\x15\xA5\xDD\xBF\xC5\x78\x7E\x50\xB5\xCC\x55\xEE\x50\x7B\xCB\x08\x4E", 40, + "\x47\x9A\xD3\x63\xAC\x36\x6B\x95\xA9\x8C\xA5\xF3\x00\x0B\x14\x79", 16); + + check_ocb("\x0F\x0E\x0D\x0C\x0B\x0A\x09\x08\x07\x06\x05\x04\x03\x02\x01\x00", 16, + "\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", 40, + "\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", 40, + "\xBB\xAA\x99\x88\x77\x66\x55\x44\x33\x22\x11\x0D", 12, + "\x17\x92\xA4\xE3\x1E\x07\x55\xFB\x03\xE3\x1B\x22\x11\x6E\x6C\x2D\xDF\x9E\xFD\x6E\x33\xD5\x36\xF1\xA0\x12\x4B\x0A\x55\xBA\xE8\x84\xED\x93\x48\x15\x29\xC7\x6B\x6A", 40, + "\xD0\xC5\x15\xF4\xD1\xCD\xD4\xFD\xAC\x4F\x02\xAA", 12); +} + +#if !MCU_TARGET +static void check_ocb_long(size_t nkey, const void *expect_tag, size_t ntag) +{ + uint8_t C[22400]; + uint8_t K[32]; + uint8_t S[128] = { 0 }; + uint8_t N[12] = { 0 }; + size_t nC = 0; + + memset(K, 0, sizeof K); + K[nkey - 1] = ntag * 8; + + cf_aes_context aes; + cf_aes_init(&aes, K, nkey); + + for (size_t i = 0; i < 128; i++) + { + /* N = num2str(3i+1, 96) */ + memset(N, 0, sizeof N); + write32_be(3 * i + 1, N + 8); + + /* C = C || OCB-ENCRYPT(K, N, S, S) + * nb. OCB-ENCRYPT(Key, Nonce, AAD, Plain) */ + cf_ocb_encrypt(&cf_aes, &aes, + S, i, /* plain */ + S, i, /* aad */ + N, sizeof N, /* nonce */ + C + nC, /* cipher out */ + C + nC + i, /* tag out */ + ntag); + nC += i + ntag; + + /* N = num2str(3i+2,96) */ + write32_be(3 * i + 2, N + 8); + + /* C = C || OCB-ENCRYPT(K, N, , S) */ + cf_ocb_encrypt(&cf_aes, &aes, + S, i, + NULL, 0, + N, sizeof N, + C + nC, + C + nC + i, + ntag); + nC += i + ntag; + + /* N = num2str(3i+3,96) */ + write32_be(3 * i + 3, N + 8); + + /* C = C || OCB-ENCRYPT(K, N, S, ) */ + cf_ocb_encrypt(&cf_aes, &aes, + NULL, 0, + S, i, + N, sizeof N, + NULL, + C + nC, + ntag); + nC += ntag; + } + + /* N = num2str(385, 96) */ + write32_be(385, N + 8); + + /* Output : OCB-ENCRYPT(K, N, C, ) */ + uint8_t result[16]; + cf_ocb_encrypt(&cf_aes, &aes, + NULL, 0, + C, nC, + N, sizeof N, + NULL, + result, ntag); + + TEST_CHECK(memcmp(result, expect_tag, ntag) == 0); +} + +static void test_ocb_long(void) +{ + check_ocb_long(16, "\x67\xE9\x44\xD2\x32\x56\xC5\xE0\xB6\xC6\x1F\xA2\x2F\xDF\x1E\xA2", 16); + check_ocb_long(24, "\xF6\x73\xF2\xC3\xE7\x17\x4A\xAE\x7B\xAE\x98\x6C\xA9\xF2\x9E\x17", 16); + check_ocb_long(32, "\xD9\x0E\xB8\xE9\xC9\x77\xC8\x8B\x79\xDD\x79\x3D\x7F\xFA\x16\x1C", 16); + check_ocb_long(16, "\x77\xA3\xD8\xE7\x35\x89\x15\x8D\x25\xD0\x12\x09", 12); + check_ocb_long(24, "\x05\xD5\x6E\xAD\x27\x52\xC8\x6B\xE6\x93\x2C\x5E", 12); + check_ocb_long(32, "\x54\x58\x35\x9A\xC2\x3B\x0C\xBA\x9E\x63\x30\xDD", 12); + check_ocb_long(16, "\x19\x2C\x9B\x7B\xD9\x0B\xA0\x6A", 8); + check_ocb_long(24, "\x00\x66\xBC\x6E\x0E\xF3\x4E\x24", 8); + check_ocb_long(32, "\x7D\x4E\xA5\xD4\x45\x50\x1C\xBE", 8); +} +#endif + +TEST_LIST = { + { "cbc", test_cbc }, + { "cbcmac", test_cbcmac }, + { "ctr", test_ctr }, + { "eax", test_eax }, + { "cmac", test_cmac }, + { "gf128-mul", test_gf128_mul }, + { "gcm", test_gcm }, + { "ccm", test_ccm }, + { "ocb", test_ocb }, + /* These remaining tests are too big for microcontroller targets. */ +#if !MCU_TARGET + { "ccm-long", test_ccm_long }, + { "ocb-long", test_ocb_long }, +#endif + { 0 } +}; + diff --git a/deps/picoquic/picotls/deps/cifra/src/testnorx.c b/deps/picoquic/picotls/deps/cifra/src/testnorx.c new file mode 100644 index 0000000..b18393f --- /dev/null +++ b/deps/picoquic/picotls/deps/cifra/src/testnorx.c @@ -0,0 +1,118 @@ +/* + * cifra - embedded cryptography library + * Written in 2014 by Joseph Birr-Pixton + * + * To the extent possible under law, the author(s) have dedicated all + * copyright and related and neighboring rights to this software to the + * public domain worldwide. This software is distributed without any + * warranty. + * + * You should have received a copy of the CC0 Public Domain Dedication + * along with this software. If not, see + * . + */ + +#include "norx.h" +#include "handy.h" +#include "cutest.h" +#include "testutil.h" + +static void test_vector(void) +{ + uint8_t K[16], N[8], A[128], M[128], Z[128], C[128], T[16]; + + /* This is from the v2.0 paper, section A.2. */ + + unhex(K, sizeof K, "000102030405060708090a0b0c0d0e0f"); + unhex(N, sizeof N, "f0e0d0c0b0a09080"); + + for (unsigned i = 0; i < 128; i++) + { + A[i] = M[i] = Z[i] = i; + } + + cf_norx32_encrypt(K, N, + A, sizeof A, + M, sizeof M, + Z, sizeof Z, + C, T); + + uint8_t expect_C[128], expect_T[16]; + + unhex(expect_C, sizeof expect_C, "f4afc8e66d2d80de0a7f719c899624c9ad896ec7c61739d5376d0648c7bcb204e57db05c6f83b3ff4315e8a4ef2f2c855f21ea4c51ac6de575773ba548f36e636a13b979d953bb91298ea4a6e2aa27402991e0da541997825407b2f12441de3ae6c5dbfe41b12f1480d234832765111e4c09deef9fe3971618d2217c4b77921e"); + unhex(expect_T, sizeof expect_T, "7810131eea2eab1e5da05d23d4e3cb99"); + + TEST_CHECK(memcmp(C, expect_C, sizeof C) == 0); + TEST_CHECK(memcmp(T, expect_T, sizeof T) == 0); + + uint8_t M2[128]; + TEST_CHECK(0 == + cf_norx32_decrypt(K, N, + A, sizeof A, + C, sizeof C, + Z, sizeof Z, + T, + M2)); + + TEST_CHECK(memcmp(M, M2, sizeof M) == 0); + T[0] ^= 0xff; + + TEST_CHECK(cf_norx32_decrypt(K, N, + A, sizeof A, + C, sizeof C, + Z, sizeof Z, + T, + M2)); +} + +#include "testnorx.katdata.inc" + +static void test_kat(void) +{ + uint8_t K[16], N[16], H[256], W[256]; + const uint8_t *kats = kat_data; + +#define FILL(arr, c) \ + do { \ + for (size_t i = 0; i < sizeof arr; i++) \ + arr[i] = (i * c + 123) & 0xff; \ + } while (0) + FILL(N, 181); + FILL(K, 191); + FILL(H, 193); + FILL(W, 197); +#undef FILL + + for (size_t i = 0; i < sizeof W; i++) + { + uint8_t C[256]; + uint8_t A[16]; + + cf_norx32_encrypt(K, N, + H, i, + W, i, + NULL, 0, + C, A); + + TEST_CHECK(memcmp(kats, C, i) == 0); + kats += i; + TEST_CHECK(memcmp(kats, A, sizeof A) == 0); + kats += sizeof A; + + uint8_t M[256] = { 0 }; + TEST_CHECK(0 == cf_norx32_decrypt(K, N, + H, i, + C, i, + NULL, 0, + A, M)); + + TEST_CHECK(0 == memcmp(M, W, i)); + } +} + +TEST_LIST = { + { "vector", test_vector }, + { "kat", test_kat }, + { 0 } +}; + diff --git a/deps/picoquic/picotls/deps/cifra/src/testnorx.katdata.inc b/deps/picoquic/picotls/deps/cifra/src/testnorx.katdata.inc new file mode 100644 index 0000000..13dd746 --- /dev/null +++ b/deps/picoquic/picotls/deps/cifra/src/testnorx.katdata.inc @@ -0,0 +1,4961 @@ +static const uint8_t kat_data[] = { + 0xAF, 0x7C, 0xEF, 0x63, 0x5D, 0xEE, 0x94, 0x74, + 0x9B, 0xC2, 0x03, 0x9E, 0xC0, 0x49, 0xE0, 0x81, + + 0x69, 0x63, 0x57, 0x72, 0xEC, 0x0B, 0x66, 0xF2, + 0xE4, 0xB0, 0x1D, 0x30, 0x34, 0x29, 0x84, 0xE9, + 0xA8, + + 0x02, 0x46, 0x13, 0xCC, 0x6B, 0x19, 0x96, 0xB3, + 0x7E, 0x1C, 0xA6, 0x79, 0x00, 0x28, 0x19, 0x09, + 0x9F, 0x86, + + 0x99, 0xAC, 0xEA, 0x8C, 0x2B, 0x70, 0x02, 0xBA, + 0x86, 0xE9, 0xEB, 0x22, 0xEC, 0x23, 0xAF, 0x9A, + 0x95, 0xD0, 0xE5, + + 0xFE, 0xFB, 0xA0, 0xBA, 0x20, 0x28, 0x50, 0xDC, + 0xE5, 0xD6, 0x70, 0x26, 0x7A, 0x9E, 0x2A, 0xC3, + 0x3F, 0xCB, 0xD7, 0x0C, + + 0xB9, 0x67, 0x98, 0x4F, 0xDF, 0x1B, 0x19, 0x66, + 0x9C, 0xDE, 0xAE, 0x96, 0x76, 0xDB, 0xDE, 0x55, + 0x95, 0x50, 0xD9, 0xC2, 0xBA, + + 0x62, 0x28, 0x9A, 0x03, 0xF3, 0xF2, 0xFB, 0x0E, + 0xCF, 0xD4, 0x11, 0x74, 0x1D, 0x8E, 0x8B, 0xCD, + 0x92, 0x52, 0x96, 0x5B, 0x64, 0x99, + + 0x2F, 0xCE, 0xB4, 0x86, 0x4C, 0x0A, 0x47, 0xEB, + 0x98, 0x2A, 0xD2, 0xA1, 0x59, 0x9A, 0x5A, 0xB9, + 0x0C, 0xA7, 0x7D, 0xB0, 0xA6, 0xFF, 0x05, + + 0xE0, 0x22, 0x71, 0xE2, 0xCD, 0xF8, 0x3C, 0x12, + 0x8B, 0x4E, 0x28, 0x14, 0x6F, 0x0E, 0x18, 0xAE, + 0x23, 0xDF, 0xD0, 0x59, 0x70, 0x9E, 0x54, 0x49, + + 0xF6, 0xB6, 0x9E, 0x08, 0xB8, 0x27, 0x54, 0x94, + 0xD1, 0x0B, 0x93, 0x25, 0xEF, 0xD5, 0xE2, 0x19, + 0xEF, 0xEA, 0x0C, 0xFC, 0x01, 0x75, 0xC2, 0x90, + 0x00, + + 0x54, 0xBE, 0x7D, 0xFB, 0xF5, 0xCC, 0x52, 0xC6, + 0x9A, 0x0F, 0x65, 0xBD, 0xF1, 0x53, 0x5D, 0x01, + 0x07, 0xCC, 0xAB, 0xB9, 0xA5, 0x53, 0xFD, 0xE7, + 0xE4, 0x74, + + 0xD7, 0x6E, 0x5B, 0x87, 0xD0, 0xC1, 0x68, 0x92, + 0xD0, 0x80, 0x7D, 0x36, 0x8D, 0xE7, 0x36, 0xC5, + 0x97, 0xE7, 0x46, 0xDA, 0xC9, 0x19, 0xD9, 0x98, + 0xD9, 0xBD, 0x7B, + + 0x7E, 0xEB, 0x69, 0xAB, 0x77, 0x97, 0xDA, 0xE8, + 0x47, 0xA4, 0x6E, 0x4A, 0x1D, 0xC9, 0x00, 0xF3, + 0x4B, 0x2E, 0xCD, 0x56, 0xDF, 0xE1, 0xAF, 0x9E, + 0xE8, 0xAD, 0x94, 0x48, + + 0xDF, 0xAB, 0x2B, 0xE7, 0x84, 0x9C, 0xBB, 0x96, + 0x3F, 0x02, 0x99, 0x65, 0x42, 0x1A, 0x0D, 0x33, + 0x97, 0xA8, 0x23, 0x7C, 0x59, 0x4E, 0xCC, 0x64, + 0x54, 0x11, 0x6F, 0x09, 0x80, + + 0xB5, 0x4B, 0x1A, 0x8C, 0x05, 0x62, 0x60, 0x7A, + 0xCC, 0xA8, 0x63, 0x7F, 0xCC, 0xC6, 0x13, 0x70, + 0xF8, 0xC6, 0x09, 0x5C, 0x45, 0x93, 0x3E, 0x7C, + 0x9E, 0x27, 0x23, 0x42, 0xAE, 0x52, + + 0x56, 0x3E, 0x38, 0xC7, 0x66, 0xCE, 0xB5, 0x8F, + 0x77, 0xA1, 0x20, 0xE2, 0x69, 0x08, 0x92, 0x1E, + 0x32, 0x01, 0x75, 0xD2, 0x05, 0x92, 0x1B, 0x60, + 0x60, 0x9F, 0xD2, 0xA3, 0x89, 0x49, 0x6E, + + 0x01, 0x91, 0x5D, 0xDC, 0xC7, 0xB1, 0x4F, 0x6D, + 0x73, 0x4D, 0x3B, 0x68, 0xA0, 0xA5, 0x1D, 0xA5, + 0x22, 0x3C, 0xFE, 0xDC, 0x86, 0xB1, 0x64, 0x54, + 0x92, 0xE1, 0x0F, 0x32, 0xD5, 0xA6, 0x4B, 0xAC, + + 0x13, 0xF8, 0xFC, 0xDB, 0x41, 0x62, 0x9F, 0x6E, + 0xE6, 0x9B, 0xAB, 0xE5, 0x27, 0x3B, 0x8C, 0x15, + 0x8C, 0xC8, 0x03, 0x38, 0xE3, 0x7E, 0x83, 0xDA, + 0xA2, 0x5B, 0xA8, 0x44, 0xE2, 0x41, 0x47, 0x53, + 0x2A, + + 0xD2, 0x73, 0x87, 0xC2, 0xF0, 0x28, 0x2A, 0x0F, + 0xE6, 0x37, 0xC9, 0xAF, 0xF3, 0x02, 0xAC, 0xEA, + 0x37, 0xB6, 0x8C, 0xA6, 0x37, 0x5F, 0x79, 0x89, + 0xCD, 0x90, 0x6B, 0xCC, 0xF6, 0x7C, 0xEC, 0xD9, + 0x75, 0xE6, + + 0x0B, 0x76, 0x2F, 0xAC, 0x9F, 0x20, 0xA1, 0x56, + 0x5F, 0xF9, 0x67, 0xB8, 0xDC, 0xAC, 0x48, 0xA0, + 0x00, 0xD3, 0xFE, 0x87, 0xAE, 0x13, 0x07, 0x26, + 0xD9, 0xC2, 0x6D, 0xF9, 0xEC, 0xB4, 0xA2, 0x41, + 0x17, 0xB6, 0x4C, + + 0xA2, 0xE6, 0x67, 0xBC, 0x65, 0x03, 0x88, 0xDC, + 0xAD, 0x8B, 0x3C, 0x28, 0x83, 0x5F, 0xE3, 0x47, + 0x95, 0xDA, 0xF9, 0x6A, 0x09, 0xE9, 0xF9, 0xD5, + 0x58, 0xE4, 0xC3, 0x64, 0x3E, 0xE2, 0x82, 0x22, + 0xC3, 0xF7, 0x50, 0x40, + + 0x73, 0xA2, 0xA1, 0xB7, 0xC4, 0xCC, 0x5F, 0xA6, + 0x86, 0x7A, 0xFB, 0xF9, 0x2D, 0xB0, 0x92, 0x90, + 0xDB, 0xD1, 0xD0, 0xD4, 0x7E, 0x83, 0xD8, 0xF7, + 0x39, 0x38, 0xFA, 0xD4, 0x24, 0x56, 0x96, 0x1D, + 0xF5, 0xB9, 0x35, 0x7A, 0x7B, + + 0xCB, 0xA1, 0x1C, 0x70, 0x7E, 0x42, 0xF7, 0x32, + 0x68, 0x56, 0x27, 0xC7, 0xFD, 0x13, 0xCE, 0xC2, + 0xD3, 0xEE, 0x3E, 0x33, 0xF3, 0xC3, 0xB4, 0x87, + 0x31, 0x6E, 0xDA, 0xA1, 0x09, 0x77, 0x9E, 0xBE, + 0x43, 0x8D, 0x11, 0xF9, 0x0B, 0x5B, + + 0x31, 0xD7, 0x0C, 0xE3, 0xD3, 0x37, 0xCA, 0xF9, + 0x9E, 0x7E, 0x57, 0xF1, 0x45, 0x02, 0x7D, 0xDC, + 0xA9, 0xBF, 0x73, 0x6B, 0x04, 0x16, 0x44, 0x6D, + 0x62, 0x65, 0x2E, 0x5D, 0xBE, 0x75, 0xD0, 0xA2, + 0x88, 0xF2, 0x60, 0xFE, 0x7B, 0xED, 0x74, + + 0x42, 0xD4, 0xAD, 0xFF, 0xDB, 0x5D, 0xBF, 0x2F, + 0xA9, 0xE1, 0x7B, 0x60, 0xB8, 0xE8, 0x4D, 0x6F, + 0xF9, 0x70, 0xE4, 0x26, 0x40, 0xFA, 0x19, 0x36, + 0x07, 0x5C, 0xEA, 0x0F, 0x47, 0x89, 0xE0, 0x91, + 0xD2, 0xF7, 0x75, 0xB7, 0x27, 0x3A, 0xCC, 0xEB, + + 0xCB, 0x20, 0xDD, 0xCD, 0x30, 0x13, 0xD8, 0x84, + 0xD8, 0x57, 0xAA, 0xFA, 0x74, 0xDA, 0x46, 0x49, + 0x35, 0x60, 0x93, 0x20, 0xAB, 0x32, 0x19, 0x02, + 0x3C, 0x8F, 0x9F, 0x1E, 0xAD, 0x4B, 0x8F, 0x5A, + 0xE5, 0x1F, 0xEA, 0x8B, 0x5C, 0x53, 0x81, 0x68, + 0x0D, + + 0xF9, 0x00, 0x75, 0x76, 0x0E, 0x8B, 0x33, 0xAF, + 0x83, 0xD4, 0xB4, 0x0D, 0xF1, 0x48, 0xD5, 0x02, + 0x08, 0x6F, 0xEC, 0x1B, 0xB0, 0x40, 0x82, 0xF2, + 0xF4, 0x52, 0xCB, 0x43, 0xE6, 0x0F, 0x1F, 0xBE, + 0x15, 0xCE, 0x9D, 0xB8, 0xED, 0x3F, 0x7B, 0xD0, + 0x94, 0xFF, + + 0x6C, 0x78, 0x7D, 0x34, 0x07, 0xA5, 0xB2, 0xA9, + 0xF8, 0x8F, 0xB6, 0x4E, 0x0D, 0x31, 0x23, 0xD2, + 0x71, 0xB5, 0x7D, 0x10, 0xBA, 0xA7, 0x35, 0x1F, + 0xDE, 0x10, 0xE7, 0x21, 0x56, 0x50, 0xB1, 0xAD, + 0x76, 0x03, 0x7C, 0x8C, 0x88, 0x12, 0x09, 0x46, + 0xF4, 0x5F, 0x57, + + 0xFC, 0xAC, 0x41, 0xB1, 0x66, 0xBD, 0xFE, 0x96, + 0x4D, 0xDA, 0x18, 0x9C, 0xDA, 0xA8, 0xC7, 0x6A, + 0x94, 0x5D, 0x2B, 0x44, 0xB0, 0xF6, 0x89, 0x58, + 0x9C, 0x30, 0xD3, 0xC8, 0x48, 0x2F, 0xE8, 0x9F, + 0xB5, 0x33, 0x4C, 0x2A, 0x89, 0x15, 0x71, 0xDF, + 0xDB, 0xA0, 0x4B, 0x00, + + 0x1F, 0x85, 0x54, 0x96, 0x15, 0x04, 0x80, 0x09, + 0xDB, 0x13, 0xA7, 0x52, 0x03, 0x5F, 0x8C, 0x6E, + 0x66, 0x9D, 0x65, 0x64, 0xB0, 0x73, 0xC0, 0x61, + 0xCD, 0xF1, 0xEF, 0xE6, 0x01, 0x5B, 0x3B, 0x85, + 0xB8, 0x9E, 0x54, 0xC8, 0x47, 0x9C, 0x8A, 0x9C, + 0x27, 0x43, 0x4D, 0xB3, 0x33, + + 0x96, 0xEA, 0xF6, 0x72, 0xED, 0x28, 0x75, 0x42, + 0x06, 0x81, 0xEA, 0x29, 0xDF, 0xE4, 0x06, 0x51, + 0xD2, 0x3F, 0x42, 0xF5, 0x9C, 0xDD, 0x4C, 0xCD, + 0xA3, 0xEC, 0x46, 0x65, 0xD8, 0xA2, 0x5D, 0x05, + 0x6D, 0x5B, 0x46, 0x18, 0x44, 0xB0, 0x34, 0x02, + 0x7A, 0xEF, 0x24, 0x2A, 0x73, 0x69, + + 0x41, 0xC9, 0xA4, 0x8C, 0xE0, 0x92, 0xB4, 0xAB, + 0xCB, 0xFA, 0x94, 0xDF, 0xF5, 0x29, 0xFA, 0x23, + 0x43, 0x0F, 0xF0, 0x96, 0x01, 0x58, 0xAC, 0x6E, + 0x16, 0x5B, 0x59, 0xAA, 0x46, 0x10, 0xA5, 0x27, + 0xF3, 0xD0, 0x6D, 0x7B, 0xA8, 0x44, 0x2E, 0x9D, + 0xF9, 0x62, 0x87, 0xA4, 0xA2, 0xDC, 0x69, + + 0xA6, 0xBB, 0x70, 0x33, 0x8E, 0x2E, 0xBD, 0xF7, + 0x24, 0xAF, 0x04, 0xAF, 0x33, 0x3C, 0x58, 0x6A, + 0xB0, 0x83, 0xC6, 0x4B, 0xE6, 0xB6, 0x90, 0xDA, + 0x29, 0xFC, 0x34, 0xDE, 0x50, 0x4E, 0x4F, 0x77, + 0xD6, 0x31, 0x41, 0x80, 0x52, 0x06, 0x39, 0x11, + 0x83, 0xA9, 0x34, 0x69, 0x87, 0xAC, 0x54, 0x28, + + 0x5E, 0x8D, 0x7B, 0xAE, 0xCD, 0x3B, 0x76, 0xBC, + 0xD6, 0x0A, 0x5D, 0x7A, 0x91, 0x4C, 0x51, 0x5D, + 0x54, 0xA6, 0x9D, 0xB8, 0xAD, 0xE9, 0xA3, 0xCB, + 0x80, 0xAD, 0xE8, 0x72, 0x99, 0xFE, 0x31, 0xFA, + 0x9F, 0x5E, 0xB4, 0x7A, 0x8A, 0x33, 0x19, 0x42, + 0xF4, 0x7E, 0xB1, 0x68, 0x28, 0x72, 0xFD, 0xCB, + 0x39, + + 0x4E, 0x1D, 0xFC, 0x0C, 0x9C, 0x55, 0x80, 0x6A, + 0x05, 0x1B, 0x55, 0x77, 0x9D, 0x42, 0x98, 0x4A, + 0x70, 0x70, 0x1E, 0x06, 0x48, 0xD3, 0x76, 0x72, + 0x6A, 0x14, 0x71, 0xA2, 0xF9, 0xB1, 0xB2, 0xE9, + 0x17, 0x08, 0x95, 0x20, 0x2A, 0x00, 0xDF, 0x29, + 0x3B, 0xFA, 0x3C, 0xD2, 0x63, 0xAF, 0x24, 0xF9, + 0x08, 0x09, + + 0xCB, 0x1E, 0x1E, 0x95, 0xE6, 0x40, 0xDF, 0x3F, + 0x0C, 0x7B, 0x5B, 0x76, 0xFD, 0x13, 0xC4, 0x95, + 0xBE, 0xCC, 0x66, 0xD9, 0x1D, 0x4F, 0xBD, 0x29, + 0xBB, 0x3F, 0x5C, 0x3B, 0xA4, 0x0E, 0x4E, 0x76, + 0xD4, 0x42, 0x6F, 0x64, 0xCA, 0x26, 0xAB, 0x7B, + 0xA6, 0x9C, 0x54, 0x7B, 0x2A, 0x71, 0xBA, 0xC2, + 0xF0, 0xF2, 0x6F, + + 0x63, 0xF4, 0x8B, 0x62, 0x4F, 0xE7, 0x46, 0xCC, + 0x4F, 0x60, 0x20, 0x8B, 0x00, 0x57, 0xD0, 0xA7, + 0xDF, 0x6F, 0x08, 0x5C, 0x6F, 0x00, 0x78, 0xA8, + 0x5A, 0x77, 0x71, 0x65, 0x6E, 0x01, 0x71, 0x8D, + 0x2B, 0x15, 0x90, 0x7E, 0x59, 0xA4, 0xF6, 0x23, + 0xF3, 0x12, 0xBC, 0x3C, 0xFD, 0x21, 0x7D, 0x2E, + 0x7E, 0x4F, 0x83, 0xD7, + + 0x76, 0xC1, 0x46, 0xC5, 0x86, 0x11, 0xE9, 0x28, + 0x6A, 0x97, 0x26, 0xC7, 0x89, 0xA7, 0xCD, 0x01, + 0x40, 0x53, 0xD6, 0x42, 0xB3, 0xF1, 0x48, 0x51, + 0x11, 0xC6, 0x9E, 0xDF, 0x91, 0x5C, 0xD9, 0x4C, + 0xB9, 0x68, 0x63, 0x6D, 0xBC, 0xCE, 0x6F, 0x5E, + 0xE3, 0xFC, 0xAB, 0xCE, 0x24, 0x94, 0x5C, 0x43, + 0x51, 0xB6, 0xE7, 0x99, 0x45, + + 0xC9, 0xC7, 0xCD, 0x1F, 0x2E, 0x48, 0x25, 0xC3, + 0x1C, 0xF8, 0x85, 0x32, 0x15, 0x56, 0x82, 0x17, + 0xF4, 0xDC, 0x5F, 0xAE, 0xEF, 0x94, 0xDD, 0xFA, + 0xEA, 0xEE, 0xFC, 0x9B, 0x8C, 0xE1, 0x2E, 0xCF, + 0xE3, 0x40, 0x03, 0xF7, 0xBD, 0x7B, 0x12, 0x56, + 0x42, 0xCA, 0x1A, 0x68, 0xEF, 0xC3, 0xEF, 0xAC, + 0xEB, 0xA8, 0xD8, 0x91, 0x8C, 0x42, + + 0x78, 0xF9, 0x51, 0x9E, 0x84, 0x32, 0xFC, 0xBA, + 0x46, 0xA0, 0xF1, 0x04, 0x77, 0xF6, 0x93, 0xAD, + 0xB3, 0x95, 0xA3, 0x69, 0xA4, 0xAD, 0x45, 0x89, + 0x39, 0xFC, 0x08, 0x35, 0x45, 0xA9, 0xDC, 0x26, + 0x55, 0xA2, 0x8E, 0x3E, 0xF5, 0xE3, 0x86, 0xD1, + 0x1A, 0x37, 0x4C, 0x96, 0x16, 0x68, 0x06, 0x01, + 0xF0, 0x0D, 0x02, 0x4F, 0x17, 0x34, 0x9C, + + 0xD8, 0xFF, 0x53, 0x2C, 0xEE, 0xA5, 0xEB, 0x21, + 0xD8, 0x4C, 0x89, 0xCF, 0x7F, 0x15, 0x59, 0x5D, + 0xDD, 0x45, 0x01, 0x6A, 0x71, 0x0B, 0x47, 0x2C, + 0x72, 0x08, 0xC4, 0x5D, 0x71, 0x6A, 0x59, 0xDB, + 0x46, 0x0E, 0xDF, 0xD7, 0x2B, 0xDA, 0xE1, 0x69, + 0x0A, 0xE7, 0xD7, 0x5F, 0x18, 0x49, 0xC6, 0xA2, + 0xA7, 0x1A, 0x2E, 0x94, 0xCD, 0x45, 0xD9, 0x5A, + + 0x51, 0x54, 0x80, 0x8F, 0x07, 0xDA, 0xCC, 0x7B, + 0xB8, 0xE1, 0xE0, 0xEE, 0xF2, 0x7D, 0x8D, 0x2D, + 0xF0, 0x50, 0x5A, 0xB6, 0xF5, 0xBC, 0x62, 0xBC, + 0xA6, 0x75, 0xB7, 0x80, 0xBA, 0x6E, 0x1D, 0x7C, + 0xD5, 0xC7, 0x42, 0x31, 0xA3, 0x98, 0x08, 0xFE, + 0x13, 0xA2, 0xA6, 0x74, 0x4D, 0xF3, 0x20, 0x9E, + 0xEB, 0x83, 0x64, 0x27, 0xE4, 0x9C, 0x3C, 0x1F, + 0xC2, + + 0xF1, 0x24, 0xBC, 0xD3, 0x95, 0x66, 0x65, 0x69, + 0xA7, 0x6E, 0x61, 0x08, 0x9C, 0xBF, 0xC3, 0x9B, + 0x9B, 0x09, 0x05, 0x0B, 0x94, 0xB4, 0x64, 0xB2, + 0x0E, 0x0F, 0x19, 0x0C, 0x6D, 0x92, 0xF5, 0xC3, + 0xB8, 0x9B, 0xBF, 0x59, 0x00, 0x6D, 0x8D, 0x48, + 0xE0, 0xB1, 0x06, 0xC2, 0x3E, 0x33, 0x26, 0x24, + 0x6F, 0x53, 0xBE, 0x86, 0xD7, 0xEA, 0xCD, 0x18, + 0x18, 0x24, + + 0xE8, 0xE1, 0xC4, 0x50, 0xEF, 0xB6, 0xF0, 0xCA, + 0x78, 0xEF, 0x9C, 0x51, 0x61, 0x58, 0x18, 0xB7, + 0x90, 0x09, 0x48, 0x73, 0xA1, 0x13, 0x6D, 0x16, + 0x8D, 0x97, 0x84, 0x92, 0x25, 0x44, 0xBF, 0xF5, + 0x3C, 0x9D, 0xCE, 0x68, 0x7E, 0x4B, 0x0A, 0x49, + 0xE1, 0xAE, 0xA7, 0xB3, 0xC8, 0xE0, 0x7B, 0x54, + 0xF9, 0xAB, 0x22, 0x0A, 0x19, 0x4F, 0x86, 0x1F, + 0xB7, 0x0F, 0x3A, + + 0x23, 0xD4, 0x95, 0xC7, 0xF9, 0xB7, 0x17, 0x4B, + 0xE8, 0x10, 0xF9, 0xB4, 0xCA, 0x9F, 0xBA, 0xBE, + 0x66, 0xAA, 0x83, 0x98, 0x7D, 0x2B, 0xF4, 0x84, + 0x18, 0x13, 0xCE, 0xBE, 0x10, 0x7D, 0x1B, 0x76, + 0x89, 0xBC, 0x9C, 0xF4, 0xE6, 0x71, 0x62, 0xA7, + 0x39, 0x89, 0xEF, 0x0D, 0xE9, 0x63, 0xD9, 0xB1, + 0x96, 0x63, 0xAC, 0xBC, 0xC7, 0x85, 0x77, 0xE3, + 0xD4, 0x40, 0x41, 0x5C, + + 0x0F, 0x8F, 0x47, 0xF8, 0xEA, 0x17, 0x30, 0x7E, + 0x67, 0xE2, 0x69, 0xF5, 0x20, 0xD9, 0xE0, 0x87, + 0xAB, 0xF6, 0x70, 0xBE, 0xF1, 0x26, 0x25, 0x01, + 0x14, 0x69, 0xE8, 0x8E, 0xA9, 0x8E, 0xB2, 0xAE, + 0xA5, 0x2C, 0x5F, 0x9D, 0x0C, 0x50, 0xC6, 0x95, + 0xC1, 0xDE, 0xEC, 0x7D, 0xEA, 0x87, 0xCA, 0x02, + 0xD7, 0x3F, 0x5E, 0x8A, 0xB3, 0x3A, 0xDA, 0x6A, + 0x33, 0xAD, 0x06, 0xD4, 0x6D, + + 0xAB, 0x89, 0xF3, 0x78, 0x67, 0x63, 0xD5, 0xFC, + 0xF2, 0x19, 0xD5, 0x91, 0x5C, 0x10, 0x86, 0x9F, + 0xD2, 0xDC, 0x46, 0xD9, 0xC7, 0xAA, 0x4E, 0x44, + 0xFF, 0x79, 0x5B, 0x8A, 0xE3, 0x47, 0x92, 0x20, + 0xA8, 0xA3, 0x5C, 0x95, 0xEF, 0xD2, 0x21, 0x49, + 0x8C, 0x66, 0x0D, 0xCD, 0xD2, 0x1D, 0x11, 0x22, + 0x31, 0x62, 0x47, 0xC9, 0xD4, 0xC1, 0xBC, 0xB3, + 0x80, 0x84, 0xB4, 0x71, 0xC1, 0xAB, + + 0x85, 0x76, 0x7F, 0x0D, 0x73, 0xE2, 0x6C, 0xB5, + 0x98, 0x1F, 0x7E, 0x09, 0xFC, 0x2D, 0xD6, 0xA4, + 0xE7, 0x57, 0xDC, 0x14, 0xCB, 0xD9, 0x7D, 0x46, + 0xC9, 0x6E, 0xE7, 0x61, 0x0A, 0x5F, 0x3D, 0xAB, + 0x4D, 0x39, 0x77, 0x96, 0xF5, 0xB0, 0x0C, 0x53, + 0x21, 0xBB, 0xA2, 0xAC, 0xA9, 0xC6, 0x91, 0xB9, + 0xF8, 0x03, 0xE2, 0xDD, 0xEE, 0x3B, 0xB7, 0xEB, + 0xD8, 0xB7, 0xCE, 0xAC, 0x02, 0xA1, 0xA8, + + 0xAA, 0xBE, 0xDD, 0x59, 0x61, 0xF8, 0xD1, 0x11, + 0x7A, 0xC2, 0x81, 0x7E, 0x61, 0x58, 0xAF, 0xA2, + 0x67, 0xC1, 0xA2, 0x8F, 0xE1, 0xC7, 0x0D, 0x27, + 0x2B, 0x4A, 0x9A, 0x6A, 0x77, 0x68, 0xFD, 0x68, + 0x18, 0xF1, 0xDB, 0x55, 0xBA, 0xEC, 0x2A, 0x37, + 0x7F, 0xD2, 0x68, 0xA7, 0xEF, 0x31, 0x77, 0x97, + 0xDE, 0x6B, 0x56, 0xB1, 0x5E, 0xBE, 0xBD, 0x51, + 0x07, 0x3E, 0xD6, 0xD9, 0x01, 0x64, 0xB8, 0x65, + + 0x52, 0x67, 0x5D, 0x5E, 0x11, 0xDA, 0x06, 0xC2, + 0xA6, 0x8E, 0x41, 0xE4, 0x30, 0x59, 0x2F, 0x73, + 0x40, 0x03, 0x88, 0x85, 0xE6, 0x91, 0x29, 0xCB, + 0xA6, 0x68, 0x56, 0xCF, 0x05, 0xE4, 0xD7, 0xA2, + 0x46, 0xC6, 0xC1, 0x06, 0x50, 0x0E, 0x1F, 0xAA, + 0xC2, 0xB2, 0x6E, 0x2F, 0xB9, 0x1A, 0x9F, 0xC3, + 0xC0, 0xD8, 0xD7, 0xF6, 0xCD, 0xD8, 0x92, 0xFB, + 0x6A, 0x4F, 0xFA, 0x2D, 0x0E, 0x13, 0x01, 0xD8, + 0x9A, + + 0x6B, 0xA5, 0xF2, 0x8D, 0x73, 0x10, 0x6F, 0x06, + 0x2B, 0x8B, 0x10, 0x56, 0xF6, 0x5F, 0xC0, 0x94, + 0x1C, 0x70, 0xB3, 0xBE, 0xEE, 0x00, 0x4D, 0x50, + 0x94, 0x88, 0xC5, 0x51, 0x01, 0x8B, 0xE8, 0xA5, + 0xF8, 0x89, 0x0F, 0x97, 0x7F, 0x0C, 0x67, 0x6F, + 0xC7, 0x1F, 0x91, 0x20, 0xD7, 0x32, 0x9C, 0x63, + 0xFF, 0xE7, 0xB8, 0x63, 0x0F, 0xE1, 0xCE, 0xC7, + 0xEA, 0x82, 0x1D, 0x57, 0xEC, 0xB9, 0xF9, 0x05, + 0x8D, 0x94, + + 0xFE, 0x5B, 0xB2, 0x66, 0xD8, 0xDF, 0x19, 0x49, + 0x59, 0x8E, 0xBC, 0xC5, 0xC7, 0xD1, 0x6F, 0x0A, + 0x8A, 0x5B, 0x0C, 0xF4, 0xC2, 0x35, 0x62, 0x61, + 0x89, 0x2E, 0x43, 0x70, 0x7D, 0x5A, 0xE7, 0xF1, + 0x63, 0x92, 0xDD, 0xD4, 0xBD, 0x61, 0x93, 0x8C, + 0x21, 0x13, 0x96, 0xBA, 0xDB, 0x7A, 0x1B, 0x81, + 0x43, 0x6B, 0x3F, 0x2C, 0x2E, 0x6B, 0xB2, 0x49, + 0x2E, 0x28, 0xB2, 0x79, 0x52, 0x66, 0x89, 0x48, + 0xE7, 0x97, 0x44, + + 0x4C, 0xAD, 0x82, 0xE3, 0x09, 0xEA, 0x08, 0x73, + 0xF4, 0x16, 0x4D, 0x67, 0x33, 0x17, 0xB0, 0x47, + 0x16, 0xA2, 0xAF, 0x99, 0xFE, 0x5A, 0x45, 0x70, + 0x5C, 0xB2, 0xF2, 0x7A, 0x64, 0xC9, 0x65, 0xE3, + 0x07, 0xAD, 0x8D, 0x77, 0x3F, 0x10, 0x9E, 0xCE, + 0xC5, 0x63, 0xA2, 0x67, 0xE3, 0xFE, 0x48, 0xAC, + 0x9A, 0x8F, 0x9E, 0xB9, 0x63, 0xB2, 0x2B, 0x59, + 0xF1, 0x53, 0x18, 0x95, 0xA8, 0x12, 0x28, 0xAF, + 0xCF, 0x97, 0x9A, 0xC9, + + 0x5C, 0x23, 0xC9, 0xB0, 0x2F, 0x8E, 0x8B, 0x75, + 0xBC, 0xD3, 0x76, 0x22, 0xD0, 0x83, 0x85, 0x0A, + 0xA8, 0xC1, 0xF6, 0x06, 0xD5, 0x80, 0x59, 0xEB, + 0xF5, 0xD2, 0xBF, 0x35, 0x76, 0x64, 0xF6, 0x40, + 0x2B, 0xC4, 0x59, 0xA4, 0x8A, 0x21, 0x78, 0x77, + 0xBD, 0x4C, 0x2D, 0x11, 0x24, 0x31, 0x33, 0xF4, + 0x8D, 0x89, 0x61, 0xA4, 0xD9, 0x74, 0xEC, 0xEA, + 0xC9, 0x90, 0x9C, 0x79, 0x5D, 0x4B, 0x9E, 0x92, + 0xB5, 0x0D, 0x94, 0xE4, 0xCE, + + 0xAC, 0xE3, 0xD3, 0xD0, 0x7C, 0x51, 0x6F, 0xD7, + 0x73, 0xC9, 0x82, 0x6B, 0x9A, 0x6C, 0x8D, 0x56, + 0xB4, 0x17, 0xB7, 0xD8, 0xAD, 0x80, 0xA7, 0xFC, + 0xCA, 0xD5, 0x5B, 0x8E, 0x3A, 0xE3, 0x4B, 0x6B, + 0xFD, 0x75, 0x1B, 0xD1, 0xE5, 0xBA, 0x64, 0xD8, + 0xC4, 0x29, 0x83, 0xF8, 0x18, 0x98, 0xA2, 0x96, + 0x10, 0xB2, 0x3E, 0x60, 0x04, 0xE9, 0x9A, 0x68, + 0x2D, 0xFD, 0x41, 0x6A, 0xB7, 0xB4, 0xB8, 0x81, + 0x08, 0x54, 0x7A, 0xBF, 0xD1, 0x91, + + 0xA9, 0xB0, 0xF9, 0x25, 0x66, 0xC3, 0xF5, 0x4D, + 0x9A, 0x9A, 0x18, 0x15, 0x8E, 0x06, 0x8E, 0xC3, + 0x75, 0x7B, 0x99, 0xE2, 0x07, 0x49, 0x41, 0x19, + 0xA1, 0x7E, 0x3E, 0x13, 0xED, 0x3E, 0xB9, 0x66, + 0x89, 0x37, 0x37, 0xE3, 0xBC, 0xC4, 0xA7, 0xA5, + 0xB4, 0x5A, 0x57, 0x1B, 0xF7, 0xAD, 0x54, 0x13, + 0xF5, 0x4F, 0x80, 0x3F, 0xF9, 0x02, 0x89, 0xCA, + 0xFE, 0x2F, 0xE3, 0xC5, 0x63, 0xB6, 0x4D, 0x72, + 0x95, 0x25, 0x51, 0xFE, 0xD7, 0x4C, 0x7B, + + 0xB9, 0x14, 0xD1, 0xAD, 0x98, 0x4B, 0x37, 0x28, + 0x4B, 0x73, 0xFE, 0x28, 0x3F, 0x0B, 0x83, 0xC4, + 0xB6, 0xED, 0x8D, 0x4E, 0x25, 0x12, 0xB5, 0x41, + 0xB3, 0xAC, 0x2C, 0x2A, 0x8B, 0x5C, 0x3D, 0xE6, + 0x69, 0xE1, 0x99, 0x18, 0x31, 0x78, 0xCB, 0xAC, + 0xA8, 0xE7, 0x7F, 0x44, 0xFF, 0x87, 0xBB, 0x92, + 0xBD, 0x98, 0xEF, 0xD3, 0x86, 0x1B, 0xD3, 0xB5, + 0xB2, 0x78, 0x56, 0x1D, 0x7A, 0x0C, 0x4D, 0xC7, + 0x15, 0xA1, 0x68, 0x10, 0x85, 0x19, 0x39, 0x9A, + + 0x4B, 0x01, 0x86, 0x3D, 0x0D, 0xB2, 0x31, 0x1F, + 0xA1, 0xCA, 0x12, 0x0C, 0x59, 0x69, 0xEC, 0x14, + 0x62, 0xC9, 0x17, 0xC2, 0x61, 0xF1, 0x60, 0x2B, + 0x8C, 0x46, 0x80, 0x62, 0x9E, 0x7E, 0xE4, 0x21, + 0xC3, 0x3F, 0xF7, 0x4C, 0x31, 0xCE, 0x8B, 0x4C, + 0x87, 0x48, 0xD9, 0x60, 0x39, 0xFD, 0xFC, 0x12, + 0x15, 0x2F, 0x35, 0x74, 0x79, 0xEB, 0x87, 0x20, + 0x72, 0x23, 0xE6, 0x66, 0x5A, 0xD4, 0xFB, 0x72, + 0xA9, 0x6B, 0xDB, 0xB4, 0x30, 0xFD, 0x80, 0xF5, + 0xBB, + + 0xBC, 0xD6, 0x2A, 0xC2, 0x2E, 0x81, 0x71, 0xF7, + 0xF9, 0x29, 0x5B, 0xBB, 0x8C, 0x84, 0xB3, 0xB2, + 0x47, 0xF4, 0xBB, 0x9C, 0x82, 0xC0, 0x35, 0xA2, + 0xEA, 0xA7, 0x4E, 0xA5, 0x5E, 0xD5, 0x0D, 0xDF, + 0x89, 0x3E, 0xC1, 0x3F, 0x8B, 0x82, 0xE5, 0xEB, + 0x4D, 0x87, 0xD9, 0x73, 0xAC, 0x06, 0x8A, 0xB4, + 0xF3, 0x2C, 0x83, 0xCB, 0x08, 0xB5, 0xB2, 0x74, + 0x16, 0xD0, 0x97, 0x87, 0x53, 0x6C, 0x73, 0x7B, + 0x20, 0x0D, 0x18, 0x7F, 0x3A, 0xD6, 0x3D, 0xF0, + 0x9E, 0x00, + + 0xE9, 0x20, 0xCF, 0x62, 0x26, 0x4F, 0xA8, 0xA2, + 0x8C, 0xCC, 0x32, 0xDE, 0x6B, 0xA5, 0x44, 0xB7, + 0xD0, 0x25, 0x48, 0x4B, 0xB7, 0x10, 0x61, 0x3B, + 0x93, 0xFB, 0xFB, 0x57, 0x02, 0xBC, 0x02, 0xD2, + 0x8E, 0x33, 0x08, 0xBF, 0xA2, 0x02, 0xB4, 0xDF, + 0x86, 0x33, 0xCD, 0xCD, 0x7C, 0x3B, 0x8D, 0xDB, + 0x90, 0x3A, 0xF3, 0x55, 0x82, 0x73, 0x5F, 0x27, + 0x8D, 0xED, 0x06, 0x08, 0x18, 0x27, 0xAF, 0xF8, + 0x7B, 0x01, 0x4E, 0xB0, 0xF2, 0xE8, 0xD9, 0x73, + 0x43, 0x59, 0xB0, + + 0x92, 0x47, 0x3D, 0x62, 0x6D, 0x82, 0xB4, 0x89, + 0x35, 0xCB, 0x4A, 0x66, 0x24, 0xF1, 0x14, 0x40, + 0xA6, 0xAD, 0xE1, 0xD4, 0xC8, 0xFB, 0x08, 0x90, + 0xFC, 0x33, 0xA8, 0x2E, 0xCF, 0x38, 0x4C, 0x45, + 0xF8, 0x8D, 0x4B, 0x11, 0xF8, 0x7F, 0xD8, 0x4F, + 0x22, 0x2D, 0x7B, 0x38, 0x8C, 0x8F, 0xDF, 0x8B, + 0xF1, 0x4B, 0xCF, 0x46, 0xE5, 0xCC, 0x8D, 0x0A, + 0x7A, 0x0E, 0xF8, 0xDE, 0xCE, 0x10, 0xCA, 0xCD, + 0xA8, 0x30, 0x0A, 0x63, 0x0E, 0xA0, 0x32, 0x54, + 0x5C, 0x77, 0x49, 0xDC, + + 0xED, 0xA1, 0x20, 0xA9, 0x5E, 0x2A, 0xBC, 0xBC, + 0x1A, 0x8D, 0x48, 0x71, 0x4E, 0x3A, 0xFA, 0xEA, + 0x8F, 0x78, 0x3F, 0xA6, 0x61, 0x28, 0xC3, 0xC9, + 0x01, 0xCF, 0xD4, 0x2E, 0x36, 0x40, 0x90, 0xA8, + 0xCA, 0x45, 0x40, 0xFE, 0x9E, 0xB6, 0x40, 0x28, + 0xFB, 0x28, 0x3E, 0x07, 0xFC, 0xA1, 0x1A, 0x8D, + 0xA8, 0x5B, 0xB1, 0x1A, 0xE3, 0x18, 0xDC, 0xB3, + 0x19, 0x14, 0xF9, 0xB9, 0x9C, 0xF2, 0x15, 0xE2, + 0x70, 0xD5, 0x74, 0xFF, 0x1E, 0xED, 0x92, 0x0C, + 0xC9, 0xE7, 0x57, 0x55, 0x8B, + + 0xD5, 0x15, 0x63, 0x0A, 0x41, 0xF9, 0x2B, 0x15, + 0x55, 0x1E, 0x63, 0x96, 0xC3, 0xF0, 0x21, 0x90, + 0xFB, 0xB9, 0xC7, 0xFE, 0xEE, 0x15, 0x2C, 0x28, + 0xBB, 0x2C, 0x9D, 0x6F, 0x8E, 0xAB, 0x73, 0xD3, + 0x03, 0x39, 0xE9, 0xB4, 0xEC, 0xED, 0x48, 0x1F, + 0xDA, 0x87, 0x29, 0xA1, 0x4B, 0xD7, 0xC8, 0xCD, + 0xDB, 0xF0, 0xCF, 0x82, 0x54, 0x27, 0xBF, 0x20, + 0xF4, 0xD4, 0x52, 0xED, 0x64, 0xA2, 0x4A, 0x6D, + 0xC8, 0x78, 0x1E, 0x83, 0x03, 0xBA, 0x22, 0x9C, + 0xFC, 0xB1, 0xE0, 0x67, 0xC7, 0x23, + + 0x62, 0xB2, 0xA4, 0xD4, 0xCD, 0xAE, 0xD3, 0xF1, + 0xFB, 0x71, 0x5C, 0xDD, 0xD6, 0x51, 0x91, 0x9D, + 0x48, 0xBD, 0x3C, 0xF9, 0x0C, 0x05, 0xEB, 0xF8, + 0x55, 0x48, 0x03, 0xE6, 0x9C, 0x13, 0x9E, 0xB8, + 0x6B, 0xB5, 0x09, 0xB3, 0x89, 0x04, 0x96, 0xFD, + 0x1C, 0xE8, 0x0F, 0xC9, 0x19, 0x85, 0xE4, 0xCC, + 0xBB, 0xDB, 0xC3, 0x15, 0x69, 0xD9, 0xA1, 0x91, + 0x9B, 0x16, 0xE0, 0xD0, 0x74, 0x3F, 0xF4, 0x2D, + 0x33, 0x74, 0x02, 0x1D, 0xE3, 0xDB, 0xF8, 0xD3, + 0x33, 0xD9, 0x2F, 0x77, 0x94, 0x6B, 0x20, + + 0x86, 0x99, 0xFC, 0xEF, 0x12, 0x8E, 0x6E, 0x84, + 0x7A, 0x25, 0xD3, 0xB6, 0x37, 0xEE, 0x42, 0x0D, + 0x88, 0xE2, 0x53, 0xD7, 0x59, 0x4A, 0x73, 0x67, + 0xA6, 0x20, 0xA3, 0x91, 0x7C, 0x1F, 0xCA, 0x18, + 0xC9, 0xEF, 0xD1, 0xD0, 0xE0, 0xC7, 0x23, 0x14, + 0x03, 0x05, 0x7F, 0x84, 0xCD, 0xDB, 0xDB, 0x2D, + 0x31, 0x33, 0xBE, 0xD5, 0x1A, 0xDC, 0xC2, 0xBE, + 0x6C, 0x9B, 0x35, 0xFB, 0xAD, 0xC1, 0xDE, 0xDC, + 0xDC, 0xB9, 0xDE, 0xF5, 0x38, 0xCF, 0x06, 0x95, + 0x53, 0x24, 0x47, 0x9F, 0xB8, 0x41, 0x84, 0x9B, + + 0x13, 0x83, 0x54, 0x9F, 0x66, 0xE7, 0x40, 0x2D, + 0xD9, 0xB5, 0xB9, 0xAC, 0xB0, 0x08, 0x06, 0x54, + 0x50, 0x34, 0x07, 0x8A, 0x2E, 0x0B, 0xF2, 0x06, + 0x85, 0xF2, 0x8C, 0xC1, 0x24, 0x2D, 0x9F, 0x66, + 0x9A, 0x94, 0x93, 0x0E, 0xFA, 0x5E, 0xCC, 0x1D, + 0x74, 0xAE, 0x8F, 0xAA, 0x51, 0x0E, 0x7F, 0xD4, + 0x61, 0xA2, 0x29, 0xAB, 0xE9, 0x70, 0x8F, 0x99, + 0xFF, 0x11, 0x38, 0x84, 0x63, 0x6D, 0x91, 0xB8, + 0xF8, 0x08, 0x01, 0x08, 0x65, 0x39, 0xCF, 0x42, + 0x34, 0x29, 0x36, 0xDE, 0x29, 0xE9, 0x44, 0xFE, + 0x7E, + + 0x13, 0x26, 0xF8, 0xB2, 0xD9, 0x68, 0xE5, 0x81, + 0x20, 0xE6, 0xA3, 0xC8, 0x34, 0x43, 0x4D, 0xCE, + 0xA7, 0x87, 0x10, 0x98, 0x35, 0x91, 0x70, 0xF0, + 0x1D, 0xA4, 0xFB, 0x73, 0x00, 0x8E, 0x15, 0x49, + 0x14, 0xF8, 0xE8, 0x11, 0xA6, 0xBD, 0xEE, 0x7D, + 0x51, 0x49, 0x52, 0x5F, 0x0D, 0x63, 0x8D, 0x52, + 0x40, 0x4D, 0xF5, 0x40, 0x15, 0x04, 0x7C, 0xE2, + 0x23, 0xCE, 0x1F, 0x37, 0x15, 0x2C, 0xDF, 0x92, + 0xDC, 0xF7, 0xAB, 0xA5, 0x2B, 0xE8, 0x8C, 0xF9, + 0xF1, 0xBC, 0x42, 0x79, 0xE0, 0x6B, 0xBF, 0xEA, + 0x74, 0xBA, + + 0xB4, 0x5B, 0x74, 0xCE, 0x30, 0x5A, 0x8C, 0x7B, + 0xCE, 0x65, 0x3E, 0x64, 0xEE, 0x0D, 0xC8, 0x91, + 0x2E, 0x57, 0x6B, 0xE6, 0xD1, 0x95, 0xCC, 0x64, + 0xB6, 0x0E, 0x67, 0xD2, 0xB3, 0x52, 0x78, 0x18, + 0x1E, 0xE6, 0x49, 0x32, 0x61, 0x68, 0x94, 0x19, + 0x98, 0x54, 0xDB, 0x92, 0x8B, 0x9B, 0x4E, 0x75, + 0x93, 0x72, 0x59, 0x63, 0x8D, 0x3F, 0x2F, 0x9C, + 0x4D, 0xC6, 0x94, 0x60, 0xDD, 0xD9, 0xB3, 0xB4, + 0x04, 0xC0, 0xBC, 0x49, 0x94, 0x01, 0x61, 0x1B, + 0xF0, 0xB6, 0x94, 0xCF, 0x21, 0xE6, 0x96, 0x77, + 0xDF, 0xB9, 0x13, + + 0xB2, 0xEB, 0xE5, 0x13, 0x22, 0xCD, 0x5F, 0x6C, + 0x28, 0xB3, 0xCD, 0x37, 0x6A, 0xCC, 0xC8, 0x59, + 0xBC, 0x0A, 0x44, 0xC0, 0x53, 0xB7, 0xD9, 0x1F, + 0x6A, 0xD2, 0x7D, 0x34, 0xB8, 0x9F, 0xBF, 0xA7, + 0xF2, 0x87, 0x09, 0xF7, 0xE0, 0xDE, 0x43, 0x66, + 0xD8, 0xAF, 0x04, 0x03, 0x2A, 0x48, 0xBC, 0x70, + 0x49, 0x34, 0x05, 0x03, 0x85, 0xFC, 0x30, 0x78, + 0xE5, 0x2A, 0x30, 0x65, 0x95, 0x62, 0xD6, 0x0B, + 0x5C, 0xD6, 0x97, 0x4C, 0x65, 0x0A, 0x94, 0xB5, + 0x2E, 0xF7, 0x86, 0x30, 0x63, 0x4F, 0xA8, 0x72, + 0xAD, 0xF4, 0x19, 0xFB, + + 0x9B, 0x61, 0xE9, 0xE9, 0x8D, 0xB0, 0x02, 0x06, + 0x4D, 0x1C, 0x28, 0x5C, 0x65, 0x7E, 0x89, 0x02, + 0x47, 0x76, 0x1A, 0x1E, 0xEC, 0x64, 0xBA, 0x8D, + 0x59, 0xF0, 0xC5, 0xCE, 0x89, 0xCF, 0x11, 0x5E, + 0x3A, 0xBF, 0x8A, 0x32, 0x63, 0xFB, 0xD8, 0xC7, + 0x56, 0xA8, 0x6F, 0xF3, 0x2B, 0xC8, 0x5B, 0x8F, + 0xEC, 0x2C, 0x91, 0x35, 0x60, 0xE4, 0x84, 0x09, + 0x2F, 0x36, 0x8E, 0x4F, 0xFB, 0x06, 0xBD, 0x53, + 0xC4, 0x8A, 0x6B, 0x3B, 0xFA, 0x04, 0x50, 0x15, + 0x17, 0x63, 0x46, 0x9C, 0x54, 0xF6, 0xB6, 0xA9, + 0xEA, 0xE5, 0x60, 0x5E, 0x75, + + 0xC6, 0x4D, 0xA8, 0x7B, 0xC5, 0xC5, 0xDA, 0x2B, + 0xBB, 0x19, 0xA2, 0x36, 0xBE, 0x76, 0xA7, 0x34, + 0x92, 0xD4, 0xE2, 0xA5, 0xF3, 0xF1, 0xF8, 0xBE, + 0xA0, 0xA3, 0xB8, 0xE2, 0x3C, 0x39, 0x34, 0x84, + 0xE5, 0xF7, 0x48, 0xF3, 0xD1, 0x4F, 0x4E, 0xC2, + 0x4E, 0xB3, 0xC2, 0xBB, 0xE0, 0x1E, 0xAA, 0xBF, + 0x42, 0x62, 0x90, 0x4D, 0x62, 0xF5, 0x79, 0xE5, + 0x50, 0x25, 0x03, 0x37, 0x90, 0xAA, 0xF7, 0xFC, + 0xEB, 0xC0, 0x32, 0xDA, 0x1C, 0xD7, 0x1E, 0xA5, + 0x1C, 0x49, 0xF4, 0xE4, 0x94, 0x2D, 0x88, 0xA8, + 0x24, 0x7C, 0x0D, 0x1F, 0xB1, 0xA7, + + 0xE5, 0x1F, 0xCF, 0xE1, 0x7A, 0x7C, 0xF5, 0x66, + 0x6E, 0x92, 0x41, 0x68, 0x89, 0x5C, 0xD7, 0xB4, + 0x24, 0x06, 0xEE, 0x3D, 0x0D, 0xC7, 0xDF, 0xA9, + 0x48, 0x39, 0xCB, 0x8E, 0x79, 0xE9, 0xA8, 0x3D, + 0xB9, 0xFC, 0x97, 0xF1, 0x03, 0x06, 0xEB, 0xFB, + 0x14, 0x97, 0x96, 0xA1, 0xFB, 0x25, 0x12, 0x6D, + 0xA9, 0x2B, 0x0C, 0xC1, 0x27, 0x87, 0x4D, 0x17, + 0xDE, 0xEA, 0xD3, 0xEE, 0x3A, 0xA9, 0xD5, 0x80, + 0x52, 0x0E, 0xBB, 0x15, 0xA8, 0x16, 0x58, 0x25, + 0x7A, 0x6C, 0x55, 0x30, 0x80, 0xCE, 0x9B, 0x1E, + 0x97, 0x21, 0xD7, 0xEE, 0x5D, 0x4C, 0xE6, + + 0xEE, 0x3C, 0x03, 0x94, 0x28, 0xD8, 0xED, 0x96, + 0x2E, 0x67, 0x08, 0xEE, 0xDB, 0x31, 0xD9, 0x42, + 0x10, 0x51, 0x99, 0x56, 0x68, 0x60, 0xA6, 0x79, + 0x19, 0x7A, 0x54, 0xC3, 0xBD, 0x2A, 0x2D, 0x69, + 0xF1, 0xE2, 0xEF, 0xA7, 0x26, 0x3D, 0x9A, 0x6F, + 0x26, 0x8D, 0xE3, 0x0E, 0x3B, 0xFB, 0xCC, 0xFF, + 0x65, 0x09, 0xAD, 0x24, 0xEE, 0xA8, 0xD8, 0x55, + 0x83, 0xF5, 0xEA, 0x61, 0x07, 0x6F, 0x90, 0x54, + 0x3C, 0x1A, 0x8E, 0xB0, 0x89, 0x79, 0x8A, 0xA7, + 0x35, 0x05, 0x7B, 0xAF, 0xF0, 0xBC, 0x94, 0xB1, + 0x59, 0xF2, 0xB8, 0xAA, 0xC9, 0x92, 0x9B, 0x4A, + + 0xF6, 0xF8, 0x0F, 0x47, 0xEF, 0xEB, 0x09, 0xB2, + 0xB3, 0xC6, 0x0E, 0xE1, 0xA5, 0xA2, 0x62, 0x6D, + 0x44, 0x25, 0xEE, 0xEE, 0x35, 0xD0, 0x18, 0xBE, + 0x07, 0xC3, 0xAD, 0x9E, 0x8E, 0x93, 0xBB, 0x78, + 0xD7, 0x8F, 0x47, 0x6A, 0x27, 0x4C, 0x39, 0x18, + 0xD1, 0xC5, 0xA9, 0x9F, 0x31, 0x7F, 0x47, 0x6A, + 0x95, 0x35, 0x74, 0x6B, 0x1C, 0xD5, 0x33, 0x51, + 0x68, 0x58, 0x67, 0x89, 0xA8, 0x43, 0x9B, 0xE5, + 0xB4, 0x94, 0x5E, 0x62, 0xD5, 0x1C, 0xCB, 0x31, + 0x1E, 0x5C, 0xA8, 0x10, 0xA4, 0xC0, 0x62, 0xDE, + 0x19, 0xCC, 0x17, 0x09, 0x4A, 0x5B, 0x88, 0xC3, + 0xE4, + + 0xFB, 0xD9, 0x1D, 0x95, 0x93, 0x0F, 0xBA, 0xDB, + 0x70, 0x42, 0x47, 0x89, 0xB4, 0xF2, 0xCB, 0xF9, + 0x65, 0x5F, 0x8D, 0xC7, 0x44, 0x51, 0x39, 0x7F, + 0xCC, 0xD2, 0x8A, 0xA7, 0x14, 0xFE, 0xFC, 0x4A, + 0x36, 0x50, 0x6E, 0x18, 0xF9, 0xAF, 0x0E, 0xB5, + 0xEC, 0x1B, 0xCB, 0x66, 0xE0, 0x1A, 0x41, 0xC2, + 0xA8, 0x5F, 0x62, 0x35, 0xE5, 0x9C, 0xCB, 0x35, + 0x40, 0x51, 0x54, 0x25, 0xA6, 0xAB, 0x3D, 0x87, + 0x13, 0x58, 0x6C, 0x39, 0xEA, 0x09, 0xDE, 0xF4, + 0xD9, 0x8D, 0x55, 0xF3, 0xBF, 0x18, 0xDC, 0xD5, + 0x9E, 0xD2, 0xCE, 0x9D, 0x38, 0x99, 0x16, 0xB3, + 0x67, 0xAB, + + 0xFF, 0x15, 0x86, 0xD2, 0x9C, 0x6E, 0xBB, 0xC6, + 0x3F, 0xB4, 0x57, 0x9D, 0x0C, 0x84, 0xCF, 0xE3, + 0xAB, 0x55, 0xA6, 0x11, 0x6A, 0xBB, 0xB8, 0x3A, + 0x4C, 0x18, 0x53, 0x53, 0x54, 0xAE, 0x1F, 0x48, + 0x81, 0x98, 0x10, 0x13, 0x95, 0xE5, 0xF7, 0xC9, + 0x7A, 0xE5, 0x08, 0x3D, 0xD2, 0xE3, 0xE0, 0x63, + 0x08, 0x2C, 0x53, 0x35, 0x4A, 0xFA, 0xB1, 0x33, + 0x8C, 0xE7, 0x4B, 0xE7, 0x56, 0x9C, 0x97, 0x26, + 0xC8, 0xD7, 0x8F, 0x37, 0xF0, 0x8C, 0x69, 0x30, + 0x6C, 0x6B, 0xE9, 0x50, 0x03, 0xDC, 0xF3, 0x06, + 0x02, 0x6B, 0x73, 0x97, 0xF3, 0xC8, 0x51, 0x90, + 0x56, 0xEE, 0x6B, + + 0xD1, 0x92, 0x49, 0xBE, 0x13, 0xB9, 0x0D, 0xC3, + 0x94, 0x48, 0xF7, 0xAE, 0x8D, 0xB3, 0xCD, 0x40, + 0xBB, 0x01, 0x2F, 0x5E, 0x65, 0xBF, 0x15, 0xE7, + 0xC9, 0x23, 0xD8, 0x8A, 0x31, 0x92, 0xD4, 0x89, + 0x03, 0x92, 0x64, 0x77, 0xC4, 0x02, 0xA1, 0xC8, + 0x06, 0xA5, 0x46, 0xD8, 0x83, 0x19, 0x9B, 0x49, + 0x2C, 0x77, 0x14, 0x1C, 0x37, 0xEC, 0xBC, 0x16, + 0x16, 0x90, 0x38, 0x5D, 0x7B, 0x62, 0xC7, 0x5E, + 0xB9, 0x90, 0x4C, 0x86, 0xB1, 0x04, 0x3F, 0x9B, + 0x97, 0xFF, 0x96, 0x96, 0xF8, 0x8C, 0xED, 0x30, + 0x29, 0x97, 0xCE, 0x39, 0x08, 0xEA, 0xCE, 0xEC, + 0xE4, 0xD5, 0x97, 0x80, + + 0xED, 0x40, 0x76, 0x4D, 0x2A, 0x83, 0x9E, 0xB9, + 0x0E, 0xA6, 0x9B, 0xFF, 0x43, 0xD3, 0x57, 0x22, + 0xA1, 0xE5, 0x19, 0x44, 0x8A, 0x2C, 0x2B, 0xA5, + 0xE0, 0x58, 0x00, 0xEA, 0xDE, 0xD7, 0x77, 0x95, + 0x98, 0x02, 0x43, 0xC8, 0xAC, 0x9E, 0x10, 0x49, + 0xDD, 0xFC, 0x0D, 0x6B, 0x69, 0x0C, 0x5F, 0xA6, + 0xBC, 0x11, 0xCB, 0xB0, 0x40, 0x8C, 0xFD, 0xFD, + 0x28, 0xEF, 0xEB, 0xAC, 0x73, 0xCD, 0x15, 0x74, + 0x49, 0x08, 0x5F, 0xE5, 0xCA, 0x39, 0x3E, 0x07, + 0xAB, 0xF0, 0xE8, 0xC3, 0x6F, 0xEC, 0x41, 0x50, + 0x29, 0x5A, 0xF8, 0xD8, 0x95, 0x68, 0x77, 0xA9, + 0x31, 0x9A, 0x5E, 0xB7, 0xFD, + + 0x4E, 0xB1, 0x03, 0x95, 0xE6, 0x22, 0x49, 0x89, + 0xDE, 0x26, 0x73, 0x65, 0x0B, 0xE2, 0x85, 0xA1, + 0xC5, 0x15, 0x5B, 0x5F, 0x8D, 0xC8, 0xB3, 0x15, + 0xCB, 0x39, 0xD8, 0xD8, 0x12, 0xEF, 0x83, 0x89, + 0x02, 0x81, 0x5A, 0x36, 0xB4, 0xAF, 0xE0, 0xA4, + 0xE3, 0x1E, 0x72, 0xB6, 0x80, 0x1A, 0xCA, 0xB9, + 0xF8, 0x64, 0x80, 0x62, 0x55, 0xA3, 0x50, 0x6F, + 0xD0, 0x4C, 0x37, 0xFC, 0xFE, 0x8D, 0xC6, 0xFE, + 0xD9, 0x6A, 0x6A, 0x1E, 0x44, 0x35, 0xCF, 0x6A, + 0x1F, 0xDC, 0x86, 0x22, 0x74, 0x5D, 0xC9, 0x6A, + 0xFA, 0x32, 0x12, 0xB3, 0x12, 0x8A, 0x9D, 0x29, + 0x6A, 0xB0, 0x10, 0x73, 0xF3, 0xAF, + + 0xAF, 0x25, 0xCA, 0x2F, 0xCC, 0xAC, 0xEB, 0x64, + 0x67, 0xCA, 0x4A, 0xC6, 0xE6, 0x6C, 0x98, 0xC8, + 0xB9, 0x12, 0xCF, 0xCD, 0x56, 0xC1, 0x01, 0x32, + 0x36, 0xEB, 0xE5, 0x58, 0xAC, 0xEC, 0x2D, 0xA6, + 0xF9, 0x95, 0x84, 0xD9, 0x21, 0x13, 0x81, 0xE7, + 0x8B, 0x46, 0x34, 0x3E, 0x29, 0xD7, 0x02, 0xAE, + 0xC7, 0x45, 0x38, 0xC1, 0x69, 0x70, 0xE9, 0x38, + 0x2C, 0x9D, 0x0F, 0xE1, 0x32, 0x48, 0x74, 0x65, + 0x8A, 0x20, 0x50, 0x2A, 0x5C, 0xF9, 0x6F, 0xD7, + 0xAC, 0x8A, 0xFB, 0x63, 0x24, 0x09, 0x6F, 0x9F, + 0xC0, 0x19, 0xA9, 0x84, 0x53, 0x2E, 0x4F, 0x25, + 0x22, 0x19, 0x99, 0x6D, 0x63, 0xD6, 0x2F, + + 0x06, 0x07, 0xEB, 0x05, 0xA8, 0x83, 0xC7, 0xFC, + 0x5B, 0x09, 0xBF, 0x80, 0xC9, 0x86, 0x1C, 0x98, + 0xDD, 0x25, 0xE9, 0xDB, 0x66, 0xC5, 0x49, 0x4F, + 0xF0, 0x12, 0xD6, 0x73, 0x04, 0xD7, 0x1B, 0xC6, + 0xFF, 0xEB, 0xB7, 0x34, 0x09, 0x74, 0x96, 0xB4, + 0xDD, 0xB2, 0x52, 0x3E, 0xB2, 0xDC, 0x9D, 0x4D, + 0x12, 0x77, 0x7C, 0x5B, 0xB1, 0x2D, 0x18, 0x35, + 0xD0, 0xCF, 0x22, 0x40, 0x2B, 0xEF, 0x91, 0x28, + 0xDC, 0x4A, 0xF8, 0xB3, 0xAE, 0x22, 0x7B, 0x9D, + 0x1C, 0x92, 0x19, 0xD2, 0x3E, 0x7D, 0xAF, 0xD5, + 0x31, 0x95, 0xB0, 0x9A, 0x52, 0xD2, 0xFE, 0x3F, + 0x23, 0x37, 0x22, 0xF5, 0xC6, 0xC4, 0x41, 0xCC, + + 0x67, 0x3B, 0x8B, 0xB9, 0x68, 0x4C, 0x7A, 0x96, + 0xA1, 0xB1, 0x89, 0x79, 0x49, 0x24, 0xC4, 0x12, + 0x42, 0x1A, 0x78, 0x6F, 0xE2, 0x7F, 0xB4, 0x92, + 0xCD, 0xC0, 0xAD, 0xF5, 0x2D, 0x87, 0x6D, 0x22, + 0x18, 0xFC, 0xCE, 0x69, 0xAA, 0xD8, 0x01, 0x2D, + 0x49, 0x64, 0x3C, 0x24, 0xEE, 0xFF, 0xCA, 0x32, + 0x34, 0xF5, 0x09, 0x5E, 0x90, 0x50, 0x35, 0xEB, + 0xB8, 0x2D, 0x16, 0x10, 0xAD, 0x38, 0x30, 0x3D, + 0x97, 0x07, 0xD0, 0x99, 0xCF, 0xF0, 0xA8, 0xBB, + 0x21, 0xB0, 0x54, 0xCB, 0x56, 0xC6, 0x72, 0x96, + 0x22, 0x58, 0x9A, 0x10, 0xAE, 0xA5, 0xD5, 0x52, + 0x0F, 0xB6, 0x3C, 0xF3, 0x16, 0x59, 0x3F, 0x46, + 0x56, + + 0xC3, 0xFC, 0x2F, 0xDE, 0xBC, 0xC5, 0xF4, 0xB3, + 0x17, 0x21, 0x88, 0xF7, 0xAB, 0x80, 0x06, 0x7A, + 0x36, 0x20, 0x97, 0x8A, 0x71, 0x16, 0xB9, 0x38, + 0xCF, 0x0E, 0x91, 0xC2, 0x4E, 0x38, 0x99, 0xC8, + 0x5B, 0xAA, 0xC0, 0xFC, 0xD4, 0x48, 0x85, 0x3F, + 0x41, 0xF0, 0x0A, 0x17, 0x2E, 0x32, 0xD3, 0xD4, + 0xF4, 0x43, 0xDE, 0x00, 0x0D, 0xE0, 0xA5, 0x81, + 0x2B, 0xB9, 0xF3, 0x9D, 0xE4, 0xA9, 0x29, 0xF1, + 0x25, 0x1F, 0x44, 0x7C, 0xBC, 0x84, 0xEA, 0xA1, + 0x39, 0xA3, 0x2F, 0x16, 0x7B, 0xEB, 0x47, 0xF6, + 0x33, 0x53, 0xD4, 0x4D, 0xFE, 0x63, 0x72, 0x4E, + 0x32, 0x72, 0x87, 0x1A, 0xA8, 0x94, 0x56, 0x7B, + 0xC0, 0x6E, + + 0xB9, 0x69, 0x4D, 0x32, 0x87, 0xD6, 0xC6, 0x8B, + 0xEC, 0xA1, 0xDF, 0xF8, 0x58, 0xB4, 0xCD, 0xCE, + 0x1E, 0xC5, 0x37, 0x4E, 0x9B, 0x77, 0x8F, 0x8A, + 0xEB, 0x89, 0x03, 0x4A, 0xA0, 0x0F, 0x51, 0x3C, + 0x6C, 0x1B, 0x14, 0x7D, 0x0D, 0x1D, 0xBF, 0xA1, + 0x8D, 0x59, 0x48, 0xA7, 0x95, 0x24, 0xED, 0x09, + 0x8D, 0xDF, 0x89, 0xCA, 0x80, 0xB7, 0x2D, 0x0D, + 0x71, 0xC7, 0x73, 0xFA, 0xB0, 0xFE, 0x70, 0xCE, + 0xEA, 0xA2, 0x53, 0x23, 0x07, 0x41, 0x5B, 0x75, + 0x44, 0x93, 0x0A, 0x36, 0xB3, 0x5A, 0x6C, 0xBD, + 0xDA, 0x66, 0x43, 0x55, 0x3C, 0x87, 0xC2, 0x8E, + 0x51, 0xA3, 0x28, 0xFB, 0xFB, 0x5F, 0x8E, 0x82, + 0xA9, 0x60, 0x5D, + + 0x7E, 0x26, 0x4B, 0x2B, 0x10, 0x93, 0x82, 0xAC, + 0x0B, 0x01, 0x88, 0x29, 0x90, 0x43, 0xA7, 0xAE, + 0xC5, 0xDE, 0xC6, 0xAB, 0x67, 0x49, 0x45, 0x6B, + 0x30, 0x5F, 0x87, 0x59, 0x4D, 0xB2, 0xE0, 0x8B, + 0xEC, 0x9F, 0x9D, 0xB6, 0x5E, 0x1A, 0xA8, 0xE8, + 0xB2, 0xF5, 0xC4, 0xDB, 0xC1, 0x39, 0xC6, 0xF4, + 0x85, 0xE8, 0x59, 0x19, 0xE8, 0xE8, 0xAA, 0xE6, + 0xA7, 0xE1, 0x66, 0x1B, 0x9A, 0xE3, 0x7C, 0xCE, + 0x5A, 0x7A, 0x3B, 0x32, 0x38, 0x89, 0x60, 0x6B, + 0xB8, 0xD9, 0xA1, 0xE2, 0x33, 0x62, 0xDA, 0xED, + 0xC0, 0xAF, 0x10, 0x23, 0xCA, 0xD5, 0x18, 0x5F, + 0x4F, 0xAC, 0x07, 0x02, 0x5D, 0x62, 0x4B, 0xCF, + 0x89, 0x5D, 0xB6, 0x8F, + + 0x4F, 0xB9, 0xC8, 0xBB, 0x61, 0x38, 0x62, 0xC0, + 0x7E, 0xDA, 0xBB, 0x97, 0x1F, 0x14, 0x93, 0x3D, + 0x56, 0x14, 0x06, 0xA0, 0xDA, 0xA6, 0x2F, 0x8D, + 0x61, 0xC6, 0xD3, 0x70, 0x2A, 0xF2, 0xEF, 0xD6, + 0x97, 0x81, 0x1B, 0x18, 0xCB, 0x5D, 0x3F, 0xC0, + 0xAB, 0xF1, 0xE8, 0x16, 0x59, 0xA3, 0x9E, 0xDC, + 0x59, 0x0D, 0x5D, 0x1C, 0x51, 0x81, 0xF6, 0x1A, + 0x12, 0x0C, 0xF7, 0xE3, 0xF0, 0x6E, 0x8D, 0x89, + 0x8D, 0xED, 0x1F, 0xE7, 0x4E, 0xE9, 0xB0, 0x9A, + 0x04, 0xB2, 0x9F, 0xB5, 0x05, 0xBB, 0xE2, 0x70, + 0x4F, 0x9F, 0x86, 0x03, 0x86, 0x9C, 0xE9, 0x63, + 0x8B, 0xA5, 0x8B, 0xFC, 0xD3, 0xF4, 0x24, 0xE1, + 0xE2, 0x46, 0xEB, 0x77, 0x2C, + + 0xD3, 0xE2, 0xB1, 0xBF, 0xE6, 0x28, 0xA2, 0x19, + 0x09, 0x7C, 0xE5, 0xDE, 0x6C, 0x07, 0x79, 0x55, + 0x36, 0xD2, 0x39, 0x8B, 0x1C, 0x1E, 0xBB, 0x67, + 0xF6, 0xE9, 0xA5, 0xD6, 0xCE, 0x83, 0x47, 0xFC, + 0x2D, 0x5D, 0xF3, 0x9D, 0xA8, 0x98, 0x78, 0x2B, + 0x2A, 0x4B, 0x1C, 0xC8, 0x7F, 0x45, 0x3F, 0x58, + 0x01, 0x35, 0xE4, 0x5C, 0xF8, 0xEE, 0x27, 0xC6, + 0x53, 0x88, 0x4B, 0x96, 0xDA, 0xBF, 0x57, 0x37, + 0x45, 0x2F, 0x4B, 0xDC, 0x57, 0x74, 0xD9, 0x69, + 0x15, 0x94, 0x60, 0x45, 0x72, 0xFA, 0x52, 0x05, + 0x2B, 0xA5, 0xF2, 0x85, 0x6F, 0x3C, 0xF6, 0xF1, + 0xF8, 0x4A, 0xAF, 0xF4, 0xC3, 0x0D, 0x6A, 0x8C, + 0xF3, 0xBC, 0x6A, 0x07, 0x02, 0x15, + + 0xBE, 0x63, 0x59, 0xA9, 0x6D, 0x8D, 0x63, 0xB4, + 0x80, 0xB1, 0xEA, 0xCD, 0x8B, 0xB3, 0x98, 0x8A, + 0x63, 0xF3, 0x0C, 0xD6, 0x93, 0xC0, 0x9E, 0x88, + 0x33, 0x19, 0x0F, 0xC0, 0xF0, 0xFE, 0xCC, 0x33, + 0xC7, 0xC5, 0xF0, 0xD6, 0x73, 0x6D, 0x57, 0xA4, + 0x4C, 0x49, 0x20, 0x10, 0x99, 0x68, 0x73, 0x99, + 0x38, 0xE9, 0x25, 0x11, 0x8B, 0xE1, 0xC8, 0x90, + 0x13, 0x6A, 0x96, 0x0F, 0x82, 0x23, 0xFD, 0x5E, + 0x71, 0x8D, 0x6E, 0xA4, 0x37, 0x7C, 0x35, 0xC6, + 0x37, 0xD3, 0xC8, 0xBE, 0x4B, 0x59, 0x94, 0xE2, + 0x5C, 0x70, 0xEB, 0x62, 0xC5, 0xF1, 0x7E, 0x63, + 0x02, 0xCE, 0xE4, 0x07, 0x38, 0x2E, 0x8F, 0x63, + 0x8D, 0xAC, 0xBB, 0x93, 0xAB, 0x8D, 0x71, + + 0x52, 0xBD, 0xA1, 0x06, 0x3C, 0x94, 0x40, 0x14, + 0x24, 0x66, 0x94, 0x84, 0x7F, 0x92, 0x07, 0x8C, + 0x02, 0xB4, 0x9A, 0x9D, 0x77, 0x91, 0xDC, 0xEC, + 0x69, 0xC6, 0x9F, 0xD5, 0x89, 0xDE, 0x3A, 0x55, + 0xF1, 0x8C, 0x2D, 0xAD, 0xBE, 0x56, 0x1B, 0x88, + 0x4D, 0xE1, 0x88, 0x8A, 0xEF, 0xFB, 0x2B, 0x1A, + 0xE0, 0xEF, 0xF3, 0x79, 0xED, 0x25, 0x23, 0xF3, + 0xAA, 0x1B, 0xE1, 0xF6, 0xB3, 0x0E, 0xC4, 0xA7, + 0x02, 0xBB, 0xCC, 0x25, 0x08, 0x67, 0x75, 0x23, + 0xE6, 0x0D, 0x5E, 0x4E, 0xE9, 0x00, 0xB3, 0xA8, + 0x7F, 0xB3, 0x42, 0x08, 0x7B, 0x36, 0xC8, 0x4A, + 0x67, 0xCA, 0xC3, 0x8E, 0xEC, 0x62, 0xB3, 0xA1, + 0x2E, 0x8A, 0xB6, 0x60, 0xE0, 0xD6, 0x38, 0xA9, + + 0x9E, 0x06, 0x4A, 0xB5, 0x71, 0xD7, 0x33, 0x84, + 0x95, 0x9C, 0x5F, 0xF8, 0x12, 0x0D, 0x46, 0xFA, + 0xE5, 0x67, 0x31, 0xE2, 0x71, 0xDC, 0xB6, 0xB4, + 0xE1, 0x3B, 0x16, 0x28, 0xE5, 0x04, 0x20, 0x90, + 0xC1, 0x70, 0xB3, 0x1C, 0x93, 0xF6, 0x6E, 0xC8, + 0x42, 0x8A, 0xAC, 0x85, 0x67, 0xB5, 0xAE, 0x74, + 0x78, 0xFA, 0x32, 0x4B, 0x0D, 0x74, 0x3B, 0x38, + 0xF9, 0x31, 0x01, 0xDA, 0x65, 0xE1, 0xBA, 0x5B, + 0x47, 0xE5, 0xCE, 0x58, 0xC6, 0x16, 0x6D, 0xC2, + 0x56, 0xB7, 0x9B, 0x43, 0x0B, 0x81, 0x65, 0xDD, + 0xF5, 0xB9, 0x44, 0x93, 0x06, 0xD6, 0x07, 0x8B, + 0xDF, 0xE7, 0x1F, 0x14, 0xD6, 0x04, 0x4F, 0xFD, + 0x47, 0x67, 0xEB, 0xBF, 0x23, 0xEB, 0xA7, 0xEE, + 0xB0, + + 0x31, 0xCF, 0x75, 0x5F, 0xFF, 0xBD, 0xB1, 0xE7, + 0x04, 0xBF, 0x32, 0x3F, 0x80, 0x8C, 0x1A, 0xCF, + 0x78, 0xF2, 0x95, 0x2B, 0x4B, 0xEF, 0x30, 0xDF, + 0xE4, 0x49, 0x04, 0xF3, 0x82, 0x11, 0x30, 0x6B, + 0x30, 0x3C, 0xAA, 0x9B, 0x1F, 0xDB, 0xFA, 0xDA, + 0xC3, 0x79, 0x88, 0x1F, 0x4C, 0x06, 0x7E, 0xE5, + 0x89, 0x04, 0x78, 0x7C, 0x0B, 0xA0, 0x2D, 0x50, + 0x88, 0xD4, 0xA8, 0x3C, 0x4D, 0x9F, 0x85, 0x9E, + 0x6A, 0x0C, 0xC6, 0x75, 0xE5, 0x02, 0x19, 0x64, + 0xA6, 0x64, 0x21, 0x76, 0x2F, 0x2E, 0xBC, 0xDA, + 0xF0, 0xDD, 0x19, 0xCE, 0xB1, 0xB6, 0x35, 0x3F, + 0x6B, 0x3F, 0xCF, 0xFD, 0x71, 0x2E, 0x0C, 0x4B, + 0x8B, 0x4D, 0x26, 0xDA, 0x06, 0x46, 0xE3, 0x68, + 0xBA, 0x22, + + 0xA3, 0x90, 0x81, 0xE3, 0xBA, 0x9C, 0x12, 0x57, + 0xE3, 0x53, 0x6E, 0x90, 0x2F, 0xD4, 0x6B, 0x23, + 0x58, 0xCD, 0x1C, 0xBD, 0x6F, 0x8B, 0x16, 0x0C, + 0x40, 0x41, 0x47, 0x86, 0xA4, 0xA2, 0x90, 0x6C, + 0x81, 0x8B, 0x51, 0x53, 0xC8, 0x9A, 0xF4, 0x4F, + 0x04, 0xB0, 0xF3, 0xE2, 0xDE, 0x73, 0x17, 0x3B, + 0xB3, 0xDB, 0x61, 0xFB, 0xBD, 0xC8, 0xBB, 0x13, + 0x8A, 0xE9, 0xB0, 0x5D, 0x38, 0x5F, 0x3F, 0x7A, + 0xDC, 0xB2, 0x25, 0xB7, 0x0B, 0xBD, 0xC6, 0xD6, + 0x8F, 0x15, 0xD5, 0x17, 0x2E, 0x84, 0xD0, 0xA6, + 0xA8, 0x72, 0xEA, 0x2D, 0x2C, 0xE9, 0x31, 0x8D, + 0x22, 0x6B, 0x0F, 0xD8, 0x36, 0x70, 0x3C, 0x11, + 0xBE, 0x32, 0x2D, 0xB1, 0x14, 0xCD, 0x90, 0x18, + 0x99, 0xCA, 0x01, + + 0xA9, 0x3C, 0xC6, 0x55, 0x5C, 0xA3, 0x2C, 0x02, + 0x51, 0xB8, 0x98, 0xF0, 0xDE, 0xF3, 0x51, 0x7A, + 0x1F, 0x6C, 0xA1, 0xCD, 0x15, 0xCB, 0x3B, 0x3A, + 0x7E, 0xD5, 0x3E, 0xF0, 0xD5, 0xE4, 0x6F, 0x30, + 0x3C, 0x1F, 0x92, 0x65, 0xEE, 0x40, 0xAC, 0x8F, + 0x8A, 0xA5, 0x5D, 0x26, 0x38, 0xCE, 0x02, 0xDE, + 0xA4, 0xF6, 0xC1, 0xB5, 0x53, 0x7B, 0x37, 0x40, + 0x38, 0xC6, 0x31, 0x9B, 0x3F, 0x72, 0x0D, 0x91, + 0x46, 0xD9, 0xE3, 0x17, 0xAD, 0x07, 0x1D, 0xDE, + 0x69, 0xF9, 0xBD, 0x72, 0x1E, 0x4E, 0x68, 0x4E, + 0x16, 0x48, 0xAD, 0xEA, 0x9C, 0x18, 0x70, 0xDF, + 0x7E, 0x38, 0x4F, 0x68, 0xC5, 0xE2, 0xA1, 0x09, + 0x5E, 0x5F, 0x42, 0x75, 0x66, 0x20, 0xE6, 0xE3, + 0x1F, 0xAC, 0x76, 0x6D, + + 0xCE, 0x61, 0xD7, 0xE9, 0x85, 0xCF, 0xEF, 0xDC, + 0x2D, 0x75, 0x04, 0x60, 0x76, 0xF1, 0x4E, 0xE9, + 0x72, 0xD3, 0x2E, 0x6A, 0x5A, 0x81, 0xB0, 0xAF, + 0x29, 0x05, 0xAF, 0x9C, 0xEF, 0xF1, 0xF3, 0x96, + 0x97, 0x10, 0xCA, 0x15, 0xA9, 0x73, 0x44, 0xFC, + 0x61, 0x0D, 0x2E, 0xE5, 0x1A, 0x3B, 0x24, 0x5E, + 0x04, 0xAE, 0x5B, 0xCA, 0x40, 0xEC, 0x4B, 0x92, + 0xD8, 0x7C, 0x3F, 0x0B, 0x69, 0x9B, 0x3C, 0xC4, + 0x9E, 0x80, 0x3C, 0x2B, 0xB1, 0x39, 0xFB, 0xC6, + 0x8A, 0x65, 0x98, 0x1A, 0x53, 0xE1, 0x31, 0x90, + 0xE6, 0x26, 0x43, 0x73, 0x07, 0xAD, 0x80, 0xD9, + 0x4C, 0xC0, 0x0E, 0x5F, 0xE7, 0x6D, 0x0B, 0xEB, + 0xD1, 0x16, 0x0A, 0x57, 0xFB, 0x49, 0xF5, 0xA0, + 0x96, 0x6E, 0x9A, 0xB0, 0x82, + + 0xC7, 0x84, 0x16, 0x76, 0x54, 0xAE, 0x96, 0x80, + 0x66, 0x9E, 0xCF, 0xB2, 0x49, 0x57, 0x8B, 0x32, + 0xDA, 0x1C, 0xCD, 0xD1, 0xA7, 0x44, 0x52, 0x19, + 0x5A, 0x77, 0x8D, 0x9D, 0x89, 0xC4, 0x84, 0xD0, + 0x2D, 0xFB, 0x8D, 0xB5, 0xCD, 0x63, 0x48, 0x6E, + 0x18, 0x49, 0x1B, 0xFA, 0x48, 0xD5, 0x32, 0xBB, + 0x85, 0x0A, 0x90, 0xBC, 0x6C, 0xAF, 0x5D, 0x6F, + 0xAC, 0x34, 0xF9, 0xC6, 0x59, 0x85, 0xE8, 0xC2, + 0xA2, 0xA4, 0x9F, 0x77, 0xE4, 0x00, 0xEE, 0x94, + 0xD9, 0x57, 0xEC, 0x31, 0x7E, 0x0F, 0xB1, 0xD2, + 0xD5, 0xA5, 0x1A, 0xBE, 0x0C, 0xC2, 0x85, 0x7B, + 0x5E, 0xCD, 0xD0, 0xB6, 0x1E, 0x04, 0x86, 0xEF, + 0x84, 0x74, 0x6B, 0x97, 0x5D, 0x6B, 0x5C, 0x63, + 0x81, 0x50, 0x6F, 0x4F, 0x8E, 0x1B, + + 0xC7, 0x9C, 0x57, 0x9D, 0xE5, 0x46, 0x4F, 0xC1, + 0x4A, 0x03, 0x63, 0x2E, 0x35, 0x1B, 0x35, 0x9D, + 0xD3, 0xA7, 0xB5, 0xDC, 0xCD, 0x58, 0xEB, 0x25, + 0x52, 0x9E, 0xC0, 0xB3, 0x19, 0x85, 0x11, 0x65, + 0x14, 0x8A, 0x27, 0x0F, 0xDD, 0xB5, 0x06, 0xCE, + 0x9A, 0x1A, 0xD8, 0xB5, 0x07, 0x63, 0x24, 0x5F, + 0xD2, 0xBC, 0x79, 0xB8, 0x62, 0x45, 0x10, 0xBC, + 0x65, 0x00, 0x22, 0x94, 0x83, 0x60, 0x62, 0xF3, + 0x13, 0xE1, 0xD0, 0x5D, 0x47, 0xD8, 0xCB, 0x2C, + 0x59, 0x60, 0x9D, 0x8F, 0x9A, 0x49, 0x7F, 0x60, + 0x71, 0x85, 0xA5, 0x53, 0xCE, 0x55, 0x72, 0x30, + 0x89, 0xD6, 0x74, 0x5B, 0xEE, 0xAF, 0x48, 0x60, + 0x1C, 0x0A, 0xD0, 0x56, 0x54, 0xC5, 0x40, 0x1B, + 0x1F, 0x94, 0xAE, 0x5E, 0x45, 0x60, 0x59, + + 0xD1, 0x73, 0xAA, 0xD6, 0x76, 0x3F, 0x35, 0xDE, + 0x77, 0x82, 0x96, 0xFF, 0x1B, 0x34, 0xAD, 0x16, + 0x77, 0x73, 0xD7, 0x8A, 0x96, 0x19, 0xA0, 0xBA, + 0x22, 0x71, 0x36, 0x9D, 0xAC, 0x41, 0x14, 0xAB, + 0xA8, 0x77, 0x34, 0x7A, 0x70, 0x2A, 0xF6, 0xF6, + 0xEA, 0xFD, 0x6D, 0xDB, 0xAD, 0x65, 0x14, 0x3A, + 0xEA, 0x0E, 0x2C, 0xC4, 0xC1, 0x13, 0x7D, 0x59, + 0x8D, 0xE8, 0x61, 0x29, 0x50, 0x61, 0xC3, 0xC9, + 0x76, 0x20, 0x7C, 0xEF, 0x09, 0xA8, 0xC3, 0x8E, + 0x11, 0xD2, 0x3F, 0x32, 0x09, 0x61, 0x91, 0xF8, + 0x60, 0xF6, 0x83, 0x93, 0x08, 0x94, 0xBF, 0x5D, + 0x6E, 0xDE, 0xA1, 0x04, 0xEE, 0xB2, 0xE3, 0x10, + 0x1D, 0x22, 0xF0, 0xF8, 0x4E, 0x5C, 0xF5, 0x9B, + 0x86, 0x44, 0x57, 0x28, 0xB7, 0x8C, 0x81, 0x14, + + 0x31, 0xDF, 0xA5, 0xD5, 0xF6, 0x17, 0x19, 0x50, + 0xA3, 0x0F, 0x26, 0x1C, 0xD4, 0x98, 0xCA, 0xB0, + 0xDB, 0x0C, 0x2F, 0x8C, 0xCE, 0xCC, 0x56, 0x35, + 0x57, 0xC2, 0x9D, 0xEF, 0x64, 0x82, 0xE4, 0x8A, + 0xFA, 0xFE, 0x07, 0xF1, 0xB7, 0x10, 0xDF, 0x56, + 0x7B, 0x64, 0x88, 0x9C, 0x25, 0xF1, 0x6D, 0x6C, + 0x5F, 0xD3, 0xC8, 0xC4, 0x7E, 0xD5, 0x26, 0xB8, + 0xC3, 0xDD, 0xAD, 0x85, 0x3E, 0xA0, 0x5C, 0x33, + 0x25, 0x9A, 0xD9, 0x52, 0xB7, 0x8C, 0x22, 0x3F, + 0x96, 0xD4, 0x51, 0x7C, 0x9F, 0xAD, 0x01, 0x81, + 0xBE, 0x95, 0x90, 0x38, 0x90, 0xDD, 0x81, 0x43, + 0xB0, 0x66, 0x0D, 0x4E, 0x9E, 0xF6, 0x0C, 0x13, + 0x47, 0xA5, 0xA0, 0x1F, 0x94, 0xFB, 0x86, 0x42, + 0x8B, 0x9B, 0x22, 0x25, 0xF2, 0xB8, 0x83, 0x3C, + 0x87, + + 0x06, 0xFA, 0x95, 0x25, 0x62, 0x35, 0x4D, 0x06, + 0x3F, 0xD2, 0xAE, 0x4E, 0x86, 0x32, 0x08, 0x54, + 0x78, 0x35, 0xAD, 0x84, 0xE1, 0x6D, 0xB1, 0x5E, + 0xD0, 0x91, 0x42, 0x02, 0x39, 0x1F, 0x0D, 0x4C, + 0xE5, 0x4B, 0xDB, 0x9D, 0x1A, 0xE5, 0x05, 0x97, + 0x90, 0x0F, 0xB4, 0xCD, 0xC5, 0xF7, 0xDA, 0x81, + 0xC7, 0x22, 0x25, 0xB8, 0xAA, 0xC5, 0xA6, 0x6C, + 0xCD, 0x21, 0xC3, 0xCA, 0xFA, 0xDB, 0x0C, 0xDC, + 0x35, 0x7B, 0x49, 0x2E, 0xC9, 0xD4, 0xDC, 0x2C, + 0xF5, 0x2A, 0x3E, 0xBA, 0x93, 0x52, 0x88, 0x73, + 0x4B, 0x3A, 0x1E, 0x5B, 0x37, 0xE4, 0x54, 0x8E, + 0xE7, 0x3C, 0x78, 0x37, 0xBA, 0x31, 0x09, 0x3A, + 0xE0, 0x36, 0xFF, 0x6C, 0x7D, 0xA9, 0x68, 0x0F, + 0x33, 0xCD, 0x1B, 0x7E, 0xD0, 0x12, 0x98, 0xC7, + 0xC6, 0x9E, + + 0x59, 0xBC, 0xD3, 0x93, 0xD5, 0x73, 0x5C, 0x4F, + 0xCD, 0x32, 0x8C, 0xA7, 0xEC, 0xB1, 0x92, 0x82, + 0xFF, 0x08, 0x94, 0xED, 0xCE, 0x65, 0x7E, 0xDD, + 0x28, 0x65, 0xEA, 0xDB, 0x3B, 0x77, 0x87, 0x41, + 0x5A, 0xE7, 0xE4, 0x4F, 0x5D, 0xC6, 0x8E, 0xB7, + 0xD5, 0x83, 0x5A, 0xA4, 0x1B, 0xE5, 0x40, 0x50, + 0xA1, 0x0E, 0xB3, 0x8F, 0xE3, 0x31, 0x87, 0xD8, + 0xBF, 0xD9, 0xBE, 0x07, 0x68, 0x9F, 0xAF, 0xD5, + 0x5A, 0xB3, 0xEB, 0x4B, 0x59, 0x0E, 0xDA, 0x36, + 0xE8, 0xCE, 0x29, 0x5A, 0x9D, 0x68, 0xF7, 0x40, + 0xAD, 0xD4, 0x4C, 0xEC, 0xD3, 0xB1, 0xF8, 0x13, + 0x5B, 0x4B, 0x9A, 0xE9, 0x7A, 0x19, 0x37, 0x21, + 0x35, 0x2F, 0x01, 0xE3, 0xF0, 0x7E, 0xF4, 0x80, + 0x3E, 0xBF, 0xB0, 0xDB, 0xB8, 0x5B, 0x46, 0xBF, + 0x81, 0xB2, 0x6D, + + 0xE6, 0x27, 0x7B, 0xD7, 0x3A, 0x57, 0x4A, 0x57, + 0x1F, 0x16, 0x60, 0x67, 0xE3, 0xCB, 0x95, 0x54, + 0x52, 0x0F, 0xD3, 0xF8, 0x2E, 0x4E, 0xFE, 0xAC, + 0x98, 0x99, 0x14, 0x6E, 0x24, 0x91, 0x5A, 0x2C, + 0xA1, 0xDE, 0x40, 0xBB, 0x2E, 0x59, 0x23, 0x98, + 0xCE, 0x15, 0x9A, 0xDC, 0x0D, 0x1B, 0xB2, 0xC4, + 0x6C, 0xC3, 0x23, 0xBE, 0xCB, 0xF3, 0xF6, 0x67, + 0xCF, 0x10, 0x5B, 0x90, 0xEE, 0xAC, 0x62, 0x54, + 0xCA, 0x9C, 0xD4, 0xD4, 0x83, 0x41, 0x1B, 0x46, + 0xD8, 0x42, 0x0B, 0x01, 0x7B, 0xC2, 0x1B, 0xD0, + 0x6A, 0x55, 0x1D, 0x2F, 0x2D, 0xF3, 0x63, 0xCD, + 0xAC, 0xCB, 0x30, 0xF0, 0x95, 0x7C, 0x9E, 0x37, + 0xA8, 0x37, 0x42, 0xA5, 0xAC, 0xCC, 0xA2, 0xFD, + 0x6E, 0x90, 0x0B, 0x64, 0x50, 0x38, 0x72, 0x7A, + 0x0B, 0x7E, 0x50, 0xFF, + + 0xAF, 0xB6, 0xBB, 0x4A, 0x9E, 0x62, 0xFB, 0xD7, + 0xDA, 0x69, 0xA1, 0x60, 0x20, 0x32, 0xE6, 0x56, + 0xA1, 0x6F, 0x37, 0xCA, 0x2D, 0x23, 0xBC, 0x39, + 0xED, 0xEF, 0xB7, 0xEE, 0x47, 0x4E, 0xCB, 0x46, + 0xD3, 0xCA, 0x19, 0xF6, 0x58, 0x4C, 0x31, 0x2B, + 0x65, 0xD6, 0xBC, 0x74, 0x21, 0x05, 0xD7, 0x99, + 0x96, 0x8F, 0x47, 0xE4, 0xD8, 0x4B, 0xD1, 0x60, + 0x27, 0x2E, 0x9B, 0xBF, 0x22, 0xC4, 0xAC, 0x37, + 0xDB, 0x41, 0x61, 0xF6, 0xA7, 0x25, 0x4B, 0xC6, + 0x75, 0x97, 0x0B, 0x17, 0xCB, 0x49, 0x60, 0x5A, + 0x3E, 0xB6, 0xD7, 0x88, 0x8D, 0x02, 0x89, 0xB3, + 0xB0, 0xCC, 0x03, 0xE4, 0x6F, 0x87, 0x44, 0xFF, + 0x01, 0x6D, 0xC0, 0xC9, 0xC5, 0x78, 0x34, 0xD2, + 0x33, 0x91, 0x0D, 0x51, 0x7A, 0xD9, 0x4D, 0x68, + 0x91, 0x47, 0xAB, 0xF9, 0xA1, + + 0x7B, 0x15, 0x4A, 0xEF, 0x0F, 0xD6, 0x75, 0xB7, + 0x1B, 0x32, 0x8B, 0x31, 0xC1, 0xCD, 0x3A, 0xF8, + 0x7F, 0x2F, 0xA5, 0xBD, 0x7C, 0x5C, 0x43, 0x82, + 0x4A, 0x01, 0x3B, 0x50, 0x59, 0x74, 0xB5, 0x61, + 0x40, 0x3B, 0xC0, 0xF3, 0xA1, 0x3E, 0x75, 0x36, + 0x85, 0x74, 0xC3, 0xB3, 0x55, 0xB6, 0xAB, 0xFC, + 0xE9, 0x36, 0x9E, 0x01, 0x1E, 0xCB, 0xEA, 0x1B, + 0xB2, 0x02, 0xD8, 0x42, 0x75, 0xD7, 0x36, 0x5E, + 0x45, 0x17, 0x0E, 0xDF, 0x33, 0xEF, 0x97, 0xCE, + 0xC5, 0x1D, 0x41, 0x4A, 0x06, 0xD3, 0xA1, 0x2F, + 0x5C, 0xD6, 0x7F, 0xE1, 0x45, 0xF7, 0x69, 0xC5, + 0xFA, 0xE6, 0x88, 0x3A, 0xCB, 0x96, 0xC0, 0xA4, + 0xB7, 0x50, 0x9C, 0x67, 0xA2, 0x12, 0x85, 0xC3, + 0x0D, 0xD3, 0x0A, 0xE2, 0x58, 0xD7, 0x94, 0x42, + 0xFD, 0x3D, 0x54, 0xA7, 0x08, 0x85, + + 0x56, 0x28, 0xAF, 0x54, 0xEA, 0x3E, 0xF7, 0xCF, + 0xB9, 0x44, 0xE1, 0xB6, 0x1D, 0x0D, 0xE5, 0x3F, + 0xDF, 0x47, 0x9D, 0xE4, 0x94, 0xA0, 0xBF, 0x0E, + 0xE1, 0xC1, 0x13, 0x73, 0x0F, 0x23, 0xB1, 0xBE, + 0xC1, 0x19, 0xD5, 0x4E, 0x0D, 0x19, 0xA0, 0xD5, + 0x20, 0xDD, 0xEA, 0xA9, 0x05, 0x27, 0xF7, 0x8C, + 0x3C, 0xB3, 0x18, 0x07, 0xDC, 0x11, 0xE8, 0x60, + 0xC2, 0x61, 0x67, 0xAD, 0x0B, 0x6C, 0xA9, 0xD9, + 0x3D, 0x65, 0xE9, 0x31, 0x68, 0xFB, 0x3F, 0xDF, + 0x4B, 0x0C, 0x3B, 0xC6, 0x51, 0xE4, 0x77, 0x73, + 0x9D, 0xB5, 0x5C, 0x37, 0x3B, 0xDD, 0xAD, 0xF2, + 0xA8, 0x22, 0x98, 0x4D, 0x5F, 0x4B, 0xE6, 0xE1, + 0x0A, 0x46, 0x60, 0x0F, 0xDE, 0x02, 0xF9, 0xE6, + 0xFC, 0x3E, 0x22, 0x5D, 0x42, 0x66, 0x50, 0x60, + 0x67, 0x8C, 0x16, 0x28, 0xC4, 0x87, 0x02, + + 0x7E, 0xF5, 0xA3, 0x08, 0x91, 0x76, 0x3F, 0x65, + 0x90, 0xBF, 0xA2, 0xD7, 0x38, 0x6B, 0x41, 0x4E, + 0xC7, 0xD5, 0xBE, 0xEC, 0x60, 0xC5, 0x80, 0x3B, + 0xD6, 0x7A, 0x45, 0x4A, 0x44, 0x0F, 0xA1, 0x28, + 0xB5, 0x5C, 0x58, 0xB4, 0x2F, 0x56, 0xD3, 0xB9, + 0x43, 0x49, 0x09, 0x5E, 0xA6, 0x50, 0xFE, 0xCF, + 0x6A, 0x1D, 0x26, 0xC5, 0x4E, 0x96, 0x1A, 0xD0, + 0xBA, 0x3F, 0x8D, 0x8D, 0xB1, 0x9E, 0x13, 0x66, + 0xCB, 0x9C, 0xEC, 0x57, 0x34, 0x59, 0x7C, 0x8F, + 0xE7, 0xE4, 0x8A, 0x52, 0x77, 0x31, 0x0A, 0x7C, + 0xB2, 0xD4, 0x80, 0x89, 0xCA, 0x09, 0xAD, 0x93, + 0xBC, 0x96, 0x60, 0xBE, 0xB7, 0x92, 0xD5, 0xDA, + 0xBB, 0x2C, 0x3B, 0xF6, 0x80, 0xA1, 0xFA, 0xB7, + 0x06, 0xA9, 0x5D, 0x74, 0x16, 0x5F, 0x4D, 0xBD, + 0xDF, 0x2B, 0x45, 0xA4, 0x14, 0xBA, 0x65, 0xBF, + + 0xDB, 0xE7, 0xA9, 0x45, 0xFF, 0x17, 0xA2, 0xCE, + 0xC3, 0x98, 0x34, 0xD0, 0x2F, 0xC2, 0xC1, 0xAE, + 0x0A, 0xC0, 0x0B, 0xFF, 0x6A, 0x0D, 0xA1, 0xAB, + 0xDA, 0x0B, 0x58, 0xA0, 0x85, 0xFC, 0xF1, 0xC7, + 0x53, 0x09, 0x22, 0x4E, 0xBC, 0x0D, 0x32, 0x83, + 0x2D, 0xF9, 0x06, 0x38, 0x18, 0x55, 0xC3, 0x6D, + 0x79, 0x46, 0x9D, 0x10, 0x5F, 0x14, 0xFB, 0x02, + 0x1D, 0xAD, 0x3A, 0xB2, 0x16, 0x48, 0xB5, 0xAD, + 0xB7, 0x24, 0x59, 0x93, 0x09, 0xA3, 0xDE, 0x52, + 0xE4, 0x4E, 0x63, 0xB8, 0xF4, 0x2E, 0x04, 0x90, + 0x10, 0x70, 0xE6, 0x2D, 0xE9, 0x27, 0xEC, 0x20, + 0x28, 0xF9, 0xBB, 0xF0, 0x1B, 0xF6, 0xB1, 0xA9, + 0x74, 0x60, 0x93, 0xF9, 0x39, 0x5D, 0x45, 0x29, + 0x43, 0x7B, 0x06, 0x50, 0x4B, 0x81, 0x79, 0xC1, + 0xF4, 0xBB, 0x71, 0x60, 0xE6, 0x1F, 0x93, 0xB2, + 0x33, + + 0xA8, 0xE6, 0xE9, 0xEB, 0x5A, 0xE1, 0xDE, 0x41, + 0xE9, 0x3E, 0x93, 0x29, 0xA3, 0x8C, 0x26, 0x4C, + 0x89, 0x01, 0x48, 0x46, 0x39, 0xB7, 0x49, 0x8D, + 0xAD, 0xDB, 0x76, 0x22, 0x74, 0x1C, 0x49, 0x79, + 0x61, 0x36, 0x56, 0x16, 0x10, 0x20, 0x68, 0x5E, + 0xBF, 0x1A, 0x25, 0x87, 0x7F, 0xC6, 0xB8, 0x17, + 0xB6, 0x4F, 0x89, 0xDD, 0x6A, 0x72, 0xD8, 0xF7, + 0xDF, 0x27, 0x50, 0xBF, 0x93, 0x26, 0xFE, 0x7A, + 0xD3, 0x55, 0x54, 0xC7, 0x23, 0x18, 0xFC, 0x88, + 0x47, 0x1A, 0x58, 0x51, 0x49, 0xE7, 0xC0, 0xAB, + 0xF8, 0xC8, 0x54, 0x38, 0xE0, 0x75, 0x6B, 0x47, + 0x1D, 0x5F, 0xC2, 0x60, 0x59, 0x72, 0x92, 0x57, + 0x5C, 0x2E, 0x0D, 0xAC, 0x94, 0x9A, 0x86, 0x74, + 0xF0, 0x42, 0x98, 0x11, 0x41, 0xE8, 0x3A, 0x3F, + 0xBA, 0xE4, 0xB4, 0xB6, 0x0E, 0x85, 0x11, 0xBC, + 0xB3, 0x23, + + 0x3C, 0xD8, 0x11, 0xEC, 0xC7, 0xBB, 0xA6, 0x7C, + 0x7F, 0x06, 0x4F, 0x7E, 0xBF, 0x3F, 0xA1, 0x96, + 0x65, 0x75, 0xC4, 0xDC, 0x2C, 0x2D, 0x89, 0x6D, + 0x75, 0xCB, 0xDD, 0xB2, 0xF9, 0x4F, 0x1B, 0x4C, + 0xE7, 0xCA, 0x02, 0xA5, 0x64, 0x6A, 0xEE, 0xEF, + 0x0B, 0xDF, 0xE8, 0x79, 0x2C, 0xBA, 0x0F, 0x46, + 0x17, 0xA0, 0x45, 0x14, 0x0C, 0x99, 0x2E, 0x66, + 0x09, 0xFC, 0x17, 0xD5, 0x03, 0xA3, 0x50, 0x98, + 0x82, 0xD6, 0x1B, 0x53, 0xBE, 0x27, 0x4D, 0xB3, + 0xBD, 0xC0, 0x43, 0x81, 0x75, 0x7D, 0xCF, 0x11, + 0xC7, 0x70, 0x50, 0x01, 0x0C, 0x08, 0x2E, 0xE3, + 0x81, 0xC7, 0x04, 0x43, 0xDE, 0x6F, 0x59, 0xB7, + 0x9E, 0x82, 0x33, 0x74, 0x6D, 0xF9, 0x0A, 0xE5, + 0x69, 0x99, 0xFC, 0x54, 0x73, 0xD8, 0xC8, 0x5A, + 0x65, 0x43, 0x45, 0xE0, 0x1B, 0xF4, 0xFA, 0x80, + 0xA0, 0x86, 0x7F, + + 0xB3, 0x36, 0xBC, 0x71, 0x8B, 0x9A, 0x93, 0x81, + 0x95, 0xD8, 0x5E, 0xF8, 0xDA, 0x56, 0x9E, 0xDB, + 0xF2, 0xBE, 0xFE, 0xB0, 0x57, 0xA6, 0x85, 0x48, + 0x59, 0x72, 0xEF, 0xB4, 0xF2, 0xFD, 0x5E, 0x98, + 0x9C, 0x74, 0x84, 0x52, 0xED, 0x88, 0xFA, 0x97, + 0x5C, 0x91, 0x4E, 0x34, 0x92, 0xD3, 0xC4, 0x4C, + 0x1B, 0x2F, 0x5F, 0xC8, 0x9B, 0xF1, 0xF6, 0x60, + 0x8F, 0xA5, 0x3E, 0xEC, 0x15, 0x2F, 0xCA, 0xC6, + 0xFF, 0xB9, 0xDD, 0x3E, 0x05, 0x6F, 0xAE, 0xE2, + 0x67, 0xD7, 0xF5, 0xE6, 0x93, 0x74, 0x07, 0x54, + 0x86, 0x62, 0xA3, 0x14, 0x4B, 0x8F, 0x00, 0xEA, + 0x70, 0x29, 0x15, 0xA5, 0x05, 0x9A, 0xFE, 0xFE, + 0xF5, 0x74, 0xE7, 0x29, 0xA1, 0x2C, 0x37, 0xEF, + 0x08, 0x81, 0xE0, 0x63, 0x4C, 0x0B, 0x4C, 0xD8, + 0xAF, 0x3A, 0x13, 0x11, 0x16, 0x03, 0x5C, 0x73, + 0x74, 0x6D, 0xDB, 0xD2, + + 0x84, 0x6D, 0xEF, 0xF2, 0x61, 0x96, 0xD3, 0x49, + 0x3A, 0x6E, 0x30, 0x46, 0xB9, 0x56, 0x8A, 0x81, + 0xC0, 0xCB, 0xDF, 0x5B, 0xCD, 0x5A, 0xAE, 0xF1, + 0xA1, 0x10, 0xE7, 0x03, 0x9A, 0xAE, 0x61, 0x16, + 0x2F, 0xD0, 0x0C, 0x26, 0x7A, 0x96, 0x8E, 0x3A, + 0x97, 0x8A, 0x0A, 0xC9, 0x90, 0x7F, 0x1E, 0x98, + 0xF2, 0x17, 0x74, 0xE7, 0xD1, 0xB2, 0x7F, 0x0F, + 0xE6, 0x19, 0x80, 0x2F, 0x83, 0x22, 0x63, 0xDD, + 0x73, 0x01, 0xEB, 0xC7, 0x72, 0xC7, 0x87, 0xBA, + 0x76, 0xF2, 0x53, 0xD2, 0xDD, 0x23, 0x91, 0x4F, + 0x67, 0x6F, 0x69, 0x74, 0xB4, 0xD8, 0x24, 0x20, + 0xD8, 0x29, 0x91, 0x78, 0xD6, 0xA2, 0xE0, 0x42, + 0x4C, 0xAE, 0x3A, 0xFB, 0xAB, 0x01, 0x70, 0xCF, + 0x57, 0xE7, 0xD0, 0x17, 0xF0, 0x4E, 0x9D, 0x52, + 0x4C, 0x37, 0x25, 0xA3, 0x23, 0x16, 0x82, 0x89, + 0x40, 0x64, 0xA4, 0x0C, 0x5A, + + 0x69, 0xB0, 0x3E, 0x4F, 0x95, 0x66, 0xFD, 0x32, + 0x6D, 0x68, 0xF4, 0xDD, 0x78, 0x1D, 0x54, 0x43, + 0xD1, 0xB2, 0x50, 0x71, 0x1C, 0x48, 0xDB, 0x8E, + 0xE0, 0x58, 0x1A, 0x89, 0xE0, 0x37, 0x64, 0x76, + 0x69, 0x85, 0xB9, 0x34, 0x82, 0xA5, 0x57, 0xAE, + 0x2A, 0x03, 0x00, 0x9B, 0x97, 0xCF, 0xE1, 0x59, + 0x94, 0x74, 0x07, 0x63, 0xD9, 0x6B, 0xBC, 0x1B, + 0x03, 0xD9, 0xE4, 0x40, 0xA5, 0xCB, 0x63, 0xF4, + 0xA1, 0xBA, 0x2F, 0xCE, 0x1A, 0x8F, 0xB7, 0x5A, + 0xB9, 0xFE, 0xEF, 0x8C, 0xA6, 0x74, 0x97, 0xC0, + 0xD6, 0x80, 0xB7, 0x2F, 0x03, 0xD8, 0x9B, 0x6E, + 0x36, 0x5D, 0xCC, 0x92, 0x21, 0x6E, 0xB8, 0x02, + 0xFA, 0xF9, 0xEA, 0x24, 0xA0, 0x3F, 0xDA, 0x2A, + 0xD9, 0x43, 0x1C, 0x07, 0xF2, 0x0A, 0x2C, 0x75, + 0x3D, 0x42, 0xDB, 0xEA, 0x33, 0xAB, 0x9D, 0x95, + 0xBE, 0x53, 0xF6, 0xAC, 0x56, 0xEB, + + 0x80, 0x25, 0xA8, 0x28, 0x34, 0x43, 0x8F, 0xDB, + 0xE4, 0xB8, 0x7A, 0x84, 0xE3, 0x12, 0x2E, 0xD3, + 0x28, 0x14, 0xFE, 0x42, 0x90, 0x95, 0x26, 0x2B, + 0xA1, 0x19, 0x69, 0x23, 0xCF, 0x9D, 0x91, 0xF4, + 0xD4, 0xF3, 0x3A, 0xCF, 0xA0, 0x61, 0x3E, 0x91, + 0xBF, 0xEE, 0x5B, 0xF8, 0x4E, 0xBE, 0xA0, 0x0A, + 0xAB, 0xBC, 0xE4, 0x6A, 0x11, 0x45, 0x20, 0xEA, + 0x3B, 0x29, 0xB7, 0xCA, 0x79, 0x75, 0x06, 0x48, + 0x4B, 0x7E, 0x11, 0xC5, 0xA8, 0x6A, 0x51, 0x9B, + 0x32, 0xBA, 0x3A, 0xA4, 0x06, 0x69, 0xC6, 0xD7, + 0x8F, 0xDE, 0x67, 0x52, 0x63, 0xFC, 0x71, 0x99, + 0x62, 0x10, 0x4B, 0x58, 0x77, 0xAB, 0x16, 0xBC, + 0x5D, 0xDF, 0x25, 0x45, 0xB3, 0x37, 0x03, 0x12, + 0x42, 0xAE, 0x77, 0xBD, 0xD5, 0x59, 0x37, 0x62, + 0x28, 0x8C, 0x0F, 0x70, 0x3B, 0x0B, 0x08, 0x2B, + 0x13, 0xAE, 0x21, 0x58, 0x20, 0x38, 0xDC, + + 0xCE, 0xFA, 0x4C, 0xBD, 0xCA, 0xBC, 0x3A, 0xC0, + 0x4B, 0x91, 0x3F, 0x33, 0x19, 0x46, 0x05, 0x16, + 0x80, 0xC0, 0xE2, 0x63, 0xB0, 0xE0, 0xD5, 0x2B, + 0xCE, 0xBA, 0x01, 0x36, 0x5B, 0xCD, 0x68, 0x17, + 0xCF, 0xEC, 0x0A, 0x31, 0x45, 0x36, 0x1B, 0x28, + 0xB1, 0x85, 0x90, 0x55, 0xB5, 0x27, 0x90, 0xE1, + 0x09, 0x4E, 0x8E, 0x27, 0x91, 0xD7, 0x15, 0x70, + 0x85, 0x89, 0x20, 0x0D, 0xD3, 0xF1, 0x70, 0x74, + 0xAA, 0x29, 0x0A, 0xA2, 0xA3, 0x96, 0x4B, 0x6E, + 0xBB, 0x04, 0x99, 0x4D, 0xC9, 0xD8, 0xFB, 0xA3, + 0xD0, 0xEF, 0x9D, 0x2A, 0x77, 0xC9, 0x0E, 0x85, + 0x33, 0x68, 0xFC, 0x30, 0xA1, 0x48, 0x55, 0x75, + 0x55, 0x21, 0xC8, 0x1F, 0x01, 0xF2, 0xA5, 0x2B, + 0x7D, 0xD7, 0x5E, 0x8C, 0x76, 0x06, 0x18, 0xBD, + 0x74, 0x4A, 0x5F, 0xE4, 0xE9, 0xD7, 0xCB, 0xEB, + 0x37, 0x95, 0x4F, 0x06, 0x26, 0xD2, 0xF1, 0xD4, + + 0x37, 0x0D, 0xA6, 0xE9, 0x34, 0x06, 0x17, 0x74, + 0x20, 0x52, 0x8E, 0x5B, 0xC2, 0x71, 0x8D, 0x6E, + 0x3C, 0xAA, 0xAA, 0xF9, 0x7F, 0x82, 0x09, 0x74, + 0x29, 0xCF, 0x79, 0xC1, 0xF7, 0x42, 0xB8, 0xA9, + 0x36, 0xA5, 0xFF, 0x77, 0x15, 0x09, 0x0B, 0x04, + 0x7E, 0xF4, 0xE1, 0xB1, 0x88, 0x89, 0x7A, 0x88, + 0x98, 0xD3, 0xA7, 0x95, 0x23, 0x69, 0x88, 0xD3, + 0x8F, 0x61, 0x1E, 0xBF, 0x99, 0xAD, 0xB4, 0x56, + 0xDF, 0x37, 0x12, 0x78, 0xDA, 0xF7, 0xDA, 0x42, + 0x8A, 0x43, 0xBD, 0x1A, 0xB2, 0x0A, 0xDA, 0x71, + 0xD6, 0x16, 0xDB, 0x15, 0xCA, 0xEA, 0x74, 0xA2, + 0x11, 0x0D, 0xAE, 0xE5, 0x2B, 0x64, 0x01, 0x61, + 0x91, 0x38, 0x16, 0xF4, 0x6E, 0xB9, 0x01, 0xDF, + 0xFD, 0xCD, 0x56, 0x79, 0xA7, 0x42, 0x7D, 0x8C, + 0xF4, 0x10, 0x4B, 0x00, 0xBA, 0x05, 0x77, 0x35, + 0x29, 0x74, 0x45, 0xDF, 0xCF, 0x2A, 0x6E, 0xBF, + 0x60, + + 0x49, 0x06, 0x7D, 0xD6, 0xE2, 0xC8, 0x54, 0x45, + 0x41, 0x97, 0x98, 0x29, 0xB5, 0x49, 0x3C, 0xE2, + 0x29, 0xC1, 0x87, 0xAC, 0x4A, 0x88, 0x31, 0x54, + 0x12, 0xDF, 0x6F, 0x7C, 0x88, 0xD4, 0x5A, 0x69, + 0xEF, 0x89, 0xB7, 0x26, 0xBD, 0x5D, 0x14, 0x45, + 0x5B, 0xAA, 0xC0, 0x31, 0x77, 0x20, 0x56, 0x22, + 0x32, 0x43, 0x0B, 0x0F, 0x29, 0x9F, 0x2C, 0xA5, + 0x42, 0xFC, 0x25, 0x1C, 0x9C, 0x1B, 0xE7, 0xBE, + 0x65, 0x8F, 0xBD, 0xA8, 0x07, 0xCC, 0x54, 0xFF, + 0xD2, 0x3B, 0xC0, 0x52, 0x9F, 0x21, 0xDE, 0xA4, + 0xA0, 0x8F, 0xB9, 0x05, 0x51, 0x8B, 0x4D, 0x6C, + 0x87, 0x4A, 0xD3, 0x82, 0xD4, 0x0D, 0xE2, 0x11, + 0xAC, 0x48, 0x81, 0x6D, 0xF9, 0xFD, 0xEC, 0x8D, + 0x15, 0x38, 0x8E, 0x6B, 0xE4, 0x5B, 0x04, 0x42, + 0xD5, 0x34, 0xFB, 0xE1, 0xBC, 0x0D, 0xDB, 0xB7, + 0xA9, 0xF7, 0xE6, 0x42, 0x5C, 0xBF, 0x89, 0x57, + 0x63, 0xF6, + + 0x90, 0x0A, 0xF6, 0x05, 0x11, 0x92, 0x82, 0x59, + 0xA1, 0x31, 0x54, 0xE8, 0x93, 0x6C, 0xE5, 0x4B, + 0xE8, 0x6C, 0x14, 0x80, 0xFB, 0x5F, 0xB5, 0x52, + 0xAA, 0x4A, 0x04, 0x64, 0xC5, 0xC5, 0x03, 0x2C, + 0x2D, 0xB1, 0x6F, 0xDC, 0xA0, 0x74, 0xFF, 0xDE, + 0xE5, 0x0D, 0x50, 0xE0, 0xBE, 0x35, 0xBA, 0x42, + 0x5E, 0x56, 0x64, 0x4F, 0xA0, 0x19, 0xD0, 0xAC, + 0xC6, 0x79, 0x09, 0x61, 0x1C, 0x72, 0xE2, 0x38, + 0x66, 0x7E, 0x96, 0x61, 0xC7, 0x69, 0x41, 0xEF, + 0x5A, 0x41, 0x89, 0xB2, 0xC6, 0xA9, 0xF3, 0xCB, + 0x8C, 0xAE, 0x1E, 0xBE, 0xDE, 0xF2, 0x4B, 0x20, + 0x67, 0xFF, 0x89, 0xAD, 0x95, 0x0A, 0x4D, 0x2C, + 0xEC, 0x62, 0x45, 0x53, 0x5C, 0xD8, 0x8F, 0x41, + 0x1B, 0x5E, 0xC6, 0x8E, 0xF6, 0x79, 0xB1, 0x3E, + 0xD5, 0x95, 0x46, 0x4E, 0x85, 0xA2, 0x32, 0x79, + 0x06, 0xE5, 0xBE, 0xD0, 0xEB, 0x77, 0xE0, 0x87, + 0x87, 0x75, 0xDD, + + 0xAC, 0x14, 0x1B, 0x69, 0xEE, 0x01, 0x0E, 0xE8, + 0x55, 0x78, 0x58, 0x91, 0xBE, 0xB4, 0x31, 0xCD, + 0x3D, 0xBE, 0x2F, 0x30, 0x81, 0x9D, 0x5F, 0x3A, + 0x65, 0x49, 0x1E, 0x24, 0x75, 0x38, 0xEA, 0x0F, + 0x45, 0x88, 0x6F, 0x66, 0x98, 0x56, 0x84, 0x41, + 0xB0, 0x7B, 0x08, 0x92, 0xE1, 0xAC, 0x8E, 0xFD, + 0x51, 0x96, 0xAD, 0x8B, 0x2D, 0x8C, 0xB8, 0xFA, + 0xB8, 0xA6, 0x63, 0x34, 0xF3, 0xC1, 0xCB, 0x51, + 0xC2, 0xAB, 0x04, 0x9B, 0x53, 0x28, 0xF9, 0x13, + 0xF0, 0x11, 0x54, 0xFF, 0xB0, 0x47, 0x18, 0x29, + 0xC5, 0x97, 0x43, 0x5E, 0x7C, 0xAA, 0x6F, 0x38, + 0x99, 0x04, 0x5C, 0x79, 0xF0, 0x08, 0xA2, 0x69, + 0xD3, 0xFF, 0x62, 0xC6, 0xDB, 0xA8, 0x2B, 0xF5, + 0xDB, 0xD2, 0x8A, 0xDD, 0x4F, 0xC3, 0xD2, 0x00, + 0xE5, 0xDE, 0x75, 0xD7, 0xC4, 0x8B, 0x76, 0xC8, + 0xDB, 0x55, 0xCB, 0xE6, 0x1B, 0xAB, 0xD2, 0x4E, + 0x3B, 0x93, 0x08, 0x43, + + 0x10, 0x07, 0x49, 0x6C, 0x11, 0x44, 0xEE, 0xFA, + 0x41, 0x19, 0xFA, 0xD1, 0x7D, 0xEC, 0xD2, 0x8F, + 0xA2, 0xBC, 0xB5, 0x38, 0x85, 0xD5, 0x4B, 0x79, + 0xFB, 0x77, 0x69, 0x42, 0x65, 0xEC, 0x29, 0x04, + 0x24, 0xCF, 0x51, 0xE1, 0xC2, 0x23, 0x06, 0x88, + 0x47, 0xAE, 0x7D, 0xA1, 0x08, 0x9A, 0x82, 0x2C, + 0xD6, 0xB4, 0x4D, 0x84, 0x96, 0x19, 0x17, 0x5A, + 0x0F, 0x22, 0x29, 0xDE, 0x0F, 0x8F, 0x71, 0xAE, + 0x12, 0x65, 0x87, 0xC5, 0xB4, 0xAD, 0x58, 0xDB, + 0xCA, 0x66, 0xE6, 0xB8, 0xE4, 0x3A, 0x80, 0x2B, + 0x18, 0x88, 0xD6, 0x21, 0xDC, 0x2B, 0xC8, 0x52, + 0x4B, 0x48, 0x90, 0x37, 0x72, 0x02, 0xCD, 0x01, + 0xEC, 0xE8, 0x03, 0xD4, 0x28, 0x8D, 0xD1, 0x94, + 0xBC, 0x1E, 0xAE, 0xA2, 0x79, 0x12, 0xB0, 0x5D, + 0x34, 0x32, 0x53, 0x1B, 0xC9, 0xA8, 0x6A, 0x1D, + 0x48, 0x30, 0x08, 0xB4, 0xC5, 0x54, 0x0A, 0x1E, + 0x24, 0x9E, 0x8D, 0x58, 0xF2, + + 0x1C, 0x73, 0x21, 0xCA, 0xB9, 0x20, 0x75, 0xEF, + 0xF4, 0x2B, 0xA5, 0xE5, 0x66, 0x62, 0xF3, 0x31, + 0x61, 0x92, 0x8B, 0x34, 0x44, 0x67, 0xC5, 0xAD, + 0xC1, 0x56, 0xC3, 0xB7, 0x23, 0xC6, 0x2B, 0xB1, + 0x34, 0x5F, 0xEF, 0xF7, 0x70, 0x21, 0x5E, 0x35, + 0x36, 0x28, 0xCC, 0xD2, 0x61, 0x58, 0xA3, 0xF6, + 0xB0, 0xD7, 0xA2, 0xBC, 0xF3, 0x02, 0x4E, 0x76, + 0x55, 0x1D, 0x48, 0x45, 0x90, 0x4C, 0xEF, 0x6C, + 0xAC, 0x58, 0x7B, 0xF2, 0x7C, 0x3E, 0x3F, 0xD6, + 0xFB, 0x57, 0x74, 0x3B, 0x91, 0xBE, 0xCE, 0x05, + 0x58, 0x9E, 0x26, 0xE6, 0x8A, 0x96, 0x8B, 0x00, + 0xC3, 0x28, 0x4A, 0xE8, 0x5C, 0xDA, 0x46, 0xF2, + 0x55, 0x14, 0x7C, 0x86, 0x58, 0xE0, 0x78, 0x16, + 0x0B, 0xA6, 0x45, 0xB2, 0xBD, 0x09, 0x83, 0x16, + 0xFE, 0x8F, 0x0D, 0xE6, 0x46, 0xE4, 0x85, 0x80, + 0x95, 0xE5, 0xC7, 0xEC, 0x5A, 0xE2, 0xE8, 0x1F, + 0xCC, 0x33, 0x25, 0x2A, 0x71, 0xB4, + + 0xDF, 0x62, 0x81, 0xFF, 0x5B, 0x88, 0x34, 0xB3, + 0xA9, 0x4A, 0xA6, 0xD9, 0x08, 0x48, 0x65, 0x7D, + 0xFC, 0x07, 0x56, 0xD8, 0xF5, 0xBA, 0x69, 0xD2, + 0xF8, 0x68, 0xAD, 0xDB, 0x14, 0x6F, 0x3F, 0x27, + 0x0B, 0x1D, 0x14, 0x53, 0x2C, 0xC2, 0x9E, 0x7E, + 0x1A, 0x97, 0xA2, 0xE3, 0x0B, 0x9E, 0x78, 0x3C, + 0x79, 0x4B, 0xD9, 0xCC, 0xDD, 0x19, 0x0D, 0x26, + 0x77, 0x65, 0xE6, 0x89, 0x19, 0xAB, 0x58, 0xA3, + 0x91, 0x50, 0x82, 0xA0, 0xB1, 0xB3, 0x7E, 0xD7, + 0x4B, 0xE2, 0xA2, 0xF7, 0x1E, 0x37, 0x33, 0x18, + 0xF6, 0x09, 0x60, 0x6B, 0x00, 0x46, 0xEF, 0xB1, + 0x06, 0x60, 0x1B, 0x0A, 0x4C, 0x0E, 0xE3, 0xFA, + 0xEA, 0xD7, 0x9D, 0xDC, 0x6B, 0x4C, 0x9E, 0x9C, + 0x61, 0x22, 0xAB, 0x19, 0x6E, 0x62, 0xA7, 0x25, + 0x26, 0xC3, 0xDB, 0x9B, 0xE0, 0x6E, 0xD4, 0x2D, + 0xDC, 0x71, 0x2A, 0x13, 0x7C, 0x77, 0xE8, 0xAA, + 0x1D, 0xED, 0x37, 0xB5, 0x6A, 0xB8, 0x3E, + + 0x02, 0xDC, 0x62, 0xA2, 0x98, 0x84, 0x22, 0x1E, + 0xB7, 0x25, 0x9A, 0xED, 0xDD, 0x60, 0x96, 0x44, + 0xEB, 0x1B, 0xC6, 0x9A, 0xEE, 0x76, 0x1B, 0x67, + 0xF4, 0x66, 0xE8, 0x63, 0x55, 0x3F, 0x6F, 0xB9, + 0x35, 0xAE, 0xB4, 0x3D, 0xD4, 0xDC, 0x4B, 0xD9, + 0x42, 0x1F, 0x7A, 0x36, 0x4D, 0x2C, 0x01, 0x2A, + 0x74, 0x5B, 0x67, 0xF9, 0xE1, 0x66, 0x69, 0xEF, + 0x89, 0x17, 0x96, 0xFF, 0x3F, 0xC6, 0x6C, 0xAB, + 0xFA, 0xE8, 0x82, 0x6D, 0x65, 0x48, 0xD2, 0x8D, + 0x0D, 0xCB, 0x0F, 0x5B, 0x2E, 0xCF, 0x17, 0xEA, + 0x60, 0x96, 0x76, 0x64, 0x33, 0x9A, 0xDE, 0x11, + 0xB4, 0x2A, 0xC4, 0x2D, 0x35, 0x2B, 0xF8, 0x36, + 0x80, 0xC4, 0x4E, 0xFE, 0x14, 0xFD, 0xB2, 0x57, + 0x0A, 0x46, 0x29, 0x37, 0x50, 0x7B, 0x26, 0x29, + 0x2B, 0xAE, 0x3D, 0xB9, 0xF8, 0x38, 0x31, 0x4C, + 0x13, 0x67, 0xD5, 0xF2, 0x40, 0x92, 0x09, 0xF1, + 0x5C, 0xE1, 0xF3, 0xE7, 0xE3, 0x6B, 0x25, 0x88, + + 0x70, 0x64, 0x4F, 0x22, 0x92, 0xCC, 0xBA, 0x70, + 0x10, 0xB5, 0xF9, 0x5D, 0xF6, 0xBD, 0x95, 0x7A, + 0x9E, 0xA1, 0xC3, 0x00, 0x77, 0x6A, 0x12, 0x94, + 0x43, 0xC9, 0x0C, 0xBB, 0xE2, 0xCD, 0xBE, 0xB7, + 0xF4, 0x11, 0xB0, 0x86, 0x11, 0xBB, 0x98, 0x31, + 0xBE, 0xC0, 0x82, 0xF9, 0x84, 0xF7, 0x99, 0xA6, + 0x1A, 0x44, 0x66, 0x28, 0x93, 0x32, 0xD9, 0x91, + 0x16, 0x8F, 0x53, 0x0D, 0x21, 0xF4, 0xFC, 0x69, + 0x5F, 0xDD, 0xFE, 0xF5, 0x59, 0x9A, 0x96, 0x4F, + 0x1C, 0x2A, 0xFD, 0xE4, 0x88, 0x66, 0x2A, 0x78, + 0x33, 0x60, 0xF3, 0x10, 0x55, 0xED, 0x68, 0xE6, + 0x34, 0x67, 0x62, 0x04, 0xBF, 0x39, 0x17, 0x3D, + 0x1B, 0x22, 0x8A, 0x77, 0x45, 0x90, 0x7C, 0x5C, + 0xCF, 0x4E, 0xE2, 0xB0, 0x88, 0xB8, 0x2B, 0x4A, + 0x4F, 0xCC, 0x14, 0xDC, 0xD3, 0x6A, 0x77, 0xA7, + 0xA7, 0x52, 0x0A, 0x4A, 0xCC, 0x3C, 0x30, 0xF0, + 0x9B, 0x10, 0x1A, 0xAA, 0xC8, 0x5A, 0xFE, 0x6F, + 0xA6, + + 0xC1, 0x62, 0xCF, 0x2C, 0xBA, 0xC0, 0xC0, 0x84, + 0x67, 0x4E, 0x96, 0xA2, 0xFF, 0x81, 0xE6, 0x74, + 0xFA, 0x22, 0xCE, 0x6B, 0xC4, 0x9C, 0x1D, 0x03, + 0xC0, 0xB1, 0x8B, 0x10, 0x33, 0xC3, 0x1F, 0x61, + 0x2D, 0x67, 0x09, 0x2F, 0x04, 0x51, 0x49, 0xCE, + 0x9F, 0x94, 0x14, 0xAB, 0x0A, 0x7F, 0x39, 0xD9, + 0xA5, 0x4F, 0x4E, 0x5A, 0x3C, 0xCB, 0xB8, 0x74, + 0x51, 0x28, 0x3B, 0xBE, 0x1B, 0x5E, 0x94, 0xFC, + 0xD1, 0xB2, 0x0A, 0x46, 0x20, 0xB3, 0x7E, 0x6E, + 0xD3, 0x9F, 0x76, 0x3F, 0x4A, 0xFF, 0x0B, 0xB7, + 0x52, 0xE2, 0xA4, 0xDF, 0x2E, 0x0C, 0x72, 0xE2, + 0xC7, 0x91, 0x2D, 0x5F, 0xB3, 0x38, 0x4F, 0x03, + 0x5C, 0x19, 0x2F, 0xBD, 0xDC, 0x84, 0xB5, 0xF9, + 0xFF, 0x8C, 0x19, 0x6F, 0x57, 0x4A, 0xD7, 0xA6, + 0xC5, 0x93, 0x2E, 0xEA, 0x35, 0xD7, 0xF8, 0xD4, + 0x08, 0xCA, 0x21, 0x06, 0x21, 0x12, 0x9B, 0xE2, + 0xF5, 0x38, 0x2C, 0xD4, 0x02, 0xD9, 0x56, 0xDE, + 0x5D, 0xB5, + + 0x1F, 0x86, 0xAE, 0xE4, 0xE4, 0xB2, 0x42, 0x60, + 0x9B, 0xBF, 0x1E, 0xA5, 0x16, 0x65, 0xA0, 0xDD, + 0xB3, 0xC6, 0xCA, 0xD7, 0xB8, 0xAE, 0x14, 0xCD, + 0xDF, 0xE9, 0xFF, 0x63, 0x38, 0x87, 0xE8, 0x2F, + 0xAE, 0xC7, 0xC7, 0x43, 0x46, 0x24, 0x54, 0xC0, + 0x41, 0xA6, 0x27, 0x73, 0x4E, 0x63, 0x0D, 0x0F, + 0x2D, 0xAA, 0xD6, 0x62, 0xFE, 0x8E, 0xE4, 0x3F, + 0xA2, 0xC3, 0x98, 0x5B, 0x93, 0x5B, 0x11, 0xEA, + 0xC9, 0x6D, 0xED, 0x9E, 0x71, 0x9D, 0xD9, 0x5E, + 0xA7, 0xB3, 0x4C, 0xB9, 0xBF, 0x2C, 0xE0, 0x90, + 0x3C, 0x19, 0x82, 0xE5, 0x94, 0xB9, 0xD7, 0x67, + 0x3F, 0xA7, 0x78, 0xAE, 0x10, 0x8B, 0x59, 0x69, + 0xD3, 0x73, 0x33, 0x2C, 0xD5, 0x74, 0x76, 0x56, + 0xA5, 0x16, 0xF0, 0x78, 0xB9, 0x4E, 0xFA, 0x0B, + 0x71, 0x77, 0x99, 0xAD, 0xBC, 0xD5, 0x86, 0xE0, + 0xFD, 0x36, 0xF6, 0x35, 0x97, 0x05, 0xEF, 0xB6, + 0xFF, 0x83, 0xA7, 0x0F, 0x65, 0xE8, 0x98, 0x8F, + 0x42, 0xB0, 0x8B, + + 0x90, 0x2A, 0x6C, 0xF6, 0x1D, 0xD4, 0x36, 0xDD, + 0xC3, 0x87, 0x2D, 0xE1, 0x68, 0xED, 0x8F, 0x21, + 0x0E, 0x53, 0xF5, 0x03, 0xE9, 0xA0, 0xD5, 0x3A, + 0xF9, 0xEF, 0x15, 0xCC, 0x0E, 0xE3, 0xD6, 0x27, + 0xB3, 0x30, 0x8B, 0x60, 0x0A, 0x43, 0x2F, 0xA4, + 0x41, 0x0E, 0xE9, 0x8F, 0xFD, 0x6E, 0x7C, 0x8A, + 0xEA, 0xCB, 0x7F, 0x1E, 0xEF, 0x15, 0x97, 0x59, + 0x4A, 0x67, 0x25, 0xFD, 0x72, 0xB6, 0x0D, 0x3A, + 0xC0, 0xCE, 0xD4, 0xA7, 0xF6, 0x61, 0x6B, 0x60, + 0x8C, 0x55, 0xB3, 0xB7, 0xA9, 0xB0, 0x7E, 0xD8, + 0x95, 0xBB, 0xAA, 0xFD, 0x3B, 0x72, 0x40, 0x72, + 0x28, 0xA4, 0x73, 0x19, 0x75, 0x37, 0x39, 0x07, + 0xBD, 0xB8, 0xB6, 0x51, 0x48, 0xC2, 0xB5, 0xFF, + 0xE8, 0xBC, 0x0C, 0x65, 0xD0, 0x8E, 0xB2, 0x32, + 0xA5, 0xCE, 0xE7, 0xB9, 0x3C, 0xC2, 0x56, 0xBF, + 0x4D, 0x7B, 0x8E, 0xA4, 0xCE, 0x2D, 0x7B, 0x32, + 0x48, 0xA9, 0xE3, 0x3A, 0x34, 0x18, 0xB0, 0x6A, + 0x68, 0x88, 0xED, 0x55, + + 0xB6, 0xDF, 0x47, 0x88, 0x2F, 0x70, 0x18, 0x3C, + 0xB9, 0x72, 0x04, 0x1E, 0x75, 0x5F, 0x45, 0xCA, + 0xA4, 0xB0, 0xAC, 0x11, 0x1B, 0x65, 0x4B, 0xAE, + 0xCA, 0x4D, 0xA1, 0xA9, 0x2C, 0xDF, 0x76, 0x14, + 0x94, 0x2A, 0xFC, 0x04, 0xC2, 0xE8, 0xAE, 0x94, + 0x15, 0x11, 0x07, 0x7A, 0x72, 0x25, 0xB6, 0x6D, + 0x70, 0x03, 0xC9, 0xAC, 0xE0, 0xAB, 0xD0, 0xC1, + 0x90, 0x4E, 0x08, 0xF4, 0x9F, 0x13, 0xB4, 0x7B, + 0xB5, 0x56, 0x37, 0x20, 0x83, 0x0C, 0x71, 0xA6, + 0x93, 0xF5, 0x00, 0x2F, 0xC9, 0xD5, 0x6A, 0x5C, + 0xB7, 0xA7, 0x9E, 0xB2, 0x1B, 0x62, 0x49, 0x9B, + 0xA0, 0x2A, 0x54, 0xC5, 0xE4, 0x5C, 0xCF, 0x5A, + 0x63, 0x9D, 0xA2, 0x85, 0x8D, 0xFA, 0xEE, 0x2B, + 0x1A, 0x41, 0x35, 0x97, 0x67, 0x36, 0x53, 0x25, + 0x52, 0x57, 0x8C, 0x8E, 0x53, 0x27, 0x89, 0xBC, + 0x2D, 0x3F, 0x91, 0xC4, 0x36, 0xB1, 0xEF, 0x54, + 0x1A, 0xCB, 0xDA, 0xC9, 0x78, 0x54, 0x78, 0xBA, + 0xDD, 0xA9, 0x23, 0x9D, 0xB4, + + 0xB0, 0xA8, 0x6B, 0x05, 0x72, 0x86, 0xDA, 0xD2, + 0x21, 0xDC, 0xC6, 0x3C, 0x0B, 0x47, 0x2E, 0xAA, + 0x4A, 0x4F, 0x19, 0x7B, 0xAC, 0xEF, 0x6F, 0xA6, + 0xD4, 0x9D, 0x45, 0xEF, 0xB4, 0x14, 0x83, 0x49, + 0xA6, 0x20, 0x55, 0x18, 0xA0, 0x64, 0x49, 0xD0, + 0xBB, 0x1C, 0xA5, 0x49, 0x56, 0x13, 0x8E, 0xD6, + 0x9A, 0x22, 0x3E, 0x1A, 0x28, 0x24, 0x6E, 0x55, + 0xAF, 0xA3, 0xDD, 0x02, 0x1B, 0xBC, 0x1E, 0xF2, + 0xA7, 0x8A, 0x5C, 0xBD, 0x6A, 0xE8, 0x26, 0x21, + 0x81, 0x5D, 0x6C, 0xDF, 0x44, 0xF8, 0xB4, 0xA7, + 0x42, 0x9D, 0x73, 0x9A, 0xC4, 0x7A, 0x4B, 0xF2, + 0x98, 0xFC, 0xB7, 0xE8, 0xEA, 0x8C, 0x0A, 0xC2, + 0xD5, 0xC3, 0x51, 0xCF, 0x3F, 0x71, 0xB5, 0xE4, + 0xC3, 0x5B, 0x30, 0xA6, 0xE4, 0x80, 0xFA, 0x46, + 0xB4, 0xBC, 0x13, 0x01, 0xA0, 0xA5, 0xF8, 0xEF, + 0x6D, 0xA8, 0xD3, 0xCB, 0x92, 0xD7, 0xBF, 0x46, + 0x73, 0xC4, 0xE0, 0x12, 0xC3, 0x57, 0xC1, 0x20, + 0x5E, 0xF2, 0xF2, 0x7A, 0x18, 0x54, + + 0xD5, 0x8C, 0x8D, 0x9B, 0x33, 0xB8, 0x9B, 0xC3, + 0x15, 0x50, 0xC9, 0xC7, 0x25, 0x8B, 0x36, 0xA6, + 0x5C, 0x3E, 0x87, 0x59, 0x3F, 0x6B, 0x0A, 0xD8, + 0x22, 0x39, 0x48, 0x8A, 0xE4, 0x50, 0x23, 0x8F, + 0x3A, 0x6E, 0xE1, 0x60, 0xA3, 0xDE, 0xB3, 0xFA, + 0xEA, 0xD4, 0x8B, 0x25, 0x17, 0xB1, 0x4E, 0xBA, + 0x0E, 0x97, 0x22, 0x37, 0xEE, 0xFE, 0x5D, 0x42, + 0xB5, 0xE5, 0xC1, 0x96, 0x34, 0xA6, 0x0E, 0x11, + 0xAA, 0x41, 0xD4, 0xF5, 0xDA, 0x40, 0x22, 0x2B, + 0xF9, 0x9E, 0x9C, 0x55, 0x6C, 0xB9, 0x73, 0xDF, + 0x94, 0x71, 0x8B, 0x90, 0x91, 0x67, 0xF4, 0xFE, + 0x01, 0x90, 0x64, 0xB8, 0xB8, 0x31, 0x85, 0xAE, + 0x82, 0x1B, 0x65, 0xD4, 0xC8, 0x61, 0x9D, 0x1D, + 0x9A, 0x94, 0x40, 0xD9, 0x7C, 0xE2, 0xE9, 0x09, + 0xE9, 0x2B, 0xD0, 0xEB, 0xBE, 0x57, 0xAD, 0x29, + 0xCB, 0xA5, 0xD4, 0x46, 0xBA, 0xB6, 0x86, 0xB8, + 0xD1, 0x2F, 0x0B, 0xFC, 0xCA, 0x43, 0xBA, 0x60, + 0xA2, 0x0F, 0xA6, 0xC6, 0x2F, 0x05, 0x0A, + + 0xA1, 0x11, 0x6A, 0x04, 0xA8, 0x55, 0xB1, 0x04, + 0xA8, 0x6F, 0x55, 0x85, 0xE9, 0xF1, 0x0D, 0x6F, + 0xDD, 0x04, 0x10, 0x3C, 0xB3, 0xAD, 0xD2, 0x9E, + 0xE7, 0x1E, 0x08, 0xBF, 0x65, 0x0A, 0xB0, 0xB4, + 0x95, 0x6D, 0xC6, 0x3E, 0xD8, 0x71, 0x46, 0x3E, + 0x1B, 0x1E, 0x82, 0x8D, 0x16, 0xF8, 0x98, 0xF1, + 0xEC, 0x0B, 0x0C, 0xFB, 0x73, 0xDB, 0x28, 0x1E, + 0xC1, 0xD0, 0xC2, 0xB1, 0xB0, 0x48, 0x02, 0xD9, + 0x69, 0xC5, 0x72, 0x6D, 0xB2, 0xD2, 0xCE, 0x15, + 0x8C, 0x77, 0xAD, 0x48, 0x7C, 0x4D, 0xEA, 0x87, + 0x5E, 0x13, 0x6D, 0x61, 0xFE, 0xFC, 0x75, 0xAA, + 0x76, 0x55, 0x2E, 0x87, 0x26, 0x92, 0x9D, 0x43, + 0x61, 0x7D, 0x1F, 0xFD, 0xE0, 0xCA, 0xC0, 0x87, + 0xAF, 0xAF, 0xC4, 0x92, 0x37, 0xED, 0xBB, 0x4F, + 0x38, 0x03, 0xE0, 0x1E, 0x1D, 0xB1, 0x4A, 0xBA, + 0x9A, 0xA0, 0x14, 0x84, 0x90, 0x90, 0x70, 0x62, + 0xA2, 0x55, 0x1D, 0x49, 0xD1, 0x51, 0xF1, 0x9B, + 0x1E, 0xD6, 0x8C, 0x02, 0xA4, 0xE2, 0x2D, 0x0E, + + 0xB9, 0xED, 0x83, 0x89, 0xD5, 0x73, 0x97, 0x72, + 0x3A, 0xF3, 0x32, 0x9A, 0xC4, 0xE0, 0x80, 0x5B, + 0xA3, 0x2E, 0x18, 0x82, 0xD9, 0x6D, 0xDC, 0xA3, + 0x4F, 0x00, 0x24, 0x67, 0x52, 0xDC, 0xD9, 0x07, + 0x1E, 0x62, 0x1A, 0xC8, 0xA8, 0xC0, 0xEF, 0x68, + 0x5E, 0xED, 0x2A, 0x23, 0xA2, 0x91, 0xC4, 0x49, + 0x69, 0xF4, 0xC1, 0x84, 0x60, 0x84, 0xAB, 0x34, + 0x91, 0x73, 0x76, 0x4A, 0x70, 0x98, 0xD5, 0x42, + 0xA0, 0x8C, 0x69, 0x1A, 0x6B, 0xC5, 0x90, 0x86, + 0x4E, 0xF3, 0x4C, 0xA0, 0xBA, 0x47, 0x4F, 0x41, + 0x0E, 0x83, 0x30, 0xF7, 0x5B, 0x47, 0x4C, 0x2D, + 0x78, 0xB9, 0x49, 0x51, 0x54, 0xF4, 0x01, 0x23, + 0x77, 0x6D, 0x2D, 0x05, 0x1F, 0xA9, 0xA5, 0x65, + 0x83, 0x92, 0xF3, 0xCE, 0x19, 0x2A, 0x9A, 0xF2, + 0xC6, 0x9C, 0x08, 0x8D, 0xF9, 0x31, 0xCA, 0xD0, + 0xF0, 0x56, 0xB9, 0xD2, 0xDE, 0x09, 0x6E, 0x7B, + 0xF1, 0x33, 0x45, 0xAD, 0x5D, 0x20, 0xC9, 0x6B, + 0x12, 0x91, 0xCC, 0xA0, 0x44, 0xF3, 0xB2, 0x9C, + 0x29, + + 0x28, 0x3B, 0xFC, 0xF7, 0x0B, 0xE0, 0x62, 0x2F, + 0xB3, 0x2E, 0x10, 0xD3, 0x78, 0x0C, 0x33, 0x6D, + 0xD7, 0xA8, 0xB2, 0xF7, 0x1C, 0x6C, 0xBA, 0xD0, + 0x67, 0x5F, 0xB5, 0xD3, 0x71, 0x09, 0xD7, 0x34, + 0xCE, 0xFB, 0xF0, 0xEE, 0x7D, 0x5C, 0xCE, 0x03, + 0x20, 0x85, 0xEA, 0xD6, 0x01, 0xF9, 0x4A, 0x3C, + 0x83, 0xC8, 0x59, 0xA3, 0x42, 0x90, 0xB3, 0x14, + 0x04, 0x86, 0xB1, 0x1A, 0xDB, 0x7C, 0x35, 0x56, + 0x96, 0x15, 0xE4, 0x17, 0x27, 0xB0, 0x47, 0xF2, + 0x75, 0x4E, 0xBD, 0x4D, 0x6D, 0x40, 0xDB, 0x03, + 0xBE, 0x34, 0x23, 0x9C, 0x80, 0xF3, 0x34, 0xF2, + 0x89, 0x43, 0xE2, 0x39, 0x4B, 0x9B, 0xD9, 0x1A, + 0xAB, 0x48, 0x4A, 0x97, 0x66, 0x87, 0xF8, 0x37, + 0x70, 0xCC, 0x05, 0x24, 0x48, 0x9F, 0xEF, 0x19, + 0x9C, 0xB4, 0x5F, 0x76, 0x28, 0xED, 0x59, 0x12, + 0x50, 0x5B, 0x69, 0x44, 0xA9, 0xBE, 0x40, 0xEE, + 0x2C, 0x97, 0xA3, 0x4A, 0x05, 0xCC, 0x57, 0xB8, + 0xC9, 0x26, 0x85, 0xE4, 0xB1, 0x58, 0xF2, 0x4D, + 0x9B, 0xA9, + + 0x36, 0xEF, 0xE2, 0x4F, 0xAE, 0x4C, 0xB1, 0xB3, + 0xB8, 0x22, 0x91, 0xB6, 0xC5, 0x31, 0x60, 0xFF, + 0x7B, 0x00, 0x71, 0x12, 0xAA, 0x5A, 0xD2, 0x10, + 0x5E, 0x0F, 0x4C, 0xB0, 0xDB, 0x09, 0x11, 0xC7, + 0x28, 0x6C, 0x88, 0x96, 0x97, 0xC6, 0xB7, 0x56, + 0x77, 0x20, 0xAB, 0xE4, 0x03, 0xFA, 0x7A, 0xCF, + 0x8E, 0xB7, 0x73, 0xEB, 0xD6, 0x80, 0xD8, 0xAE, + 0x6F, 0xCC, 0x22, 0x90, 0x49, 0x7B, 0x21, 0x57, + 0xAF, 0x39, 0x8E, 0x25, 0x98, 0x54, 0x2C, 0x7A, + 0xFC, 0xF1, 0xAE, 0xA0, 0xD7, 0x69, 0x95, 0x04, + 0x6A, 0x93, 0xDA, 0x41, 0x8F, 0xE3, 0x53, 0x7D, + 0x8C, 0xC4, 0xE6, 0x14, 0xD6, 0xF5, 0x23, 0x10, + 0xFF, 0xF4, 0x3D, 0x87, 0x8E, 0x44, 0x7B, 0xBD, + 0xDF, 0x5B, 0x81, 0x1E, 0x88, 0x9C, 0x8E, 0x3C, + 0xB3, 0xA1, 0x8D, 0xC9, 0x1F, 0xE2, 0x51, 0xFB, + 0x31, 0xBA, 0x07, 0x2D, 0xF8, 0xFA, 0x54, 0x88, + 0x64, 0x00, 0xA0, 0x73, 0x2A, 0xD7, 0x9B, 0x85, + 0xBB, 0xED, 0x0A, 0x2E, 0xA1, 0x98, 0x3D, 0x87, + 0xFA, 0x0A, 0x43, + + 0xAE, 0x78, 0x23, 0x96, 0x97, 0xE1, 0x6D, 0x02, + 0x31, 0xA9, 0x56, 0xBA, 0xDC, 0xA4, 0xDB, 0xD5, + 0x5F, 0x37, 0x26, 0xF8, 0xED, 0x78, 0x79, 0xAA, + 0x7B, 0xCC, 0xC3, 0x67, 0x38, 0x86, 0xB6, 0x58, + 0x7D, 0x2A, 0x5E, 0x77, 0xF8, 0xDB, 0x3A, 0xB0, + 0xB3, 0x83, 0x3A, 0xEF, 0xB5, 0xB0, 0x46, 0xA7, + 0x85, 0x98, 0xA8, 0x71, 0x1C, 0x2C, 0xB6, 0x06, + 0x3F, 0xEC, 0x59, 0x78, 0x15, 0xAD, 0x17, 0x65, + 0x94, 0x37, 0xC1, 0x21, 0xC9, 0x1A, 0x16, 0xEA, + 0x1A, 0xFE, 0x3D, 0x35, 0xF4, 0xBA, 0x5D, 0xA0, + 0x21, 0x9C, 0x89, 0x39, 0x69, 0xAB, 0x8E, 0x4B, + 0x8A, 0x9C, 0x38, 0xFD, 0x53, 0x37, 0xB4, 0x59, + 0xBE, 0xB0, 0x56, 0xD7, 0x6C, 0xF8, 0xB8, 0xF1, + 0x03, 0x26, 0xF5, 0x07, 0x98, 0xDB, 0xDF, 0xF3, + 0x9D, 0x02, 0x65, 0x78, 0x7C, 0xD7, 0x71, 0x06, + 0x93, 0xFF, 0xFA, 0xBE, 0xEF, 0x24, 0x24, 0x21, + 0x32, 0xD8, 0xC2, 0xFD, 0xB9, 0x08, 0xBC, 0x06, + 0xD6, 0xC1, 0xEE, 0x5D, 0xFE, 0xA1, 0x8D, 0x12, + 0x9A, 0x36, 0x5B, 0xB7, + + 0x61, 0x61, 0x14, 0x68, 0xE4, 0x54, 0x04, 0x1F, + 0xB4, 0x42, 0x87, 0x44, 0x7B, 0x01, 0xC6, 0xD6, + 0xF9, 0xBC, 0xE5, 0x1D, 0x25, 0x6E, 0xFB, 0xC9, + 0x2A, 0x50, 0x31, 0xB7, 0x8B, 0x11, 0x50, 0x39, + 0x2C, 0xE9, 0x37, 0xD1, 0x43, 0xD5, 0xD2, 0x9D, + 0x9D, 0x9E, 0x58, 0x1C, 0x14, 0x90, 0x8C, 0x0B, + 0xF0, 0x2D, 0xBE, 0xEB, 0xF4, 0x24, 0xA9, 0xFF, + 0x07, 0x5C, 0x8F, 0x49, 0x26, 0xEF, 0xC4, 0x74, + 0x8E, 0x32, 0x03, 0xCA, 0xBF, 0xB9, 0x8D, 0x3C, + 0x69, 0xE4, 0x3D, 0x35, 0x85, 0xC3, 0xAE, 0x35, + 0x33, 0x95, 0xD4, 0xE8, 0xAC, 0xBE, 0xE2, 0x2E, + 0xD2, 0x75, 0x3A, 0x42, 0x00, 0xBF, 0xC2, 0x61, + 0xF1, 0x0C, 0x5D, 0xFB, 0xC9, 0x59, 0xB1, 0xAC, + 0x37, 0x83, 0x7E, 0x52, 0xB0, 0x32, 0x38, 0x9E, + 0xD5, 0xCD, 0xE4, 0x77, 0x47, 0x08, 0x9B, 0x41, + 0x2A, 0x5D, 0xA7, 0x5E, 0xAA, 0xF2, 0xD3, 0x4F, + 0x76, 0x20, 0xB3, 0xD5, 0x55, 0xEE, 0x41, 0x00, + 0x8C, 0x7C, 0x38, 0x67, 0xF4, 0x38, 0x21, 0xB8, + 0x3F, 0x1E, 0x57, 0x46, 0x45, + + 0x66, 0xF1, 0x58, 0xCA, 0xD3, 0x81, 0x9F, 0x85, + 0x1D, 0x6F, 0xA2, 0xB5, 0xCB, 0x77, 0x12, 0x4C, + 0x41, 0xF2, 0x93, 0xED, 0x56, 0x04, 0xC7, 0xB1, + 0xA0, 0x9B, 0x6D, 0x51, 0x9C, 0xC8, 0xE9, 0xF3, + 0x9C, 0xE1, 0xC2, 0x91, 0x6A, 0x31, 0x05, 0x2A, + 0xB1, 0xEE, 0x19, 0xCD, 0x01, 0x30, 0x2F, 0x80, + 0x37, 0x03, 0xCD, 0xB1, 0x7D, 0xB8, 0xC0, 0x06, + 0x83, 0x00, 0xC7, 0x12, 0x85, 0x43, 0x8D, 0x67, + 0x12, 0xD8, 0xC8, 0x15, 0x62, 0x80, 0xA1, 0xE1, + 0x60, 0x42, 0x04, 0x74, 0x34, 0xBA, 0xBC, 0x67, + 0xF0, 0x04, 0xA7, 0x54, 0x3C, 0x6C, 0x0B, 0x10, + 0x2E, 0x02, 0x4D, 0x4F, 0x4A, 0x45, 0x97, 0x8E, + 0xF6, 0x50, 0x65, 0x44, 0xD0, 0x8B, 0x72, 0x33, + 0xCE, 0xD9, 0xDE, 0x7A, 0xE9, 0x98, 0x6F, 0x0C, + 0xE7, 0x79, 0xA8, 0x11, 0x73, 0x83, 0xF2, 0x93, + 0x5B, 0xE7, 0x3E, 0x4B, 0x81, 0xCE, 0xFB, 0x4E, + 0x27, 0x73, 0x7E, 0x66, 0xFA, 0x55, 0x1F, 0xCE, + 0xE8, 0xC3, 0x38, 0x7D, 0x04, 0xC6, 0xC3, 0x30, + 0x47, 0x8B, 0x02, 0x9D, 0x81, 0x49, + + 0x0A, 0x2A, 0x5B, 0x2B, 0x5E, 0xA4, 0x2B, 0x40, + 0x5E, 0x95, 0x70, 0x40, 0x23, 0x66, 0xD1, 0x1A, + 0x27, 0x79, 0x3F, 0x3C, 0xA9, 0x86, 0xF2, 0x75, + 0x2A, 0x52, 0x2E, 0xFE, 0x4E, 0x8E, 0x7E, 0x20, + 0x79, 0x19, 0x98, 0xDD, 0x01, 0x3F, 0x19, 0x68, + 0x23, 0x0A, 0xF0, 0x18, 0xA7, 0x13, 0x33, 0xF3, + 0x9E, 0x04, 0xA7, 0xA9, 0x01, 0xFA, 0xAC, 0xF3, + 0x81, 0x3D, 0xC6, 0x57, 0x35, 0x8C, 0xFE, 0x5D, + 0xCB, 0xE6, 0xF3, 0xA6, 0xCF, 0x18, 0x96, 0x70, + 0xCF, 0xAD, 0x05, 0xC6, 0x75, 0x05, 0x02, 0x50, + 0xA6, 0xAA, 0x88, 0xBC, 0x57, 0xA9, 0x14, 0x1A, + 0xF9, 0x1A, 0xD3, 0x40, 0xC7, 0x48, 0xFF, 0xE0, + 0x2D, 0xC6, 0xA8, 0x94, 0xBA, 0x3F, 0xFA, 0x76, + 0x44, 0xBA, 0xE8, 0x3E, 0xCC, 0x0D, 0x16, 0x8A, + 0x72, 0x91, 0xD8, 0xB1, 0x29, 0xC7, 0x3C, 0x67, + 0x1E, 0xA2, 0x3A, 0x9C, 0x9F, 0x5B, 0xCD, 0x41, + 0x8C, 0xCB, 0xFF, 0xFE, 0x70, 0x6F, 0xDA, 0xDF, + 0x10, 0x4B, 0xFF, 0x9A, 0xBF, 0x3E, 0xFF, 0x98, + 0x4E, 0x62, 0x15, 0x96, 0x97, 0xE1, 0x4E, + + 0x18, 0xDD, 0xC6, 0xD7, 0x31, 0x4C, 0x44, 0xF5, + 0x63, 0x56, 0x9C, 0xDF, 0xDC, 0x2D, 0x25, 0x5A, + 0xBF, 0x0C, 0x40, 0x69, 0x86, 0xDA, 0xA8, 0x76, + 0x86, 0x9C, 0x74, 0x9F, 0xDD, 0x04, 0x01, 0x9E, + 0x0B, 0x70, 0xEF, 0xE1, 0xF6, 0x26, 0x58, 0x5F, + 0x10, 0x2F, 0x9D, 0xC7, 0xAC, 0x4C, 0x19, 0x85, + 0xC8, 0x3E, 0xE8, 0xFF, 0xC4, 0x6C, 0x17, 0xB0, + 0xB9, 0x2E, 0x00, 0xD6, 0xEA, 0xF0, 0xFA, 0x40, + 0x93, 0x8D, 0x6F, 0xFD, 0x0B, 0xF5, 0x59, 0xEC, + 0xEA, 0x64, 0x26, 0x9D, 0x5F, 0xB2, 0x26, 0x1E, + 0x4C, 0xC8, 0x65, 0xAD, 0xD4, 0xC1, 0xAD, 0x63, + 0x29, 0x6C, 0x11, 0xCB, 0xE6, 0xDC, 0xF8, 0x2E, + 0x18, 0xA7, 0x50, 0xF0, 0x8F, 0x0E, 0x9B, 0x95, + 0xCC, 0x74, 0x3D, 0xB1, 0x9C, 0xD3, 0x50, 0x08, + 0x18, 0x5E, 0x27, 0x15, 0xD3, 0xF3, 0x5A, 0xC0, + 0x63, 0xEC, 0xB1, 0x4C, 0x77, 0x77, 0x9B, 0xE9, + 0x7B, 0x57, 0x0C, 0x31, 0xAA, 0xB5, 0xCE, 0x6C, + 0x4F, 0x46, 0x16, 0x0D, 0x55, 0xB9, 0x36, 0xF1, + 0xFA, 0x36, 0xE8, 0x89, 0xB0, 0x7B, 0xBB, 0x84, + + 0xF3, 0x24, 0x87, 0xDA, 0x52, 0xDC, 0x22, 0x0A, + 0xE1, 0x8E, 0x6A, 0x1A, 0xA6, 0x6C, 0x16, 0x48, + 0xFD, 0x70, 0xF9, 0x3E, 0x45, 0xF7, 0xE3, 0x6A, + 0xAC, 0x92, 0x5A, 0x82, 0xD1, 0xC3, 0x8C, 0x7E, + 0x76, 0x69, 0x3E, 0x34, 0x66, 0x25, 0x46, 0xD0, + 0x17, 0xEE, 0x25, 0xD2, 0xB2, 0x2A, 0xCE, 0xB8, + 0x6A, 0x56, 0x2C, 0x37, 0xB0, 0x51, 0x62, 0x1A, + 0x04, 0xB9, 0x68, 0xAA, 0x9B, 0x30, 0xEE, 0x17, + 0x57, 0xFC, 0x98, 0xCF, 0x7E, 0x4B, 0xAB, 0x25, + 0xA0, 0x91, 0x6E, 0x68, 0x23, 0x55, 0xF0, 0xC7, + 0x20, 0xA7, 0x3B, 0xE1, 0x03, 0xB3, 0x94, 0x2C, + 0x5D, 0xC1, 0x50, 0x32, 0x61, 0xB8, 0x63, 0x69, + 0x61, 0x74, 0x61, 0xCA, 0xC1, 0x49, 0x25, 0x63, + 0x9B, 0xA1, 0x5A, 0x09, 0x23, 0x44, 0x05, 0x97, + 0xCA, 0x73, 0xA6, 0x57, 0x92, 0xA5, 0x22, 0x18, + 0x82, 0x55, 0x0E, 0xFF, 0xE7, 0x53, 0xA9, 0x71, + 0x25, 0xC6, 0x2D, 0x02, 0x07, 0xD4, 0x51, 0x12, + 0x18, 0x03, 0x71, 0x4F, 0x10, 0x23, 0x17, 0xF0, + 0x76, 0xB3, 0x3B, 0x57, 0x8D, 0x88, 0xDA, 0x6F, + 0x5F, + + 0xCB, 0xF5, 0x44, 0xA5, 0x45, 0x49, 0x7D, 0x58, + 0xA6, 0x10, 0x4B, 0x4E, 0x81, 0xF2, 0x15, 0x21, + 0x03, 0xFB, 0xD2, 0x34, 0xE2, 0xF8, 0x99, 0x09, + 0x5D, 0x59, 0x24, 0xFF, 0xB9, 0x0D, 0xBC, 0xF6, + 0x2B, 0x11, 0x55, 0x4D, 0x50, 0x7C, 0xCD, 0x18, + 0x94, 0x92, 0xF3, 0x5C, 0x95, 0x7A, 0x64, 0xF7, + 0x35, 0x27, 0x56, 0x79, 0xD6, 0xD8, 0x81, 0xE0, + 0xF3, 0x3F, 0x80, 0x8F, 0x97, 0x1B, 0xD8, 0x15, + 0x70, 0x15, 0x2D, 0xC0, 0x2E, 0x7E, 0x79, 0xF8, + 0x1F, 0x88, 0xB7, 0xB7, 0xFF, 0x53, 0x67, 0xE8, + 0xC1, 0x59, 0x89, 0x66, 0xFD, 0x66, 0x4E, 0x88, + 0xA0, 0xCD, 0xC6, 0xD0, 0x34, 0x0C, 0x31, 0xE3, + 0xD4, 0x5F, 0x32, 0x32, 0xAC, 0xCC, 0xE1, 0x24, + 0x8C, 0x4E, 0xC9, 0x39, 0xC3, 0x7A, 0x6B, 0xAB, + 0xAE, 0x6C, 0x01, 0x79, 0x6D, 0xB4, 0xAF, 0x49, + 0x12, 0x10, 0xB3, 0xC5, 0x20, 0xB9, 0x63, 0x99, + 0x79, 0xC1, 0x22, 0xE4, 0x74, 0x15, 0x42, 0x61, + 0x98, 0x4E, 0xDC, 0x13, 0xD0, 0xE1, 0xD8, 0x24, + 0x0A, 0xC5, 0xB4, 0x54, 0xDC, 0x3C, 0x00, 0xDE, + 0xFB, 0xEB, + + 0x01, 0x0B, 0xD7, 0x6A, 0x19, 0xF8, 0xD2, 0x4A, + 0xD6, 0xC8, 0xFA, 0x05, 0x69, 0x4A, 0x18, 0x97, + 0x35, 0x61, 0x8B, 0xB9, 0x8B, 0x7B, 0x0C, 0xD9, + 0xF4, 0xF4, 0xC4, 0x2F, 0x1F, 0xA9, 0x98, 0x16, + 0xB7, 0xD6, 0x1F, 0x4A, 0x59, 0x42, 0x37, 0x60, + 0x37, 0x20, 0x37, 0x5C, 0x16, 0x6E, 0x63, 0x49, + 0x25, 0x1A, 0x86, 0x42, 0x00, 0xC3, 0x4A, 0x33, + 0xC7, 0x7D, 0x06, 0x14, 0xC9, 0x98, 0x19, 0x9A, + 0xD3, 0x17, 0xB4, 0x61, 0x4F, 0x52, 0x09, 0x13, + 0x54, 0xFE, 0xE4, 0x47, 0xA4, 0xFF, 0xA5, 0x20, + 0x62, 0x52, 0xB1, 0xDD, 0x42, 0xF2, 0x1C, 0xE9, + 0x0E, 0x2D, 0xEA, 0x19, 0x51, 0xF5, 0x3E, 0x7D, + 0x27, 0xFF, 0x1D, 0x43, 0x96, 0x16, 0x9B, 0xBD, + 0x9D, 0x29, 0x0E, 0x19, 0x4B, 0x6E, 0x98, 0xF4, + 0xA3, 0x51, 0x3C, 0x14, 0x64, 0xBF, 0x93, 0xAA, + 0x3C, 0x86, 0xF8, 0xB5, 0x3A, 0x2C, 0x12, 0x10, + 0x52, 0xD6, 0xE9, 0xB6, 0x36, 0x91, 0x5B, 0x36, + 0x22, 0x9B, 0xD1, 0x00, 0xEC, 0xC6, 0x13, 0x13, + 0x1D, 0x83, 0xF4, 0xF9, 0xBE, 0xC0, 0x3E, 0x71, + 0x0B, 0x64, 0xDF, + + 0xF2, 0xB0, 0xFC, 0xD2, 0x54, 0x85, 0x18, 0xD4, + 0x31, 0x7A, 0x3E, 0xD5, 0xDD, 0x8D, 0x3C, 0xCC, + 0x4E, 0x92, 0x14, 0xE4, 0x21, 0x39, 0xF3, 0x73, + 0x66, 0x71, 0x77, 0x29, 0x37, 0x49, 0x6C, 0x72, + 0xFB, 0x51, 0x17, 0x6C, 0x9E, 0x4A, 0x7C, 0x8C, + 0xBF, 0x96, 0x13, 0x8F, 0xAE, 0x7E, 0x88, 0x15, + 0xE9, 0x5E, 0x1A, 0xC4, 0xA7, 0x42, 0xFA, 0x60, + 0xD5, 0xFB, 0xCF, 0x90, 0x56, 0x97, 0xBB, 0xAD, + 0xF2, 0x2C, 0x6B, 0x07, 0xA5, 0xE6, 0x99, 0x92, + 0x75, 0x55, 0x69, 0xE8, 0xA3, 0xD9, 0x05, 0xC4, + 0x95, 0xED, 0xD0, 0x8E, 0x18, 0x60, 0x86, 0x86, + 0xEB, 0xC3, 0x20, 0xA8, 0xB9, 0x2E, 0xB8, 0x38, + 0xEB, 0xCE, 0x48, 0x2E, 0x4B, 0x42, 0xD6, 0xFB, + 0xDF, 0x97, 0xC4, 0xCD, 0x8C, 0xFD, 0x98, 0x0E, + 0xAC, 0xC2, 0xC4, 0xDA, 0x07, 0xFF, 0x8D, 0x72, + 0xF3, 0x15, 0x6F, 0x69, 0x3A, 0x61, 0x5F, 0xE0, + 0xAE, 0x1B, 0xCB, 0x16, 0x91, 0xEF, 0x54, 0x92, + 0xFC, 0xCD, 0xAF, 0xF4, 0x45, 0x4F, 0x2A, 0xCD, + 0xF8, 0xE4, 0x3F, 0x99, 0x2C, 0x61, 0xEC, 0x23, + 0xE3, 0x8B, 0xAB, 0x8F, + + 0x7C, 0x3F, 0x27, 0xDE, 0xAF, 0xE9, 0x72, 0x9C, + 0x04, 0xDA, 0xB4, 0xBB, 0xE6, 0x6F, 0x56, 0xF1, + 0xE8, 0x7E, 0xB5, 0xDA, 0x14, 0x2D, 0xFA, 0x29, + 0x80, 0x87, 0x53, 0x78, 0x65, 0xF7, 0x98, 0x64, + 0xB7, 0x11, 0x1A, 0x08, 0x4F, 0x36, 0x7E, 0xF8, + 0x4F, 0x0C, 0x70, 0xFE, 0xAF, 0xF1, 0x72, 0xC9, + 0xB8, 0x39, 0x57, 0xCB, 0x10, 0x61, 0x95, 0x49, + 0xEA, 0xB4, 0x2B, 0xA0, 0x77, 0x76, 0x30, 0x5D, + 0x9A, 0xB5, 0xCB, 0xF4, 0x5F, 0xC7, 0xA6, 0xD8, + 0x3A, 0x9E, 0xA8, 0xA1, 0x9F, 0xAB, 0xCC, 0x2C, + 0x2D, 0x8C, 0xCF, 0x3D, 0x7C, 0x11, 0xC6, 0xE9, + 0xD5, 0x15, 0xA2, 0xB8, 0xCE, 0x11, 0x45, 0x52, + 0x94, 0x0D, 0xE3, 0xF4, 0x47, 0x4A, 0xC6, 0x2E, + 0x7A, 0xBF, 0xBB, 0x4D, 0x2A, 0x89, 0xE5, 0x49, + 0x51, 0xDE, 0x25, 0x36, 0x58, 0x52, 0x3F, 0xA3, + 0xAD, 0xFE, 0xE3, 0xA3, 0x31, 0x90, 0xDA, 0xA3, + 0x1D, 0x48, 0xD6, 0xE8, 0xF2, 0x9E, 0x4C, 0xEC, + 0xCA, 0xC7, 0x46, 0x3C, 0xD5, 0x89, 0x01, 0xA7, + 0x33, 0xD3, 0x50, 0x7E, 0x31, 0x1C, 0x8E, 0xFF, + 0xF0, 0xB4, 0x42, 0x70, 0xA5, + + 0xDB, 0xFD, 0x65, 0xEB, 0x8C, 0xA7, 0x97, 0xE5, + 0xC7, 0xF4, 0x2E, 0xD4, 0x19, 0xCD, 0xA6, 0x0F, + 0x56, 0x39, 0xE3, 0x55, 0xF6, 0xFA, 0x43, 0xAC, + 0x3C, 0x0A, 0x1E, 0x00, 0xBF, 0x36, 0x1B, 0x95, + 0x71, 0x7E, 0x73, 0xBF, 0x93, 0x76, 0xF6, 0x2D, + 0xCB, 0x35, 0xBE, 0x43, 0x33, 0x2B, 0x89, 0x2D, + 0x85, 0x8A, 0xCE, 0x2F, 0xE7, 0x9C, 0x39, 0xD7, + 0x01, 0x69, 0x2E, 0xB3, 0xE7, 0xED, 0xEF, 0x41, + 0x06, 0xE7, 0xFC, 0x1F, 0x4B, 0x42, 0x71, 0xB2, + 0x59, 0x55, 0xEB, 0xBD, 0x18, 0x12, 0x63, 0x7F, + 0xEE, 0x10, 0x35, 0x84, 0x4E, 0x58, 0x9C, 0x75, + 0x86, 0xAB, 0x7F, 0x54, 0x8C, 0x90, 0x79, 0xAD, + 0xE7, 0xA6, 0x40, 0xCA, 0xB3, 0x6D, 0x8A, 0x14, + 0x8A, 0xE5, 0x13, 0x8C, 0x6E, 0xE9, 0x99, 0x56, + 0x11, 0xFC, 0xD4, 0x3B, 0xF5, 0xCB, 0x65, 0x33, + 0xEF, 0x98, 0x63, 0x7F, 0x91, 0x22, 0xD1, 0x2E, + 0xF4, 0xE0, 0xB3, 0xE8, 0xA6, 0x9E, 0xF9, 0x10, + 0xC6, 0xF1, 0x21, 0x95, 0x8C, 0x34, 0x2D, 0x7C, + 0x54, 0x92, 0x8D, 0x8F, 0xB6, 0x03, 0x34, 0xF7, + 0xF7, 0xD8, 0x5A, 0xBA, 0x57, 0x50, + + 0x41, 0x62, 0x0B, 0xC6, 0xC6, 0x43, 0xE4, 0x2D, + 0x36, 0x29, 0x87, 0x1D, 0x0D, 0xAC, 0xDB, 0xF0, + 0x00, 0x89, 0xBD, 0xE6, 0x48, 0x04, 0x24, 0x0C, + 0x28, 0x54, 0x88, 0x94, 0x3E, 0x1F, 0x2F, 0x5D, + 0x1C, 0xC1, 0x18, 0x38, 0xA6, 0xA5, 0xCA, 0xC3, + 0xAF, 0x5C, 0xDF, 0xAD, 0x8B, 0x4F, 0x53, 0x9E, + 0x6B, 0x99, 0xAB, 0x09, 0x2F, 0xD8, 0x11, 0xF9, + 0xC9, 0xAB, 0x73, 0x0A, 0xE6, 0x50, 0x6F, 0xBD, + 0xA5, 0xD1, 0x16, 0xFE, 0xC3, 0x11, 0xDE, 0x11, + 0x72, 0xC4, 0x71, 0x80, 0x40, 0x02, 0x14, 0x1F, + 0xC1, 0x6C, 0xE3, 0x28, 0xF8, 0xD8, 0x99, 0x06, + 0x1B, 0x90, 0x55, 0xFB, 0x67, 0x1D, 0x37, 0xA8, + 0xF9, 0xC7, 0xB2, 0xAD, 0x0F, 0x6C, 0xF4, 0x0F, + 0x1B, 0x42, 0xBD, 0x57, 0x51, 0x56, 0xA5, 0x83, + 0x7A, 0x15, 0x1C, 0x1A, 0xCF, 0x8C, 0xA4, 0x1B, + 0xF2, 0xFB, 0xDF, 0xC1, 0xCE, 0xD1, 0xF3, 0x29, + 0x7B, 0xA6, 0xA7, 0xE8, 0xA8, 0xB4, 0xCF, 0xEC, + 0x4E, 0xD7, 0x92, 0x82, 0x3A, 0x27, 0x05, 0xEF, + 0x88, 0x1B, 0x9C, 0x04, 0xAD, 0xF9, 0x51, 0x94, + 0xA6, 0xC1, 0xF8, 0x16, 0x3F, 0x5F, 0xF2, + + 0xFC, 0xF4, 0xBD, 0x0F, 0xE9, 0x09, 0xA4, 0xCE, + 0x31, 0x2D, 0x53, 0xD4, 0x15, 0xE5, 0x62, 0xF6, + 0x72, 0x63, 0x22, 0x53, 0x93, 0x2E, 0xCB, 0x30, + 0x01, 0x5C, 0x1C, 0xBA, 0x04, 0xA6, 0x95, 0x46, + 0x4E, 0x03, 0xEC, 0x36, 0xA2, 0xBE, 0x86, 0xD7, + 0x6E, 0x92, 0x2C, 0xEF, 0x64, 0x45, 0x94, 0x22, + 0xF8, 0x11, 0x43, 0x2D, 0x81, 0x04, 0xE6, 0x7B, + 0xEC, 0xE3, 0xF9, 0x9A, 0x43, 0xEF, 0xA7, 0xC9, + 0x5F, 0x39, 0x56, 0x6D, 0x3A, 0xBC, 0x26, 0xCD, + 0x77, 0x6F, 0x38, 0x30, 0x0A, 0x78, 0xD3, 0x98, + 0x71, 0xE1, 0x2C, 0xEA, 0xC2, 0xD8, 0x69, 0x18, + 0x74, 0xFE, 0x91, 0x1C, 0xAF, 0x5D, 0x8D, 0xDB, + 0x59, 0xDA, 0x52, 0xCD, 0xE4, 0x0D, 0x76, 0x41, + 0x9E, 0x6C, 0xD3, 0x85, 0x96, 0x3C, 0x8E, 0x66, + 0xDF, 0xED, 0x26, 0x03, 0x84, 0x88, 0x9D, 0x25, + 0x39, 0x35, 0xAD, 0xB5, 0x8B, 0xBB, 0xC5, 0x64, + 0xCE, 0xC5, 0xD7, 0x88, 0x5C, 0x2D, 0x46, 0x47, + 0x80, 0x11, 0x43, 0x85, 0xF1, 0xEB, 0x90, 0x71, + 0xA4, 0xB7, 0x5A, 0x3B, 0xB4, 0x51, 0x68, 0x76, + 0x63, 0x4D, 0x8F, 0x9F, 0x65, 0x6F, 0x9D, 0x03, + + 0x80, 0x5F, 0x0F, 0xB1, 0x39, 0x5B, 0xF6, 0x33, + 0xF7, 0x1C, 0xE6, 0x3C, 0x31, 0xC4, 0x31, 0x1A, + 0x14, 0x67, 0x38, 0x6C, 0x79, 0x59, 0x05, 0xF2, + 0x1F, 0xBA, 0xF2, 0xAE, 0x0C, 0xFF, 0xC5, 0x22, + 0x89, 0xB9, 0x2C, 0x88, 0xD1, 0x10, 0x24, 0xD1, + 0xBA, 0xD4, 0xD7, 0x41, 0x45, 0xAF, 0x57, 0xFC, + 0xF6, 0x97, 0x81, 0x4D, 0xA5, 0xF9, 0xC2, 0x25, + 0xFC, 0x3B, 0xBD, 0x0E, 0x96, 0xC2, 0x8B, 0xE3, + 0x93, 0x67, 0x65, 0xC4, 0x1D, 0x2A, 0x86, 0xCC, + 0x8A, 0x87, 0x4C, 0x35, 0xF7, 0x55, 0x51, 0xD6, + 0x8A, 0x8A, 0xE8, 0xE8, 0x69, 0xEF, 0x1A, 0xED, + 0xCB, 0x09, 0x4C, 0xE1, 0x4F, 0x36, 0xA6, 0xBD, + 0x68, 0x6B, 0x87, 0xC1, 0x39, 0x5E, 0x63, 0x61, + 0xA7, 0x19, 0x60, 0x3E, 0x1A, 0x2A, 0xF0, 0x43, + 0xA4, 0xE5, 0xE3, 0xEF, 0x76, 0x20, 0xA1, 0x7D, + 0x61, 0xFB, 0xC0, 0x8D, 0xED, 0xB0, 0x78, 0xDB, + 0xEC, 0x53, 0x44, 0x4A, 0x94, 0x42, 0x39, 0x83, + 0x41, 0x49, 0xBC, 0xA5, 0x17, 0x6C, 0xB4, 0x89, + 0x8D, 0xAB, 0x32, 0x40, 0x05, 0x69, 0x44, 0xF8, + 0x9A, 0x8B, 0x60, 0xD5, 0x47, 0x31, 0xD5, 0x17, + 0x1A, + + 0x99, 0xC8, 0x1F, 0x50, 0x51, 0xE0, 0xCA, 0x64, + 0x60, 0x38, 0x24, 0x34, 0x8B, 0x37, 0x13, 0x87, + 0x05, 0xE5, 0x3D, 0x7E, 0x8F, 0x8D, 0xF7, 0xD5, + 0x92, 0x06, 0xA4, 0x32, 0xD9, 0x64, 0xD8, 0xFC, + 0x1B, 0x5E, 0x46, 0xE4, 0x97, 0x14, 0x80, 0x2D, + 0xE2, 0x7B, 0x2E, 0xC4, 0xC2, 0x50, 0x0C, 0xDC, + 0xC1, 0xE6, 0x6A, 0x5F, 0x37, 0x02, 0x36, 0x24, + 0x98, 0x51, 0xB5, 0x92, 0x4E, 0x95, 0x80, 0x3E, + 0x93, 0x21, 0x03, 0xFB, 0x41, 0x20, 0x85, 0x55, + 0x39, 0xFC, 0xFC, 0x9E, 0xE6, 0x5B, 0x21, 0x29, + 0xC6, 0xCD, 0x63, 0x6B, 0xB8, 0x66, 0xB3, 0xB2, + 0x77, 0xE6, 0xA3, 0x3A, 0x40, 0x6C, 0xD0, 0x7C, + 0xEA, 0x36, 0x5F, 0x3E, 0xA2, 0x47, 0x64, 0xD9, + 0xF5, 0x94, 0x09, 0xE6, 0xC4, 0x90, 0x0E, 0xF6, + 0x5E, 0xE5, 0x9E, 0x54, 0xFC, 0x32, 0x51, 0x0F, + 0xED, 0x6A, 0xEE, 0x50, 0x29, 0x47, 0x0C, 0xEF, + 0xCD, 0x3D, 0x83, 0x2F, 0xA4, 0x62, 0x93, 0x0A, + 0x28, 0xEF, 0xD9, 0xAD, 0x6F, 0xAE, 0x79, 0x1B, + 0x7E, 0xB8, 0xEC, 0x21, 0x53, 0x13, 0x21, 0xDF, + 0x01, 0x81, 0x4C, 0x30, 0x07, 0x58, 0x6E, 0x80, + 0x1A, 0xED, + + 0x78, 0x66, 0x40, 0x07, 0x32, 0x18, 0x83, 0x4D, + 0x35, 0x65, 0x2D, 0xD2, 0x93, 0x09, 0xEF, 0xF7, + 0x5F, 0xD6, 0xB0, 0x99, 0xD9, 0xD0, 0xC5, 0xD7, + 0xF7, 0xC3, 0xDF, 0xD2, 0x01, 0xB2, 0x80, 0x7F, + 0xC5, 0xC8, 0x9A, 0xB3, 0x1B, 0x1D, 0x4A, 0x3E, + 0x05, 0xD4, 0x5B, 0xB5, 0xC5, 0x61, 0x7A, 0x16, + 0x49, 0x0E, 0xB3, 0x17, 0x30, 0x81, 0x10, 0xA0, + 0xC7, 0xE7, 0x3E, 0x1D, 0xAC, 0x2B, 0x36, 0x02, + 0xB2, 0x82, 0x7D, 0xAD, 0xD4, 0x24, 0x34, 0xC6, + 0x90, 0x19, 0x7E, 0xE0, 0xAD, 0xE2, 0xB2, 0xDE, + 0x59, 0xB8, 0xB5, 0x98, 0xC5, 0xEE, 0xA4, 0x14, + 0x8E, 0x4E, 0xEB, 0x9C, 0xA3, 0x27, 0x35, 0xF5, + 0x03, 0x8F, 0x3F, 0x24, 0xED, 0x7D, 0xA1, 0x86, + 0xFE, 0xD3, 0xD7, 0x45, 0xC8, 0x48, 0xDC, 0xA1, + 0x2E, 0xF9, 0xDF, 0xB3, 0x15, 0xD9, 0x16, 0x8E, + 0xEA, 0x52, 0xDE, 0x70, 0x71, 0xA9, 0x6A, 0x48, + 0x75, 0xBD, 0x6C, 0x8C, 0xF4, 0x51, 0x93, 0x35, + 0x7F, 0xD8, 0x59, 0xDB, 0xEE, 0x97, 0xB3, 0x15, + 0xC2, 0x66, 0x95, 0xB9, 0x09, 0x7F, 0x22, 0x4A, + 0x0D, 0xBF, 0xCE, 0x05, 0x11, 0x14, 0x72, 0x83, + 0x89, 0x77, 0x48, + + 0x36, 0x9A, 0x46, 0xA7, 0xD8, 0x92, 0xA3, 0x4A, + 0xBE, 0x55, 0xDA, 0xBE, 0xB4, 0x8D, 0x58, 0x09, + 0x0F, 0xEB, 0x49, 0x0C, 0xFE, 0xDA, 0x5E, 0xB3, + 0xCF, 0xB5, 0xDF, 0x65, 0x4B, 0x37, 0xF4, 0xFF, + 0x0F, 0x68, 0x8C, 0xBE, 0x3C, 0x36, 0x62, 0x54, + 0xF1, 0xC5, 0x76, 0x64, 0x6E, 0x7F, 0xCC, 0x91, + 0x61, 0x77, 0xB5, 0xD1, 0x5F, 0x56, 0x35, 0xAA, + 0xFB, 0x72, 0x8E, 0x21, 0xFF, 0x33, 0xD5, 0x36, + 0xFE, 0xF6, 0x85, 0x63, 0x58, 0x02, 0x6A, 0x35, + 0x0C, 0x3F, 0x42, 0xE2, 0xF7, 0xE7, 0x36, 0xC1, + 0x11, 0x49, 0x75, 0x99, 0x6D, 0x39, 0x68, 0x7F, + 0x17, 0x54, 0x29, 0x5A, 0x88, 0xAF, 0x48, 0xFC, + 0xF7, 0xC9, 0xD1, 0x39, 0xA2, 0xBD, 0x1D, 0xBF, + 0xAA, 0xA2, 0x37, 0x9D, 0x71, 0x7D, 0xEC, 0xF5, + 0x7C, 0xEC, 0x1C, 0x21, 0xBC, 0x73, 0x4D, 0x69, + 0xA5, 0x0C, 0xA1, 0x6E, 0xDA, 0x84, 0x52, 0xB9, + 0xED, 0xE6, 0x3C, 0xAC, 0x0D, 0xEA, 0x78, 0x40, + 0x4A, 0x24, 0x6C, 0xC5, 0xA9, 0xB9, 0x6C, 0x9A, + 0x57, 0xC4, 0xA3, 0xC2, 0x66, 0x8F, 0x4D, 0x9E, + 0x85, 0x14, 0x26, 0xB2, 0x9B, 0xB5, 0xEF, 0x22, + 0xE1, 0xA1, 0x50, 0x41, + + 0x96, 0x16, 0x23, 0x5B, 0x30, 0x81, 0xD1, 0x9A, + 0x66, 0x84, 0x92, 0xD1, 0xC4, 0x59, 0xE1, 0x24, + 0xFF, 0x2D, 0xA8, 0x6B, 0x83, 0x72, 0x38, 0xCD, + 0x7D, 0x4F, 0x5A, 0x85, 0x8A, 0x2E, 0xE9, 0x6B, + 0x48, 0x02, 0x54, 0xE4, 0x3B, 0x9F, 0x76, 0x13, + 0x9F, 0x9F, 0xF3, 0xE0, 0xF0, 0x48, 0x67, 0x9D, + 0x9F, 0x86, 0xCF, 0x7E, 0x7C, 0x1C, 0x0F, 0x61, + 0x64, 0xDA, 0x67, 0xBE, 0x68, 0x02, 0x08, 0x43, + 0x02, 0xF8, 0x68, 0x4C, 0x73, 0x4B, 0xCF, 0xFB, + 0xE8, 0xC7, 0x61, 0xCC, 0x62, 0x18, 0x39, 0xE4, + 0x04, 0x44, 0x36, 0x1E, 0xB7, 0xFB, 0xCC, 0x0A, + 0x05, 0xE7, 0x50, 0x28, 0x7F, 0xD0, 0x4F, 0xCF, + 0x81, 0xC2, 0x61, 0x72, 0x1C, 0x21, 0x76, 0x46, + 0x13, 0x05, 0x38, 0x5D, 0xEA, 0x2B, 0xF3, 0x5D, + 0xD9, 0x02, 0x3D, 0xBA, 0x4D, 0x79, 0x19, 0xB5, + 0xAC, 0xE3, 0xC7, 0x87, 0x0E, 0xA5, 0x40, 0xE5, + 0x68, 0x7A, 0x4A, 0xA1, 0x54, 0x1B, 0x10, 0xA0, + 0x9D, 0x00, 0x74, 0xB6, 0x86, 0x6D, 0x45, 0x77, + 0xBB, 0x7B, 0x4C, 0x8C, 0xD1, 0xCA, 0xC0, 0xA8, + 0xC1, 0x17, 0x17, 0x39, 0xD6, 0xC8, 0x8D, 0x75, + 0x63, 0x03, 0xB7, 0xCC, 0xA8, + + 0x18, 0xB2, 0xD0, 0xF4, 0xB4, 0xCD, 0x4D, 0xB5, + 0x3F, 0x45, 0x8E, 0x05, 0x57, 0x1A, 0x9E, 0xFB, + 0xA7, 0x46, 0xB9, 0x97, 0xC6, 0x72, 0x46, 0x48, + 0x84, 0xB2, 0xB3, 0x28, 0xBC, 0xF8, 0x3A, 0x70, + 0x4A, 0x1A, 0x26, 0x9D, 0x71, 0x31, 0xEC, 0xB2, + 0xA2, 0x26, 0x8A, 0x74, 0x03, 0x7D, 0x12, 0x07, + 0x09, 0x7B, 0xD9, 0x89, 0x3B, 0x76, 0xD6, 0xC5, + 0xB9, 0x25, 0x93, 0xFD, 0x15, 0xDA, 0x26, 0x9E, + 0x0D, 0x74, 0xAE, 0xCE, 0x81, 0xC1, 0x3E, 0x1F, + 0x4C, 0x86, 0x3C, 0x99, 0x0A, 0xC5, 0x28, 0x90, + 0x74, 0x39, 0x03, 0x07, 0x1F, 0x6A, 0xB5, 0xF1, + 0x64, 0xB4, 0xEC, 0xEE, 0x04, 0xEB, 0x46, 0xCA, + 0xCC, 0x2B, 0x3C, 0x6A, 0x9B, 0x06, 0x0A, 0xD9, + 0x76, 0xB6, 0xBB, 0x8B, 0xBD, 0x57, 0x7A, 0x31, + 0x87, 0x1D, 0x37, 0x2B, 0xAC, 0x43, 0xA5, 0x0F, + 0x0F, 0xC4, 0x25, 0x17, 0x65, 0xDF, 0xD3, 0x98, + 0x7F, 0x31, 0x89, 0xAF, 0xE2, 0x1D, 0xED, 0x3B, + 0x29, 0x5F, 0x35, 0x68, 0xEC, 0xC2, 0x77, 0x4A, + 0x9C, 0x5A, 0xAE, 0x52, 0x13, 0x8C, 0x98, 0x47, + 0x03, 0x1B, 0x20, 0x1A, 0x22, 0x4D, 0x87, 0x9D, + 0x40, 0x86, 0x48, 0xF8, 0x60, 0xFB, + + 0x63, 0x82, 0x97, 0xF6, 0x37, 0x44, 0x0E, 0x4B, + 0x54, 0x92, 0xA1, 0xFB, 0x06, 0x95, 0x89, 0x79, + 0x8F, 0x50, 0x5F, 0xD3, 0x46, 0x7A, 0xEB, 0xEC, + 0x98, 0x38, 0x40, 0x0E, 0xCC, 0x6C, 0x35, 0xBF, + 0x50, 0xD1, 0x6A, 0x3E, 0x08, 0x2E, 0x50, 0x8E, + 0xC2, 0xEB, 0xF3, 0xD8, 0x5A, 0xE8, 0xE3, 0xF4, + 0xC0, 0x37, 0x88, 0x04, 0x32, 0xAF, 0x17, 0xFC, + 0xEE, 0x42, 0x88, 0x84, 0xE7, 0xAD, 0x0F, 0xA7, + 0x51, 0xAB, 0x6A, 0xD5, 0x4B, 0xCF, 0xDB, 0xC2, + 0x09, 0xEF, 0x5D, 0x6B, 0x7F, 0x12, 0x79, 0x13, + 0xB0, 0xE8, 0xDE, 0x80, 0x72, 0x71, 0xD8, 0xDB, + 0x4E, 0x08, 0xF0, 0xA8, 0x7A, 0x68, 0xF8, 0x82, + 0xD5, 0xFD, 0xD3, 0x4A, 0x58, 0x2A, 0x99, 0x65, + 0xC2, 0x2C, 0x97, 0x59, 0x13, 0xA8, 0x96, 0x52, + 0xC3, 0xA9, 0x17, 0x47, 0xBA, 0x9C, 0xD9, 0xBB, + 0xBE, 0xAF, 0x91, 0x6F, 0xE0, 0xE3, 0x52, 0x9B, + 0xA5, 0x49, 0x7B, 0xEF, 0x5F, 0x2B, 0x03, 0xD7, + 0x4C, 0x46, 0xC0, 0xE8, 0x3F, 0xE5, 0x68, 0x7D, + 0xC9, 0xCC, 0x63, 0xA8, 0x85, 0x78, 0xFD, 0x0F, + 0x6A, 0x09, 0xBE, 0x1B, 0x1B, 0x62, 0x7A, 0x43, + 0x57, 0xF3, 0x7B, 0xC7, 0xD4, 0x9F, 0xF0, + + 0x48, 0xA5, 0xCC, 0x95, 0x4E, 0xDD, 0xE7, 0xF9, + 0x53, 0x3C, 0x37, 0xAC, 0xAA, 0x4D, 0xD7, 0x46, + 0x53, 0xD8, 0xC5, 0x51, 0x27, 0xFE, 0x09, 0x24, + 0x56, 0x0D, 0x9D, 0x57, 0x3B, 0x07, 0xB6, 0xA1, + 0x0E, 0x75, 0xF3, 0xC4, 0x80, 0x06, 0x49, 0x0B, + 0xFB, 0x36, 0x3A, 0x98, 0xB0, 0x98, 0xB8, 0xD1, + 0x4A, 0x9F, 0x7A, 0x3D, 0x06, 0x32, 0xDE, 0x00, + 0x8C, 0xF5, 0x5C, 0x65, 0xE8, 0xCB, 0x15, 0xF7, + 0x77, 0xE6, 0x93, 0x1F, 0x56, 0x21, 0x48, 0x7B, + 0xFF, 0xD1, 0x7E, 0xA2, 0xB1, 0xDB, 0x36, 0xC1, + 0x3D, 0x23, 0x9F, 0x8B, 0x2B, 0x23, 0x90, 0x58, + 0x9A, 0x60, 0xF2, 0x8D, 0x61, 0xAD, 0x69, 0x5C, + 0xC4, 0xA5, 0xB3, 0x68, 0xF0, 0x98, 0xD1, 0xB2, + 0xEA, 0x03, 0xE7, 0x02, 0x19, 0x81, 0x26, 0x5D, + 0x20, 0x4E, 0x61, 0xF1, 0xEC, 0xE6, 0x6C, 0xEF, + 0x0E, 0x55, 0x6F, 0x6D, 0x11, 0x84, 0x1A, 0x5B, + 0x7E, 0x82, 0x66, 0x88, 0x44, 0x63, 0x99, 0xB1, + 0x5B, 0xC5, 0xFB, 0x47, 0xFF, 0x9C, 0x39, 0xD6, + 0xC2, 0x96, 0xEC, 0xE3, 0xA7, 0xAA, 0xF7, 0xC2, + 0x93, 0xB2, 0x0D, 0x7B, 0x2A, 0xF8, 0xBA, 0x22, + 0x2E, 0xA5, 0x7C, 0x88, 0x41, 0x7E, 0x8A, 0xFA, + + 0x0F, 0x63, 0x92, 0x0D, 0x42, 0x3E, 0x56, 0x78, + 0x82, 0x58, 0xB7, 0x5A, 0xB4, 0xFF, 0x25, 0x5E, + 0xCD, 0x05, 0x8A, 0x3B, 0x00, 0xE7, 0xF0, 0x70, + 0xFE, 0x38, 0xC4, 0x17, 0x4D, 0x0A, 0x26, 0x71, + 0x3F, 0x64, 0x1F, 0x85, 0xE2, 0xF6, 0xED, 0x07, + 0x79, 0xDF, 0x7D, 0x03, 0x45, 0x0E, 0x26, 0xB1, + 0xC8, 0xD7, 0x7C, 0xE0, 0xF6, 0x3E, 0x6C, 0xDC, + 0xB2, 0x58, 0xFD, 0x56, 0x47, 0x69, 0xE1, 0x41, + 0xEB, 0x88, 0xEB, 0xE9, 0x8E, 0x73, 0x68, 0x4A, + 0xB4, 0xC3, 0x35, 0xA0, 0x2F, 0x8D, 0x07, 0x1E, + 0xE9, 0x7E, 0x63, 0x4D, 0x73, 0xEE, 0x79, 0x6A, + 0xA6, 0x84, 0x4A, 0x22, 0xE7, 0x88, 0xBC, 0x59, + 0x83, 0x41, 0xCD, 0x16, 0x73, 0x7C, 0xE3, 0xC0, + 0xEF, 0x23, 0x8C, 0x7A, 0x24, 0x16, 0x3F, 0x77, + 0x72, 0x10, 0xCF, 0x32, 0x1F, 0x2E, 0x37, 0xED, + 0x1B, 0x90, 0xD4, 0x4C, 0x71, 0x4D, 0x81, 0xBD, + 0x36, 0x6B, 0x6A, 0x2E, 0x07, 0xAD, 0x32, 0x35, + 0x76, 0x67, 0x66, 0xE1, 0x76, 0x95, 0x2F, 0x0A, + 0xF3, 0xF7, 0xC3, 0x25, 0x3D, 0xDF, 0xA1, 0x89, + 0xC2, 0xC1, 0x15, 0x7C, 0xD6, 0xA0, 0x07, 0x14, + 0x7D, 0x81, 0x83, 0x3B, 0x96, 0xBA, 0x39, 0x57, + 0x01, + + 0x41, 0xC2, 0x86, 0x92, 0x97, 0xA5, 0x85, 0x6B, + 0xA6, 0xEC, 0x46, 0xB2, 0xA1, 0xF7, 0x9F, 0x4C, + 0x95, 0xA8, 0xEE, 0xE0, 0xB8, 0x83, 0x46, 0x6A, + 0xAD, 0x9C, 0xD1, 0x91, 0x42, 0xD8, 0x18, 0xC1, + 0xA9, 0x54, 0x1A, 0xCA, 0xA6, 0x66, 0xF7, 0xE2, + 0x9F, 0x82, 0x73, 0xE9, 0x17, 0x32, 0x23, 0x35, + 0x3E, 0x18, 0x5C, 0xAE, 0x59, 0xB7, 0x6A, 0x04, + 0x97, 0xD1, 0x1F, 0xB2, 0x58, 0xC9, 0x37, 0xFD, + 0xE0, 0xC5, 0xA4, 0x6C, 0x9A, 0x00, 0x9B, 0x4C, + 0x06, 0x32, 0xF4, 0xA3, 0x94, 0xBF, 0xA8, 0x49, + 0xFE, 0xC2, 0x45, 0xA8, 0xB5, 0x32, 0x86, 0x5F, + 0xBA, 0xFD, 0xD1, 0x60, 0x4D, 0xF1, 0x43, 0xE1, + 0x55, 0x75, 0xD8, 0xD6, 0x1F, 0xD5, 0x7F, 0x35, + 0x67, 0x39, 0x74, 0x9C, 0x3B, 0x50, 0xBE, 0xC0, + 0x67, 0xA1, 0x90, 0x0F, 0x90, 0x27, 0xAD, 0xB9, + 0x97, 0x0C, 0xBC, 0x2A, 0xEF, 0x4D, 0x8F, 0x69, + 0x08, 0x50, 0xBD, 0x69, 0xE9, 0x2B, 0xB3, 0x37, + 0xB5, 0x90, 0x81, 0x73, 0x63, 0x64, 0xE6, 0x6A, + 0xF0, 0xEF, 0xB1, 0xEC, 0x9C, 0x8B, 0x23, 0xE3, + 0xBA, 0xDD, 0x10, 0x1A, 0x0E, 0x6A, 0x76, 0x4C, + 0x58, 0xAF, 0x8C, 0x10, 0xDD, 0xFE, 0xCB, 0x82, + 0x53, 0x9A, + + 0xB5, 0x42, 0x48, 0x28, 0x65, 0x88, 0xE8, 0xB7, + 0xB6, 0xCD, 0xAB, 0x9B, 0x31, 0xFE, 0xEC, 0xD7, + 0xBF, 0x43, 0x1B, 0x30, 0x75, 0x57, 0x96, 0x3E, + 0x34, 0x5C, 0xAA, 0x37, 0x83, 0x1C, 0xA3, 0x08, + 0x88, 0x31, 0xA0, 0x96, 0x40, 0x50, 0x7D, 0x5C, + 0x26, 0x66, 0x8D, 0x71, 0x95, 0xE0, 0xB8, 0x83, + 0x90, 0x8D, 0x9B, 0xBF, 0x85, 0x67, 0x08, 0x88, + 0x9F, 0x96, 0xC4, 0x31, 0xEC, 0xED, 0x6E, 0x9A, + 0x7E, 0x62, 0x20, 0x9A, 0xD2, 0xC6, 0xEA, 0x6B, + 0x65, 0xB2, 0xC5, 0xBB, 0xD3, 0x82, 0xC0, 0x0D, + 0xF6, 0x37, 0x3F, 0xAB, 0x88, 0xFA, 0x7E, 0xFC, + 0x4D, 0xE4, 0x83, 0x32, 0x8F, 0x3A, 0xFF, 0xAC, + 0x12, 0x81, 0xFC, 0x49, 0x0E, 0xA6, 0x37, 0xA0, + 0xFC, 0x40, 0x47, 0x4B, 0x14, 0x42, 0xC0, 0x0B, + 0xE5, 0x2D, 0x26, 0x5C, 0x2B, 0x4D, 0x0C, 0xBE, + 0xC1, 0x63, 0x67, 0x75, 0xB4, 0x1D, 0xB3, 0x9D, + 0xAA, 0xD6, 0x7C, 0xC3, 0x53, 0xD5, 0x0F, 0xE7, + 0x7F, 0xFA, 0xC0, 0xB5, 0x93, 0xBE, 0x18, 0x40, + 0x3B, 0x71, 0x32, 0xF1, 0x0F, 0x78, 0x53, 0xA1, + 0x01, 0xC6, 0xF6, 0x60, 0xEA, 0x50, 0x3A, 0x62, + 0x30, 0xBD, 0x1E, 0xE1, 0x88, 0x7C, 0x30, 0x9F, + 0xE4, 0x8E, 0x3F, + + 0x2A, 0x34, 0xC2, 0x7C, 0xAA, 0x2B, 0xA1, 0xC0, + 0x1A, 0xF5, 0xA8, 0xBC, 0xB8, 0x1E, 0x17, 0x9D, + 0xE3, 0x4E, 0x65, 0xB4, 0x4E, 0x9A, 0xB7, 0x6C, + 0xDD, 0x1E, 0x86, 0xA0, 0x68, 0x4D, 0x83, 0xB7, + 0x06, 0xD3, 0x5E, 0xAC, 0xB0, 0x9F, 0x00, 0x00, + 0x62, 0xD2, 0xCD, 0x67, 0x0C, 0x2D, 0xA9, 0x25, + 0xC1, 0x6E, 0xA8, 0xB0, 0x0E, 0xCF, 0xD9, 0x1D, + 0x5D, 0x5A, 0x04, 0xD9, 0xEA, 0x3F, 0x57, 0xE1, + 0x25, 0x19, 0xF4, 0x9D, 0x3C, 0x7D, 0xE0, 0x65, + 0x98, 0xF2, 0xD1, 0x92, 0xCA, 0x8E, 0x97, 0x6B, + 0x61, 0x58, 0x2B, 0xB4, 0x2A, 0xB4, 0x7A, 0x2A, + 0xEC, 0x93, 0xC4, 0xCB, 0x54, 0x58, 0x96, 0xD1, + 0x9E, 0x90, 0xA2, 0x2E, 0xD8, 0x62, 0x89, 0x97, + 0x2B, 0x43, 0x29, 0x6C, 0xC8, 0x78, 0x5E, 0xB8, + 0x25, 0x9E, 0x23, 0x92, 0x6F, 0x32, 0xAF, 0x4B, + 0x13, 0x6E, 0x1F, 0xB5, 0x8B, 0x8F, 0x46, 0x80, + 0x40, 0xD6, 0x34, 0xD4, 0x0D, 0x23, 0xA6, 0x46, + 0x86, 0x22, 0x51, 0xCF, 0xF5, 0xB6, 0x7E, 0x0B, + 0xD3, 0xE8, 0xD5, 0xAE, 0x47, 0x2C, 0xD9, 0xDA, + 0x61, 0x81, 0x32, 0x0A, 0xAC, 0x32, 0x83, 0x0F, + 0x5D, 0x06, 0x15, 0x19, 0x2D, 0x4E, 0x13, 0x7A, + 0xEC, 0x91, 0xEC, 0xC8, + + 0xB0, 0x44, 0xEA, 0xAC, 0x35, 0x51, 0x2D, 0x29, + 0x50, 0x73, 0x14, 0xC6, 0x5A, 0x7B, 0x9F, 0xD2, + 0x75, 0xE5, 0xE0, 0xE7, 0x66, 0x8A, 0xCB, 0x54, + 0x1D, 0xC4, 0x8A, 0x25, 0x0C, 0x7D, 0xA1, 0xA1, + 0xBA, 0x4E, 0x24, 0xB6, 0xD1, 0xF8, 0x8A, 0x9E, + 0x49, 0xD7, 0xCA, 0x7C, 0x7D, 0x83, 0xDE, 0x3E, + 0xED, 0x6E, 0x26, 0x34, 0x57, 0x6F, 0x00, 0x55, + 0xB9, 0x44, 0x54, 0x82, 0x9B, 0x9A, 0x32, 0xDA, + 0xBB, 0x59, 0x48, 0x80, 0x08, 0x96, 0x2D, 0x34, + 0x68, 0x40, 0x27, 0xB9, 0x00, 0x80, 0xC6, 0x6F, + 0x42, 0x72, 0x3C, 0x9B, 0xD1, 0xA0, 0x01, 0xCC, + 0xD2, 0x0F, 0x33, 0x00, 0x15, 0x6A, 0x65, 0x9B, + 0xED, 0x91, 0xDF, 0x56, 0xE2, 0x81, 0x54, 0x06, + 0xA0, 0xFE, 0x0E, 0xE6, 0x54, 0x16, 0xC5, 0xA6, + 0x89, 0x54, 0x87, 0x7E, 0x49, 0x47, 0x6C, 0x29, + 0x0F, 0x1C, 0x9E, 0xF7, 0x62, 0x23, 0xEB, 0x17, + 0xC2, 0x7B, 0x0D, 0x8B, 0xA0, 0xEF, 0xCE, 0x03, + 0xDE, 0x8D, 0x58, 0x0C, 0x7B, 0x6A, 0xD7, 0x66, + 0x52, 0x6E, 0x93, 0x0C, 0x13, 0xDF, 0x65, 0x28, + 0x66, 0x38, 0xA9, 0x67, 0xE9, 0x22, 0x96, 0x71, + 0xD2, 0xB9, 0xA3, 0xE8, 0x61, 0x49, 0x20, 0x7C, + 0xD5, 0x5B, 0x49, 0xCF, 0x57, + + 0x4B, 0xA0, 0x49, 0xB0, 0xEE, 0xB4, 0xED, 0x67, + 0x08, 0x08, 0x9D, 0xBB, 0x9F, 0xD8, 0x79, 0xE8, + 0x42, 0x71, 0x81, 0xDA, 0x5E, 0x8D, 0x90, 0x1B, + 0x4C, 0x9E, 0x71, 0xCF, 0x46, 0xEB, 0xE7, 0x14, + 0x96, 0x98, 0xA5, 0xDB, 0xFA, 0x35, 0x99, 0x4C, + 0x40, 0x21, 0x5D, 0xF8, 0xB5, 0xC5, 0xAE, 0xE5, + 0x29, 0x68, 0x5F, 0x4A, 0xE9, 0x1D, 0x15, 0x55, + 0xF1, 0x4F, 0x74, 0xD4, 0x15, 0xB3, 0x7D, 0x9F, + 0x37, 0xC8, 0xC8, 0xAF, 0xF1, 0xF0, 0x8D, 0xC4, + 0xF8, 0x36, 0xF0, 0x7A, 0xB9, 0xE6, 0xB8, 0xA9, + 0x66, 0x10, 0xDF, 0xFE, 0x16, 0x7C, 0x8E, 0xDE, + 0x57, 0x0C, 0x75, 0xBF, 0x67, 0x8C, 0x39, 0x49, + 0xF1, 0x66, 0x1D, 0xF2, 0x0D, 0x6D, 0xBB, 0xB4, + 0xC3, 0xC7, 0xD7, 0xE1, 0xB3, 0xEB, 0xE2, 0x0C, + 0x69, 0x4E, 0xC3, 0x60, 0x5B, 0x2B, 0xD5, 0x71, + 0x1D, 0x33, 0xF6, 0x9D, 0xBC, 0xAE, 0x0C, 0x7E, + 0x3B, 0x75, 0x16, 0x73, 0x1D, 0xD5, 0xFA, 0x71, + 0x04, 0xA1, 0x76, 0xA8, 0xCF, 0xB2, 0xD1, 0x8D, + 0x00, 0x83, 0x47, 0x14, 0x28, 0x13, 0x22, 0xF6, + 0x2C, 0xF1, 0xCF, 0xC1, 0xB9, 0x8F, 0x18, 0x42, + 0x88, 0x28, 0x9B, 0x0D, 0xFF, 0xEA, 0x2D, 0xD3, + 0xF8, 0xC5, 0x26, 0x49, 0x67, 0xB4, + + 0x5A, 0xE7, 0x15, 0x4E, 0x9B, 0xC6, 0x57, 0xBD, + 0x13, 0x39, 0xD7, 0xDA, 0x9A, 0xBA, 0x06, 0xDA, + 0x73, 0xEC, 0x64, 0xBF, 0xF4, 0x29, 0xEB, 0x4D, + 0x6B, 0x9A, 0xA2, 0x0F, 0x77, 0x6F, 0xBC, 0x2B, + 0xC5, 0x3F, 0xE8, 0xF0, 0x5A, 0x46, 0x88, 0x3C, + 0x4F, 0xC0, 0x44, 0xE7, 0xAC, 0x89, 0xED, 0xF6, + 0xF3, 0xF2, 0xE1, 0xB7, 0x24, 0x75, 0xAB, 0x72, + 0xC1, 0x06, 0x0E, 0x5D, 0x02, 0x5E, 0x57, 0xA6, + 0xA3, 0x4E, 0x4A, 0xFE, 0x46, 0xF0, 0xB4, 0xDD, + 0x2F, 0xAB, 0xD6, 0xED, 0x49, 0x68, 0x6B, 0xE3, + 0x5D, 0xFF, 0x11, 0x67, 0xA0, 0x38, 0xD1, 0xB6, + 0x6E, 0x98, 0x61, 0x88, 0xA5, 0x60, 0x3E, 0x74, + 0xFB, 0xF6, 0x47, 0x5D, 0xB4, 0xB0, 0x1F, 0xB0, + 0xD0, 0xE8, 0x7B, 0xB7, 0xD4, 0x7D, 0xA1, 0xAF, + 0xD5, 0xD8, 0x5B, 0x83, 0xDF, 0x82, 0x29, 0xA8, + 0xB1, 0x50, 0x2A, 0x06, 0xA6, 0xC0, 0x4C, 0xC0, + 0x96, 0xA4, 0xDB, 0x95, 0x61, 0x19, 0x4E, 0x1E, + 0xB8, 0x79, 0x45, 0x48, 0xCA, 0x22, 0xFD, 0x20, + 0x9E, 0x0E, 0xE1, 0xD7, 0x28, 0x00, 0x16, 0x50, + 0x42, 0xE4, 0x72, 0xB0, 0x61, 0x41, 0xA4, 0x93, + 0x9D, 0xB9, 0x0D, 0x7F, 0x2F, 0xBB, 0xD3, 0x89, + 0xA1, 0xBB, 0xCF, 0xE6, 0xE0, 0x09, 0x52, + + 0x0B, 0x8D, 0x85, 0x02, 0x46, 0xFB, 0x2D, 0x5A, + 0x4A, 0xB7, 0xC1, 0x3A, 0x8C, 0x4D, 0xA0, 0xBD, + 0x43, 0x52, 0xE1, 0x83, 0xA5, 0x6C, 0x78, 0x10, + 0xC1, 0xF1, 0xDF, 0x77, 0x66, 0x2C, 0x30, 0x82, + 0x12, 0x06, 0xBD, 0x79, 0x0A, 0xBD, 0x80, 0x92, + 0xF2, 0xCF, 0xB3, 0x50, 0xF7, 0x3A, 0x80, 0xE2, + 0xE5, 0xB6, 0xC8, 0x65, 0xFF, 0xB9, 0x75, 0x84, + 0x89, 0xB3, 0x63, 0x75, 0xA8, 0xA1, 0x45, 0x3E, + 0x46, 0x97, 0x4A, 0x09, 0xFF, 0xAC, 0x5B, 0xF9, + 0x25, 0x79, 0x49, 0x70, 0xA8, 0x87, 0x95, 0x8D, + 0x59, 0x2F, 0x27, 0x85, 0x02, 0x78, 0x30, 0x9F, + 0x72, 0xDB, 0x11, 0x88, 0x85, 0x09, 0xD4, 0x4B, + 0x71, 0x39, 0xF4, 0x68, 0xFB, 0x40, 0x63, 0x71, + 0xAA, 0xFC, 0x6C, 0xE3, 0x2A, 0xF1, 0x45, 0xF4, + 0xE8, 0x0E, 0x8E, 0xF4, 0xA3, 0xC9, 0x37, 0xA0, + 0xEF, 0xDE, 0x9C, 0x92, 0x42, 0x36, 0x95, 0x30, + 0x0A, 0x9A, 0x06, 0xB8, 0xEB, 0x5C, 0xE2, 0x85, + 0x40, 0x7C, 0x2A, 0xBD, 0xC6, 0x63, 0xCE, 0xBB, + 0x20, 0xC0, 0x1F, 0xE1, 0x5B, 0xDD, 0xD9, 0x62, + 0x2D, 0x30, 0x79, 0xBB, 0x49, 0x65, 0x2A, 0x12, + 0x82, 0x7A, 0x8D, 0x2F, 0xFC, 0xF7, 0xB4, 0x17, + 0x1B, 0x69, 0x5B, 0x90, 0x6D, 0x07, 0x0D, 0x5B, + + 0x99, 0x09, 0x0A, 0xFD, 0xC2, 0x22, 0x9F, 0x2B, + 0x4E, 0xD3, 0x2E, 0x38, 0x10, 0x2F, 0x9D, 0xD4, + 0x5E, 0xFC, 0x93, 0x77, 0x7B, 0x8D, 0xD3, 0xB9, + 0x5E, 0x1C, 0x17, 0x29, 0x4D, 0x0A, 0xA9, 0x5D, + 0x3E, 0xA9, 0x59, 0xD9, 0xBA, 0xF9, 0x5B, 0x21, + 0xF8, 0x59, 0xA8, 0xD5, 0x5C, 0x7E, 0x01, 0xDA, + 0xE5, 0x4F, 0x81, 0x1C, 0xD7, 0x63, 0x16, 0xA4, + 0x2D, 0xE6, 0xFA, 0xAA, 0x18, 0x8C, 0x87, 0xF7, + 0xF2, 0x7C, 0x38, 0x0C, 0x08, 0xD6, 0xA4, 0x1E, + 0x61, 0x19, 0xE7, 0xE9, 0x5B, 0x7C, 0x8F, 0x1A, + 0xB6, 0x4B, 0x7E, 0x78, 0x12, 0x22, 0xF4, 0xCA, + 0x59, 0xAE, 0x69, 0xC7, 0x22, 0x1D, 0xF6, 0xBB, + 0xFC, 0xEF, 0x70, 0x47, 0x0E, 0x5F, 0x85, 0x75, + 0x26, 0xA9, 0x07, 0xFA, 0x6C, 0x59, 0x67, 0x14, + 0x0B, 0x86, 0xCD, 0xA4, 0x84, 0xD9, 0xB1, 0xCF, + 0xC4, 0x26, 0x40, 0x55, 0x3E, 0xFA, 0x5F, 0x08, + 0x0A, 0x89, 0x56, 0x6F, 0x3A, 0xFF, 0xE4, 0x46, + 0x63, 0x93, 0xB9, 0xDE, 0x2C, 0x57, 0x26, 0x96, + 0x38, 0x03, 0x01, 0x80, 0x02, 0x44, 0xBA, 0x96, + 0x20, 0x7C, 0x7C, 0x76, 0x1C, 0x0C, 0x0D, 0x72, + 0x56, 0x28, 0xFD, 0x9F, 0xCB, 0xD5, 0xEE, 0x92, + 0x61, 0x5C, 0x6F, 0x0A, 0x70, 0xBA, 0xD3, 0xFB, + 0xD5, + + 0x79, 0xFE, 0xFA, 0xEB, 0xF2, 0x35, 0xB1, 0x0C, + 0x46, 0x53, 0x2E, 0x26, 0xCE, 0x1A, 0x6E, 0x36, + 0xD0, 0xD1, 0x4C, 0x2B, 0x55, 0x02, 0x8C, 0x39, + 0xA4, 0xC4, 0x14, 0x9B, 0x05, 0x83, 0x80, 0x8A, + 0xE6, 0x05, 0x7E, 0x0A, 0xE9, 0x1A, 0xBC, 0xBD, + 0xAE, 0xDE, 0xCF, 0x33, 0xC4, 0xAB, 0x10, 0x66, + 0x52, 0x56, 0x4D, 0xEA, 0xBD, 0xE2, 0xDA, 0xC8, + 0x77, 0x1D, 0x26, 0x8C, 0xD5, 0x9A, 0x01, 0xFD, + 0xFB, 0x49, 0x89, 0xB8, 0xE1, 0xD0, 0x48, 0x49, + 0x44, 0x23, 0xE2, 0xEF, 0x11, 0x74, 0x67, 0x5E, + 0x14, 0x01, 0x15, 0xEC, 0x39, 0x9B, 0x0A, 0x69, + 0x7C, 0x87, 0xC2, 0xE9, 0x58, 0x05, 0xDE, 0x15, + 0xA2, 0x71, 0x50, 0x73, 0x12, 0x5E, 0x5B, 0x87, + 0x6E, 0xAE, 0xD3, 0x8F, 0x95, 0xE9, 0x77, 0xEF, + 0x2F, 0xD3, 0x63, 0x5B, 0x40, 0xBB, 0xDD, 0xF0, + 0x01, 0x08, 0x7A, 0x5C, 0x92, 0xB6, 0x37, 0xC9, + 0xEB, 0x84, 0x07, 0x65, 0x92, 0xFC, 0xC6, 0xB3, + 0x68, 0x37, 0xED, 0x62, 0xD1, 0x70, 0xD4, 0xBD, + 0x28, 0xD3, 0x46, 0x94, 0x63, 0xD9, 0x56, 0xBE, + 0x93, 0xEC, 0x8A, 0x01, 0xB5, 0xBD, 0xC6, 0x9B, + 0x75, 0x08, 0x06, 0x08, 0xF1, 0xFB, 0x3F, 0xB0, + 0x3E, 0x18, 0x35, 0xF3, 0xFA, 0x85, 0x1A, 0xA7, + 0x9A, 0x8E, + + 0x17, 0x73, 0x8A, 0x98, 0x3B, 0x5B, 0xAD, 0x53, + 0x01, 0x84, 0x65, 0x88, 0xB4, 0x47, 0x4D, 0xC8, + 0x8C, 0x13, 0x47, 0x75, 0xB5, 0x03, 0x43, 0x64, + 0xE5, 0x8D, 0x56, 0xC1, 0xC0, 0x85, 0xE8, 0x25, + 0xE7, 0x2F, 0xC1, 0x98, 0x58, 0xE7, 0x2D, 0xD7, + 0x4D, 0x2B, 0xE0, 0x2D, 0x87, 0x67, 0x72, 0x7E, + 0xA1, 0xAE, 0xF3, 0x86, 0x88, 0xE4, 0x69, 0x23, + 0x30, 0x27, 0xA3, 0x48, 0xDC, 0xA0, 0x67, 0xCC, + 0x54, 0x31, 0xB2, 0xB7, 0x24, 0x23, 0xB9, 0x4A, + 0x5A, 0x07, 0x65, 0x4C, 0x2D, 0x5A, 0x9A, 0x4E, + 0xB4, 0xA6, 0x70, 0xFF, 0x69, 0x3E, 0xD2, 0xC9, + 0x55, 0x2E, 0x4F, 0xC4, 0x2F, 0x1F, 0x89, 0x98, + 0x76, 0x45, 0x5F, 0xBD, 0x3A, 0x8C, 0xB6, 0x55, + 0x93, 0xB0, 0xA7, 0x4E, 0x75, 0xBD, 0x7D, 0x25, + 0x08, 0x8C, 0xAD, 0x13, 0x33, 0x24, 0x57, 0xA9, + 0x1C, 0x44, 0x71, 0x86, 0x7E, 0xC4, 0xC4, 0x1C, + 0x5B, 0x1E, 0x83, 0x37, 0x1B, 0x37, 0x98, 0x9A, + 0xB4, 0xDC, 0xA1, 0xC2, 0x1A, 0x69, 0x89, 0xE1, + 0xD9, 0x67, 0x41, 0x58, 0x72, 0x6A, 0xF8, 0x22, + 0x69, 0xEA, 0x53, 0xF5, 0x3C, 0x5E, 0x53, 0x5D, + 0x98, 0x9A, 0x2A, 0xDA, 0xF6, 0x30, 0xA0, 0xBA, + 0xCE, 0xF8, 0xB7, 0x4D, 0xE2, 0xBF, 0x05, 0xAA, + 0xF7, 0x44, 0x84, + + 0x97, 0xDD, 0xB6, 0xB9, 0xD3, 0xA3, 0x8A, 0x20, + 0x05, 0x15, 0xD1, 0x14, 0x69, 0x33, 0x98, 0xFF, + 0x77, 0xDA, 0x47, 0x11, 0xC3, 0x59, 0x19, 0xFB, + 0x86, 0xEE, 0x3A, 0xB9, 0x4F, 0x61, 0x07, 0x6C, + 0x7B, 0x01, 0x89, 0x87, 0x1E, 0xA0, 0xBB, 0x34, + 0x60, 0x89, 0xAA, 0xE9, 0xE5, 0x03, 0xE4, 0xF7, + 0xA7, 0x16, 0xD0, 0xA9, 0x08, 0x3F, 0xA1, 0x25, + 0x57, 0xB4, 0xAD, 0x52, 0x80, 0x53, 0xB3, 0x3B, + 0x58, 0x58, 0x96, 0x37, 0x25, 0xEB, 0x08, 0xD9, + 0x63, 0xFA, 0x1F, 0xC8, 0x92, 0xF0, 0x50, 0x0D, + 0xAE, 0xFB, 0x16, 0x2E, 0xA7, 0xB1, 0x1E, 0x71, + 0x46, 0xCE, 0xE6, 0xA9, 0x4B, 0x67, 0xAC, 0xD8, + 0xC7, 0x01, 0x18, 0xA0, 0x83, 0xD5, 0xBA, 0xA9, + 0x94, 0xCD, 0x2C, 0x8C, 0x90, 0x86, 0xE9, 0x80, + 0x3E, 0x94, 0x0B, 0x0C, 0xDD, 0xF0, 0xB8, 0xFE, + 0x29, 0x9D, 0x9F, 0xAB, 0x16, 0xE6, 0x08, 0x12, + 0x67, 0x29, 0x45, 0x57, 0x4D, 0xB9, 0x08, 0xF3, + 0x44, 0x4E, 0x5C, 0x9C, 0x92, 0x69, 0xF6, 0x41, + 0x90, 0x17, 0x92, 0xF5, 0x1B, 0x8B, 0xBB, 0x89, + 0x2E, 0x3A, 0x7F, 0xAF, 0x82, 0x59, 0xA4, 0x94, + 0x53, 0x40, 0xF5, 0x6A, 0x2A, 0xF5, 0xA8, 0x4F, + 0x48, 0x2D, 0x01, 0x61, 0x6A, 0x98, 0xFD, 0xF8, + 0xBC, 0x46, 0xFC, 0x6F, + + 0x2F, 0x20, 0x5D, 0x1C, 0xAC, 0x77, 0x49, 0x4E, + 0x6A, 0x21, 0x43, 0x6D, 0x67, 0x9C, 0xF6, 0x87, + 0xC9, 0xAF, 0x8B, 0x58, 0xF0, 0xCB, 0x8B, 0x9C, + 0x41, 0x7D, 0x77, 0x66, 0xFF, 0x9D, 0x6D, 0xA5, + 0x23, 0xAC, 0x7E, 0x35, 0x18, 0xC1, 0x8E, 0x96, + 0x5F, 0x9B, 0xD8, 0xDF, 0xEA, 0xA1, 0x1A, 0x4E, + 0xE5, 0x43, 0x6E, 0x05, 0xE7, 0xBF, 0x0B, 0x81, + 0xD5, 0x73, 0x6C, 0x37, 0x43, 0x82, 0xF4, 0x40, + 0x8F, 0x6E, 0xA2, 0x7B, 0xEC, 0x28, 0x7B, 0xD5, + 0xA1, 0xCB, 0x38, 0xF1, 0x62, 0x5F, 0xCC, 0xCF, + 0xB5, 0x87, 0x38, 0x03, 0x5D, 0x1E, 0xC9, 0x01, + 0x4C, 0xE1, 0x97, 0xD7, 0xC5, 0x92, 0xE2, 0xEB, + 0x5B, 0x49, 0xC3, 0xB9, 0xB9, 0x9D, 0x16, 0x8D, + 0x54, 0x57, 0xB7, 0x08, 0xA9, 0xF2, 0x1E, 0x2B, + 0x6B, 0x8C, 0x0C, 0x1E, 0xD6, 0x8D, 0x0E, 0xA6, + 0x51, 0xB5, 0xC7, 0x02, 0xC1, 0x34, 0x92, 0x95, + 0x8A, 0xEA, 0xF3, 0xCD, 0x37, 0x54, 0x09, 0x74, + 0x47, 0x04, 0x93, 0x73, 0xD9, 0x91, 0x31, 0x33, + 0x91, 0x4E, 0xF5, 0x08, 0x0A, 0x8A, 0x50, 0x15, + 0x9F, 0x08, 0x36, 0xC5, 0x74, 0xEF, 0x2B, 0xD2, + 0x1E, 0x05, 0xD1, 0xB7, 0x2E, 0x57, 0x3B, 0x25, + 0x0F, 0x91, 0x65, 0x48, 0xEB, 0x2F, 0xF3, 0xDB, + 0xF8, 0xE2, 0x7B, 0xAF, 0x93, + + 0xB0, 0xC5, 0x54, 0xAD, 0x36, 0x9B, 0x5C, 0x03, + 0x86, 0x9C, 0x35, 0x73, 0xD5, 0xF9, 0x0F, 0x5F, + 0xEF, 0x3A, 0xFF, 0x73, 0x9E, 0xC4, 0xDB, 0x64, + 0x4F, 0x3E, 0xEF, 0xD2, 0x32, 0x5A, 0x7F, 0xA5, + 0xBF, 0x2C, 0x81, 0x00, 0xBB, 0xFA, 0x5F, 0x98, + 0x86, 0x71, 0x26, 0x39, 0x8F, 0xB7, 0x84, 0x38, + 0x9D, 0xE4, 0xF7, 0x99, 0x21, 0xB9, 0x0D, 0xF7, + 0x6F, 0x7F, 0xB8, 0x68, 0x8F, 0x7E, 0x7E, 0x4F, + 0xA7, 0x15, 0x2A, 0x82, 0xFC, 0x60, 0xCF, 0x70, + 0xE9, 0x86, 0xBD, 0x24, 0xA1, 0xBA, 0x30, 0xB2, + 0x70, 0x66, 0xBC, 0x6F, 0x1C, 0x39, 0x94, 0xB5, + 0x81, 0x3C, 0x54, 0x98, 0x3C, 0x82, 0xE3, 0xF8, + 0x6D, 0x3C, 0x1B, 0x5A, 0x7A, 0x81, 0x7A, 0x29, + 0xEF, 0x48, 0x3B, 0x35, 0x3F, 0x73, 0xF0, 0x05, + 0x12, 0xB6, 0xC9, 0x2D, 0xDA, 0xCB, 0x81, 0xBD, + 0xEE, 0x8F, 0x8E, 0xE7, 0x06, 0x8D, 0xD2, 0x76, + 0x39, 0x60, 0xF2, 0x57, 0xD6, 0xE5, 0xFC, 0x81, + 0x14, 0x49, 0xB4, 0x6B, 0x06, 0x6E, 0x33, 0xDC, + 0x2C, 0x2F, 0x4E, 0x15, 0xC8, 0xC2, 0x41, 0x95, + 0xA8, 0x65, 0x59, 0xCA, 0x97, 0xD8, 0xA0, 0x4A, + 0x1F, 0xCA, 0x35, 0xC5, 0x21, 0xBD, 0x51, 0x29, + 0x2B, 0x0A, 0x1B, 0x37, 0x4C, 0xF7, 0xAA, 0x88, + 0xDF, 0x44, 0x88, 0x05, 0x2F, 0x4B, + + 0x50, 0xDC, 0xA7, 0x8B, 0xED, 0x47, 0x10, 0x06, + 0x63, 0xCB, 0x7D, 0x5B, 0xE6, 0xB0, 0xED, 0x90, + 0x55, 0xF0, 0x38, 0x3C, 0x80, 0x34, 0x2B, 0x62, + 0x92, 0xC5, 0x61, 0x0D, 0xEC, 0x0E, 0xD3, 0xEA, + 0xD6, 0x81, 0xB8, 0x4D, 0xC6, 0x3E, 0xC5, 0x19, + 0x70, 0x31, 0x0F, 0x2C, 0x65, 0x07, 0x0F, 0x43, + 0x2F, 0xA0, 0x63, 0xD7, 0xFB, 0x9B, 0xD1, 0xA9, + 0x60, 0x3C, 0xFE, 0x4E, 0x09, 0xAF, 0xC7, 0x04, + 0x4A, 0xE6, 0x75, 0x18, 0xF6, 0x53, 0x87, 0x91, + 0xD6, 0x33, 0x15, 0x9C, 0x32, 0xA0, 0x52, 0x82, + 0xC7, 0xF9, 0x17, 0x9C, 0x58, 0x2C, 0xBC, 0x75, + 0x88, 0x1B, 0x7D, 0x71, 0xC3, 0xCC, 0xFE, 0x60, + 0x08, 0xDD, 0xB0, 0x5F, 0x45, 0x49, 0x08, 0x91, + 0xF6, 0x42, 0x97, 0x85, 0xBB, 0xF7, 0x04, 0xAB, + 0xFF, 0xB6, 0xB6, 0x37, 0xA3, 0xA8, 0x1D, 0x41, + 0x52, 0x9F, 0x47, 0xC2, 0xD4, 0xE1, 0x32, 0x22, + 0x26, 0xF5, 0x26, 0x0A, 0x03, 0xD5, 0xDD, 0x37, + 0x95, 0xBB, 0xC0, 0xEF, 0x4C, 0x61, 0xC9, 0x87, + 0x43, 0x88, 0x22, 0xE9, 0x5F, 0x30, 0x0A, 0x64, + 0x73, 0xF3, 0x2F, 0xD2, 0x4D, 0xE8, 0x91, 0x1C, + 0x68, 0xBE, 0x35, 0xC3, 0xF4, 0xC4, 0xD3, 0xD2, + 0x01, 0xB2, 0x19, 0x28, 0x30, 0x86, 0xD9, 0xFC, + 0x16, 0x4A, 0x0E, 0xCC, 0xCA, 0xD6, 0xF6, + + 0x74, 0xAF, 0x14, 0xA0, 0x6E, 0x50, 0x25, 0x74, + 0x8F, 0xC7, 0x16, 0x03, 0x56, 0x10, 0x01, 0x76, + 0x5B, 0x63, 0xD0, 0xEF, 0x4E, 0xA7, 0x59, 0x27, + 0x43, 0x0C, 0x3F, 0x8C, 0xFD, 0xB6, 0x09, 0x17, + 0x81, 0xD7, 0xEC, 0x94, 0x50, 0x4B, 0xC8, 0x22, + 0x3F, 0x82, 0x11, 0xA1, 0x58, 0x8C, 0x23, 0xBB, + 0x8B, 0xFB, 0x4F, 0x34, 0x31, 0x98, 0x63, 0x1B, + 0xC4, 0x8E, 0xCF, 0x12, 0x57, 0x68, 0x14, 0x2B, + 0x23, 0xA9, 0x9D, 0x17, 0x84, 0x2A, 0x53, 0xE7, + 0x67, 0x5A, 0xC1, 0x6E, 0x65, 0x25, 0x70, 0x25, + 0x76, 0x70, 0x30, 0x24, 0x85, 0xF8, 0x1F, 0x9D, + 0xFD, 0x8D, 0x84, 0xAD, 0x33, 0x07, 0xFD, 0x21, + 0x01, 0x13, 0xC0, 0x14, 0x20, 0xF2, 0xDC, 0x81, + 0x21, 0xEF, 0x3A, 0xCB, 0x32, 0x23, 0xB2, 0x76, + 0x3B, 0xA4, 0x30, 0xB6, 0x8F, 0x90, 0xE3, 0x20, + 0x4C, 0x81, 0x6A, 0xF4, 0x2C, 0xC3, 0x4C, 0x86, + 0x08, 0x86, 0x1E, 0x31, 0x48, 0x55, 0x7E, 0xF2, + 0xA7, 0xF5, 0xC6, 0x22, 0x19, 0x87, 0xE5, 0x6E, + 0xA6, 0xC5, 0x31, 0xF6, 0x1D, 0xA4, 0xD8, 0x75, + 0x90, 0x1C, 0x57, 0x01, 0xF5, 0xE8, 0x53, 0x3E, + 0x0A, 0x16, 0xDD, 0x40, 0x58, 0x0C, 0xE0, 0x8F, + 0xBF, 0x57, 0xFA, 0x1D, 0x37, 0x15, 0xC6, 0x63, + 0xC1, 0x57, 0x0C, 0x6D, 0x83, 0x6C, 0xA4, 0x0E, + + 0x6A, 0x33, 0x55, 0x52, 0x04, 0x3E, 0x41, 0x83, + 0x22, 0xED, 0x2A, 0xAC, 0x33, 0xEF, 0xA6, 0xA9, + 0x8E, 0x21, 0x9D, 0x2B, 0xFB, 0x20, 0x80, 0x22, + 0x0F, 0x7A, 0xB4, 0x67, 0xFF, 0x76, 0xFA, 0x1D, + 0x1B, 0x26, 0xF9, 0xD4, 0x3A, 0x66, 0x99, 0x4C, + 0xFD, 0x45, 0x61, 0x96, 0x02, 0x3F, 0xD3, 0x13, + 0xE4, 0x44, 0x30, 0x30, 0x6B, 0x76, 0x77, 0xEA, + 0x66, 0x69, 0x1B, 0x82, 0xE6, 0xA2, 0x50, 0x69, + 0xFD, 0x5D, 0xAD, 0x59, 0x82, 0x94, 0xA1, 0xF3, + 0x78, 0x5B, 0x00, 0x7B, 0x6E, 0xE9, 0xA2, 0xB9, + 0xB4, 0x56, 0xF9, 0xD5, 0x5C, 0x42, 0x89, 0xA5, + 0x7E, 0x86, 0xFE, 0x61, 0x6C, 0xF1, 0x0A, 0xFC, + 0xC9, 0x7D, 0x9C, 0xF0, 0xE7, 0x7F, 0xFE, 0xCD, + 0x85, 0xEC, 0x68, 0x68, 0xC5, 0x6F, 0x06, 0x35, + 0x4B, 0xA6, 0x15, 0xF8, 0x38, 0xF5, 0x1B, 0x56, + 0x20, 0x75, 0xCE, 0xA4, 0xCB, 0x5F, 0x60, 0x96, + 0xE7, 0xEF, 0x71, 0x1B, 0x1D, 0xF5, 0xD1, 0x96, + 0xA1, 0x9C, 0x64, 0xFA, 0x2F, 0x88, 0x9C, 0x87, + 0x53, 0xC5, 0x08, 0xE5, 0x93, 0xD0, 0x6E, 0xDB, + 0xA1, 0x84, 0x1C, 0x49, 0x79, 0x38, 0x48, 0xDD, + 0x6B, 0xD0, 0x80, 0x52, 0x51, 0x4A, 0x96, 0x9C, + 0x4A, 0x48, 0xB3, 0x9B, 0x86, 0x2F, 0xDC, 0x32, + 0x34, 0x27, 0x04, 0xAB, 0x37, 0xB2, 0x50, 0x6C, + 0xB8, + + 0x86, 0x42, 0xAF, 0x92, 0x97, 0x70, 0x4B, 0x6A, + 0xC3, 0x85, 0x01, 0xE0, 0xFE, 0x10, 0x0A, 0x58, + 0xD8, 0xF8, 0x1F, 0x0C, 0xA0, 0xF3, 0x41, 0x40, + 0x1F, 0x90, 0x54, 0x07, 0x52, 0x34, 0x2C, 0xC8, + 0x20, 0x3A, 0xA5, 0x1D, 0x12, 0x59, 0xC5, 0xCA, + 0x3F, 0x60, 0x7E, 0x9F, 0xB0, 0xB3, 0x66, 0x99, + 0x67, 0xD6, 0x0B, 0x05, 0x54, 0xB5, 0xE3, 0x7E, + 0xC6, 0x97, 0xF3, 0xD5, 0xC0, 0x73, 0xE9, 0xE2, + 0x70, 0x20, 0x78, 0x15, 0x74, 0x2F, 0x19, 0xCC, + 0x0F, 0xA9, 0xCC, 0xE1, 0xAE, 0x60, 0x6A, 0x05, + 0x97, 0xE4, 0x5A, 0x6F, 0xF9, 0xB6, 0x40, 0x31, + 0xE1, 0x41, 0xF2, 0x72, 0x3E, 0xB2, 0xEB, 0x78, + 0x33, 0x7E, 0x0A, 0xB4, 0x39, 0xE3, 0xEE, 0x99, + 0x17, 0x6C, 0x2F, 0x2F, 0xA8, 0x3C, 0x2C, 0xFC, + 0x70, 0x53, 0x53, 0x14, 0x82, 0x62, 0xCA, 0x55, + 0x23, 0x20, 0xFF, 0x7F, 0xC5, 0xE9, 0x28, 0xB9, + 0xA2, 0x15, 0x82, 0x6B, 0xED, 0x1C, 0xBB, 0xD6, + 0x74, 0x8E, 0xE5, 0x2F, 0x54, 0x97, 0x05, 0xAB, + 0x88, 0xF8, 0x6E, 0x5E, 0x45, 0x52, 0x61, 0x3B, + 0xCD, 0x9F, 0x3E, 0xE0, 0x43, 0x30, 0x37, 0x73, + 0x50, 0xE3, 0x21, 0x29, 0xAA, 0x48, 0x7B, 0xDE, + 0x50, 0x2B, 0xBC, 0x34, 0xBF, 0xB3, 0x0F, 0xF7, + 0xCB, 0x78, 0x4B, 0xDE, 0x7B, 0x1D, 0xAC, 0xFC, + 0xEB, 0x03, + + 0x76, 0xD3, 0x4B, 0xE8, 0x19, 0x32, 0xEE, 0xA7, + 0xD5, 0x21, 0xE4, 0xFF, 0x60, 0x32, 0xED, 0xE4, + 0x6D, 0xC3, 0xE2, 0xA4, 0xD2, 0xEC, 0x0B, 0x41, + 0x8E, 0xF2, 0xE3, 0x83, 0x84, 0x18, 0xE5, 0xF4, + 0xBA, 0xE7, 0x17, 0xED, 0xF7, 0xF6, 0xC8, 0x6C, + 0xA9, 0xC7, 0xF7, 0x23, 0x9E, 0x0F, 0xEC, 0x4C, + 0x51, 0x20, 0xB8, 0xEF, 0x02, 0xCC, 0xFF, 0xBB, + 0xDE, 0x1C, 0x47, 0x8A, 0xAB, 0x2D, 0xDF, 0xF6, + 0x86, 0x0B, 0xC8, 0xFB, 0xDE, 0x24, 0x64, 0x1F, + 0x99, 0x4F, 0x44, 0xC9, 0x78, 0x45, 0xD2, 0x2A, + 0x7A, 0x3B, 0x1C, 0xC5, 0xD8, 0x87, 0x29, 0xEB, + 0x5E, 0x02, 0x12, 0x93, 0xF7, 0xED, 0x21, 0xFF, + 0x50, 0xDB, 0xD9, 0xD4, 0x0B, 0x1C, 0xC9, 0x1D, + 0xF2, 0x1F, 0x9C, 0x93, 0x2F, 0xDA, 0x90, 0xB0, + 0xF9, 0x25, 0x4F, 0x25, 0xE7, 0x64, 0xB4, 0xD6, + 0x78, 0xB3, 0x68, 0xD8, 0x46, 0xCF, 0xE4, 0xC7, + 0x15, 0x28, 0xC2, 0x34, 0x6E, 0xB2, 0x4F, 0x12, + 0xD2, 0x89, 0xDE, 0x77, 0xDA, 0x58, 0x40, 0x62, + 0x8B, 0x55, 0xF7, 0x80, 0x82, 0x3C, 0xC3, 0xAC, + 0x7C, 0xF4, 0x2E, 0xDD, 0xC4, 0x70, 0x52, 0x63, + 0x17, 0x7F, 0x44, 0xF7, 0x65, 0x59, 0x3D, 0x91, + 0x95, 0x18, 0xB0, 0xA4, 0xA0, 0xAF, 0x52, 0x3A, + 0xD3, 0xC0, 0xDD, 0x0C, 0x00, 0xC1, 0xD7, 0xA6, + 0x1D, 0x5E, 0x35, + + 0xC5, 0x4F, 0x57, 0x22, 0xF0, 0xE2, 0x42, 0xEE, + 0x03, 0x5C, 0xDE, 0x88, 0x0A, 0x6E, 0xEB, 0xA4, + 0x41, 0xEF, 0x55, 0x9D, 0x37, 0x21, 0xAE, 0x2A, + 0xA9, 0x9D, 0x92, 0xDC, 0xCF, 0x5F, 0xD3, 0x28, + 0x23, 0x5D, 0x22, 0xDB, 0x89, 0x31, 0x4D, 0x60, + 0x08, 0x92, 0x78, 0x3A, 0x5D, 0xAE, 0x91, 0x1A, + 0x93, 0xCD, 0x4D, 0x62, 0x40, 0xA2, 0x18, 0x1A, + 0x6B, 0xCE, 0x34, 0x0D, 0x3B, 0x09, 0xC6, 0xE6, + 0x3F, 0x1B, 0x88, 0x19, 0x14, 0x29, 0xAD, 0x75, + 0x06, 0x1B, 0xD9, 0xE2, 0xEC, 0xE7, 0x1B, 0xC9, + 0x7A, 0xD3, 0x18, 0x86, 0xE7, 0x36, 0xAD, 0x21, + 0x7D, 0xA2, 0xDC, 0x1C, 0x97, 0xBA, 0xB2, 0xCB, + 0x59, 0x19, 0x7D, 0x20, 0xC5, 0x68, 0xB8, 0xBD, + 0x51, 0x49, 0x5B, 0x45, 0xAB, 0x47, 0x33, 0x63, + 0xA2, 0xEC, 0x4C, 0x2D, 0x69, 0x91, 0x27, 0x86, + 0x72, 0x28, 0xDB, 0x96, 0x55, 0x5D, 0x1A, 0x2F, + 0x7E, 0x5D, 0xC6, 0x0A, 0x69, 0xAB, 0x8C, 0x8B, + 0x36, 0x87, 0xB4, 0x1A, 0x15, 0xE4, 0x68, 0x21, + 0xDB, 0x65, 0x87, 0xBA, 0x24, 0xBD, 0x1F, 0xFA, + 0x5C, 0x4B, 0x48, 0x21, 0x83, 0xD8, 0x04, 0x50, + 0x31, 0x83, 0xF7, 0x21, 0xC5, 0xA8, 0xA9, 0x8A, + 0x5E, 0xC6, 0x1C, 0x40, 0x8E, 0x29, 0xB3, 0xF2, + 0xA8, 0x86, 0xED, 0x60, 0xCA, 0xF8, 0x24, 0x7F, + 0x09, 0xAE, 0x36, 0x3F, + + 0x6D, 0x99, 0x39, 0x36, 0xA1, 0x3E, 0x29, 0x65, + 0x55, 0xA1, 0x90, 0x10, 0xB5, 0x49, 0x64, 0xE3, + 0x09, 0xB2, 0xDB, 0x65, 0xF1, 0x5E, 0x05, 0x1E, + 0x0E, 0x6A, 0x8C, 0x38, 0xDF, 0xE1, 0xB8, 0x87, + 0x15, 0x2C, 0xCB, 0x4D, 0xD7, 0x44, 0xDB, 0x3B, + 0x80, 0xA2, 0xEA, 0x9A, 0x7A, 0xA8, 0x39, 0xE2, + 0x33, 0xFC, 0x40, 0xCD, 0x92, 0xB8, 0x62, 0x1D, + 0x9E, 0xC7, 0x65, 0x65, 0x3C, 0x24, 0x6A, 0x77, + 0x11, 0xA5, 0xEE, 0x86, 0x19, 0x5E, 0xCF, 0x01, + 0xF7, 0x74, 0x64, 0xAA, 0xDF, 0xD3, 0x0B, 0x54, + 0xF8, 0xEB, 0x9F, 0xE5, 0x3B, 0x9B, 0x3A, 0xCB, + 0x36, 0x65, 0xA6, 0xD3, 0x8B, 0x5D, 0xD3, 0x55, + 0xC4, 0xD6, 0x26, 0x46, 0x5D, 0x66, 0x4E, 0xEB, + 0x7F, 0xB3, 0x1F, 0x36, 0x12, 0x92, 0x50, 0xF4, + 0xE3, 0x2B, 0x90, 0x32, 0xFF, 0x56, 0x90, 0x81, + 0xC3, 0x52, 0x7A, 0x26, 0x67, 0xA5, 0x4E, 0xC1, + 0x3B, 0xB9, 0x44, 0x4B, 0x6F, 0x96, 0xD7, 0xD4, + 0xE9, 0x67, 0x10, 0x36, 0xD8, 0xAB, 0xC3, 0x0C, + 0x17, 0xCF, 0xC1, 0x38, 0xC9, 0xD6, 0x7B, 0xB9, + 0xFC, 0xF9, 0x1D, 0x49, 0x87, 0x84, 0xCF, 0x86, + 0x72, 0xFD, 0x95, 0x15, 0xBF, 0x27, 0x1F, 0xAB, + 0x64, 0x81, 0xCF, 0x99, 0x96, 0x2E, 0x15, 0x57, + 0xA5, 0xD7, 0x7D, 0x0E, 0xB9, 0xDC, 0x6A, 0x44, + 0xFC, 0x80, 0x80, 0x66, 0x2E, + + 0x9C, 0x95, 0xB5, 0x62, 0xE3, 0x90, 0x98, 0xF3, + 0x02, 0x26, 0x65, 0x18, 0x99, 0x4B, 0xCC, 0x67, + 0x42, 0xCF, 0x7B, 0x21, 0xC4, 0xB7, 0x97, 0x99, + 0x50, 0xBD, 0x05, 0x49, 0xB4, 0xA3, 0x9E, 0xCA, + 0x85, 0xD1, 0xD0, 0x65, 0xF3, 0x69, 0x8E, 0x21, + 0x49, 0xCA, 0x23, 0x89, 0x8F, 0x9A, 0x03, 0x7B, + 0xB0, 0x55, 0xE4, 0x21, 0x7D, 0x00, 0xC1, 0xFA, + 0xBC, 0x24, 0x65, 0x5C, 0x83, 0x67, 0x5B, 0x97, + 0x01, 0xCB, 0x00, 0x82, 0xF2, 0xA1, 0x3E, 0xE8, + 0xCC, 0xCE, 0x00, 0x5A, 0x79, 0xB5, 0xF7, 0x13, + 0xD8, 0x9D, 0x57, 0x8F, 0x98, 0x49, 0x80, 0x72, + 0xC4, 0xA5, 0x7C, 0x03, 0x91, 0x77, 0x1F, 0x82, + 0x07, 0x42, 0x88, 0x36, 0x00, 0x9B, 0x09, 0x87, + 0x90, 0xE6, 0x6C, 0x53, 0xF6, 0x3A, 0x5F, 0x6E, + 0x71, 0xA3, 0xDB, 0x58, 0x49, 0x69, 0xCC, 0x9B, + 0x69, 0x85, 0x43, 0xFF, 0x1E, 0xEA, 0x53, 0x3C, + 0xF5, 0xD9, 0x7C, 0x63, 0xF6, 0x85, 0xEB, 0xE0, + 0xE9, 0x20, 0x6F, 0xB1, 0x5F, 0xFE, 0xEA, 0xF1, + 0x30, 0x1F, 0x7E, 0x61, 0x10, 0x74, 0x29, 0x06, + 0xB4, 0x9F, 0xAC, 0xA5, 0x3E, 0xC1, 0xAF, 0xF8, + 0x6A, 0x3F, 0x7E, 0xDE, 0x75, 0x67, 0xB7, 0x79, + 0xAB, 0x96, 0x82, 0xD9, 0x52, 0x08, 0xC9, 0xEA, + 0x1F, 0x69, 0xBF, 0xF6, 0xEA, 0xE2, 0x6F, 0x7B, + 0xB7, 0x2F, 0xED, 0xA8, 0x50, 0xD4, + + 0x5E, 0xAA, 0x52, 0xBD, 0xD4, 0x77, 0xAF, 0x8B, + 0x18, 0x01, 0xE0, 0xF7, 0xC4, 0xE7, 0x4B, 0x86, + 0xAB, 0x7D, 0x11, 0xE6, 0x70, 0x98, 0x34, 0x7C, + 0x53, 0x63, 0x75, 0xFE, 0xE8, 0xB4, 0x7A, 0x95, + 0x59, 0x0A, 0x4D, 0x78, 0x65, 0x7E, 0x93, 0xEE, + 0xD0, 0x84, 0x3D, 0xE0, 0xB6, 0xA2, 0x2D, 0x8A, + 0x36, 0x68, 0x04, 0xB4, 0x84, 0x38, 0x14, 0x1E, + 0x7A, 0x61, 0x6B, 0x60, 0xA0, 0x4A, 0xE1, 0x7D, + 0x4B, 0xB8, 0x8F, 0x31, 0x49, 0x66, 0xE2, 0x3E, + 0x73, 0xE5, 0xE1, 0x73, 0xB9, 0x9A, 0x4D, 0xAD, + 0xD5, 0xED, 0x42, 0x53, 0x41, 0x42, 0x21, 0x2E, + 0xC3, 0x36, 0x8B, 0x0A, 0x40, 0xB5, 0xB4, 0x0C, + 0xB3, 0xF0, 0x53, 0x34, 0xD7, 0xE5, 0xE0, 0x70, + 0xB1, 0xEA, 0x86, 0x5E, 0x9A, 0x39, 0x30, 0x95, + 0x84, 0xC7, 0x57, 0x93, 0xC9, 0x7A, 0xF6, 0x10, + 0x3D, 0x3F, 0x68, 0x33, 0xC3, 0x4F, 0x45, 0x36, + 0xB0, 0xA4, 0xDF, 0x58, 0xE8, 0xB7, 0x4A, 0x42, + 0x8C, 0x6D, 0x04, 0xCF, 0x24, 0xA5, 0x69, 0x95, + 0xDE, 0x04, 0xC3, 0xA8, 0xDD, 0xE3, 0x78, 0x44, + 0xA0, 0x9F, 0x11, 0x9B, 0xB4, 0xA2, 0x27, 0x7A, + 0x15, 0xA3, 0x9D, 0x27, 0x80, 0x35, 0xB4, 0x9F, + 0x33, 0xBE, 0x27, 0x3A, 0xE1, 0x1F, 0x67, 0x97, + 0x1D, 0x1A, 0x7D, 0x11, 0x59, 0xEF, 0x01, 0x5E, + 0x8E, 0xD9, 0xA8, 0x16, 0x94, 0x54, 0xD9, + + 0x49, 0x52, 0x78, 0x3A, 0x42, 0x33, 0x66, 0xEF, + 0x71, 0xB1, 0xF3, 0x8B, 0x98, 0xB2, 0x65, 0x8A, + 0x8A, 0x5D, 0xE8, 0x6B, 0x9B, 0xFB, 0xEF, 0x15, + 0x86, 0x3C, 0x68, 0x1D, 0x3A, 0x5C, 0xA7, 0xD8, + 0x3B, 0x33, 0x95, 0xF0, 0x9C, 0xB8, 0xA6, 0x92, + 0x11, 0x79, 0xB2, 0xD9, 0x3C, 0x18, 0x7F, 0xBB, + 0xF9, 0xAA, 0x43, 0x01, 0xD1, 0x7D, 0xC7, 0xB7, + 0x0D, 0x49, 0x44, 0x0D, 0x36, 0x0B, 0xD9, 0xC6, + 0x54, 0x79, 0x0A, 0x63, 0x79, 0x86, 0xBD, 0x0B, + 0xC4, 0x03, 0xF5, 0xFC, 0x1E, 0x37, 0xE5, 0x24, + 0x30, 0x5A, 0x35, 0x1D, 0x13, 0xE8, 0x35, 0x9E, + 0x42, 0x0F, 0xDA, 0xB1, 0x80, 0xD1, 0xE6, 0xE5, + 0x7A, 0xD5, 0xFE, 0x66, 0x8B, 0x38, 0xD7, 0x5E, + 0xD8, 0xE0, 0x67, 0xC0, 0xE0, 0x06, 0x10, 0xE6, + 0x18, 0xC7, 0x7A, 0xF4, 0xCF, 0xFD, 0xB2, 0xB4, + 0x1C, 0xF5, 0x46, 0x7D, 0xDF, 0x7C, 0x83, 0xA5, + 0xAB, 0xC3, 0x7C, 0xE1, 0x3F, 0x0A, 0xA3, 0xAA, + 0x37, 0x9F, 0x86, 0x8F, 0xAD, 0x26, 0xB8, 0x84, + 0xF4, 0x91, 0xF7, 0x19, 0x0A, 0x7B, 0x1B, 0x46, + 0x63, 0x43, 0x08, 0x41, 0x07, 0x0D, 0x19, 0xF1, + 0xFF, 0x8D, 0x23, 0xF1, 0xE8, 0x42, 0x54, 0x4C, + 0xBC, 0x89, 0x34, 0xA3, 0x90, 0xDC, 0x8A, 0x9F, + 0xA0, 0xFD, 0xC9, 0x65, 0xAB, 0x6B, 0x29, 0x71, + 0x6C, 0xA9, 0xF4, 0x77, 0x50, 0xA5, 0xEA, 0xBB, + + 0x9C, 0xB1, 0xAA, 0x0D, 0xDB, 0xC1, 0x1A, 0x9C, + 0x5C, 0x98, 0x93, 0xD8, 0x01, 0xC7, 0xFE, 0x24, + 0xBC, 0x60, 0x5F, 0x0D, 0xA4, 0x50, 0xC2, 0x9D, + 0x11, 0x34, 0xDE, 0x31, 0xE9, 0xD2, 0x45, 0x49, + 0x6F, 0xC2, 0x91, 0x8C, 0x75, 0x1D, 0xB8, 0xD8, + 0x27, 0xA1, 0x5F, 0xF2, 0x66, 0xBA, 0xD2, 0x5C, + 0xD6, 0xBB, 0x5B, 0x45, 0x57, 0x08, 0xF3, 0x0A, + 0x5C, 0x58, 0xAA, 0x0B, 0x79, 0x12, 0x2F, 0x2D, + 0x42, 0x37, 0xCD, 0x5C, 0xB3, 0xF1, 0x8F, 0x34, + 0xE2, 0x61, 0xC3, 0x45, 0xFC, 0xFF, 0x96, 0x53, + 0x0B, 0x1A, 0x71, 0xB5, 0x5F, 0xA2, 0x12, 0x26, + 0x78, 0xCC, 0xBD, 0x5A, 0x84, 0x35, 0xAE, 0xF2, + 0xA3, 0xC3, 0x42, 0x5F, 0x51, 0x22, 0x77, 0x91, + 0x08, 0xDF, 0x1C, 0x68, 0xAE, 0x0F, 0x51, 0x79, + 0x4E, 0x75, 0xED, 0x8D, 0x55, 0x2A, 0x54, 0x88, + 0x97, 0xF8, 0x90, 0x24, 0x14, 0xE3, 0x07, 0x4D, + 0xCD, 0x26, 0x06, 0xA7, 0x35, 0xCF, 0x6A, 0x38, + 0xB7, 0x98, 0x2F, 0xA8, 0xAE, 0x6C, 0x3D, 0xB5, + 0x1E, 0x37, 0xD1, 0xE9, 0xD4, 0x53, 0x67, 0x4B, + 0xA4, 0xAE, 0xFA, 0x51, 0x01, 0x40, 0x8B, 0xD1, + 0x09, 0x74, 0x4C, 0x7F, 0xB2, 0xFA, 0xF9, 0xA8, + 0x27, 0xAF, 0xC8, 0x24, 0x2C, 0x31, 0xA1, 0xF6, + 0xF8, 0x81, 0xAC, 0xB4, 0x02, 0x83, 0x6F, 0x49, + 0xCF, 0xEC, 0xDF, 0x71, 0x5E, 0x2F, 0x33, 0x07, + 0xBA, + + 0x55, 0xA8, 0x19, 0x38, 0x4C, 0x45, 0x5C, 0xF7, + 0x03, 0x9A, 0x21, 0x1B, 0xFF, 0xA8, 0xF7, 0x76, + 0x3E, 0xF2, 0x8E, 0xF1, 0xCD, 0x69, 0xDB, 0x0F, + 0xF6, 0x1C, 0x6A, 0x92, 0x71, 0xEF, 0x4A, 0x3E, + 0xCE, 0xF9, 0xEA, 0xAB, 0x76, 0x6B, 0x1E, 0xB5, + 0x02, 0x61, 0xA7, 0x02, 0x76, 0xBA, 0x6B, 0x1D, + 0xE5, 0xF0, 0x12, 0xDF, 0x1D, 0x61, 0x82, 0x90, + 0x91, 0xDA, 0x96, 0x60, 0x89, 0x88, 0x83, 0xCD, + 0xBF, 0xAC, 0xA8, 0x86, 0xC4, 0x7F, 0xA5, 0xA3, + 0x3E, 0x50, 0xC4, 0x8B, 0x30, 0xD8, 0x98, 0x89, + 0x0E, 0x10, 0xA3, 0x77, 0x64, 0xBF, 0x44, 0xE8, + 0x7B, 0xF4, 0x56, 0xA2, 0x87, 0x06, 0xF6, 0xF0, + 0x98, 0x77, 0x92, 0x5D, 0xED, 0x6A, 0x29, 0x33, + 0x22, 0x31, 0xD5, 0x3A, 0xD4, 0xDB, 0x11, 0x34, + 0x7D, 0xA0, 0xFC, 0x96, 0x54, 0x61, 0x16, 0x2C, + 0x48, 0x6C, 0x55, 0x2B, 0xE0, 0x8C, 0xE3, 0x7F, + 0x05, 0x32, 0xFB, 0xCE, 0xD4, 0x2F, 0x47, 0x54, + 0xE5, 0x9D, 0x6B, 0x68, 0x85, 0xF0, 0xD6, 0xCD, + 0xEA, 0xB6, 0x48, 0x59, 0xA8, 0xE6, 0x0B, 0x80, + 0x21, 0x2A, 0x62, 0x74, 0x1E, 0xD9, 0x11, 0x9A, + 0x16, 0x8A, 0xB0, 0xC5, 0x2C, 0xF2, 0x77, 0x80, + 0x0D, 0x74, 0x24, 0xE9, 0x29, 0x65, 0x47, 0x1F, + 0xEE, 0xF0, 0xA4, 0x26, 0xAF, 0xC5, 0x4B, 0x83, + 0x67, 0x23, 0x7C, 0x2E, 0x54, 0x29, 0x88, 0xD0, + 0x6A, 0xCB, + + 0x35, 0x07, 0x1D, 0x30, 0xAA, 0x5F, 0xC8, 0x44, + 0xDA, 0x6D, 0xB6, 0xCB, 0x0B, 0xD6, 0x54, 0x54, + 0x48, 0x98, 0x07, 0xE6, 0xB0, 0x33, 0x48, 0xCF, + 0xB7, 0xAF, 0x58, 0xF6, 0x2A, 0x09, 0xD2, 0x7A, + 0xB5, 0xB4, 0x93, 0x6C, 0xC3, 0x83, 0xFB, 0x17, + 0xE5, 0xBF, 0xDD, 0xE3, 0x10, 0x37, 0x69, 0xF9, + 0x90, 0xEE, 0x65, 0x1E, 0x8F, 0x82, 0x14, 0xC5, + 0x38, 0xF3, 0x96, 0x22, 0x63, 0x81, 0xD4, 0x7A, + 0x23, 0x2E, 0x9A, 0xFD, 0xD4, 0xBB, 0xE1, 0x56, + 0xDD, 0xB6, 0xD0, 0x77, 0x0C, 0x88, 0x50, 0x18, + 0xE3, 0x25, 0xBC, 0x33, 0x36, 0xB0, 0xD9, 0x65, + 0xD2, 0xC2, 0xAA, 0x26, 0xB4, 0x52, 0xEF, 0x8B, + 0x19, 0x74, 0x14, 0x69, 0xB9, 0x60, 0x0B, 0x59, + 0x2C, 0xC7, 0x74, 0x0F, 0x6B, 0xF7, 0x5C, 0x4F, + 0xC7, 0xC8, 0xED, 0xFF, 0x1F, 0x02, 0x46, 0x46, + 0x93, 0x38, 0x6F, 0x34, 0x2A, 0x0C, 0xEC, 0x3E, + 0x3F, 0xAA, 0xA0, 0xEB, 0x43, 0x24, 0x84, 0x55, + 0xAF, 0x43, 0x08, 0x0B, 0xDD, 0x0D, 0x9D, 0x01, + 0x5C, 0x47, 0x27, 0x47, 0x3A, 0x0D, 0xA8, 0x27, + 0xAE, 0x04, 0x25, 0x27, 0x5D, 0xF6, 0x19, 0x6B, + 0x2E, 0x5A, 0x1B, 0x70, 0xBD, 0xB3, 0x44, 0x23, + 0x59, 0x4F, 0xE4, 0xA0, 0x02, 0x78, 0x22, 0xA6, + 0x25, 0xA8, 0x55, 0x59, 0xB5, 0xA0, 0x60, 0x47, + 0x57, 0x1F, 0x62, 0xC2, 0x16, 0x38, 0x7D, 0x86, + 0xB9, 0x0F, 0x8A, + + 0xDD, 0x6E, 0xDE, 0x6B, 0x5B, 0x0B, 0xEA, 0xD1, + 0xB1, 0xB9, 0xD8, 0xA3, 0xCE, 0xE3, 0x95, 0x79, + 0x16, 0x68, 0x90, 0x54, 0x7A, 0xC4, 0x4E, 0x77, + 0x95, 0xD6, 0xBE, 0xAF, 0x0E, 0xE0, 0x88, 0xDA, + 0x28, 0xF2, 0xEA, 0x52, 0x77, 0xFE, 0x7F, 0xAA, + 0x3D, 0xD4, 0xCA, 0x1B, 0xBC, 0xAC, 0xC5, 0x0D, + 0xE2, 0x50, 0x70, 0x57, 0x12, 0x14, 0x54, 0x97, + 0x22, 0x3E, 0x43, 0x82, 0x0F, 0xA1, 0xA8, 0x93, + 0x4A, 0xD3, 0x7E, 0xE1, 0x1E, 0xBE, 0xD5, 0x3E, + 0x36, 0x61, 0xB1, 0x0A, 0x85, 0x6F, 0x3B, 0xF4, + 0x60, 0x39, 0xEE, 0xE5, 0x81, 0x88, 0xC2, 0x6E, + 0x24, 0xAC, 0xBD, 0x0C, 0x25, 0xE7, 0xAB, 0xB0, + 0xC8, 0xA1, 0x8F, 0x09, 0xE3, 0xCA, 0x77, 0xE3, + 0x50, 0x80, 0x6A, 0x42, 0x94, 0x2C, 0x43, 0x85, + 0x71, 0xB8, 0x4A, 0xCC, 0xBA, 0x0C, 0x5D, 0xBF, + 0x2C, 0xF8, 0x36, 0x5D, 0x79, 0xFB, 0x3C, 0x11, + 0x15, 0x7C, 0x47, 0xDD, 0xC7, 0x26, 0x71, 0x44, + 0xAF, 0x17, 0xAE, 0x74, 0xF1, 0xC5, 0xE0, 0x00, + 0x5C, 0xF0, 0x96, 0x38, 0x86, 0x1A, 0x7F, 0xA1, + 0xBC, 0x0E, 0x32, 0xCA, 0x54, 0xBF, 0x5F, 0xEA, + 0x76, 0x31, 0x1B, 0x1B, 0x70, 0xE5, 0xB9, 0xA8, + 0x18, 0x58, 0xF4, 0x31, 0x92, 0xDD, 0x25, 0xB6, + 0xF5, 0xC1, 0x6B, 0x0D, 0xFC, 0x97, 0x29, 0xD1, + 0x6F, 0xE2, 0xCD, 0xC6, 0x41, 0x24, 0x28, 0x68, + 0x42, 0x23, 0x34, 0xF5, + + 0xC7, 0xD2, 0x23, 0x12, 0x22, 0x4E, 0x47, 0x8E, + 0xB9, 0x0E, 0x31, 0x80, 0xA3, 0xB3, 0x72, 0xE1, + 0xB4, 0xE6, 0x43, 0x4C, 0x9C, 0xDE, 0xBD, 0x34, + 0x79, 0xD8, 0x2D, 0x82, 0xA2, 0x79, 0x64, 0x54, + 0x13, 0x9C, 0xFC, 0x21, 0x3A, 0x7C, 0xFC, 0x84, + 0x87, 0xB6, 0x94, 0xD8, 0x8E, 0x6A, 0xBD, 0xA7, + 0xC5, 0x2E, 0xC6, 0x88, 0x46, 0xF9, 0xA3, 0xFE, + 0x66, 0x6D, 0x10, 0xB3, 0x07, 0x27, 0xDE, 0xDC, + 0x84, 0xEC, 0xFB, 0x15, 0xC7, 0x6C, 0x9E, 0x3E, + 0x03, 0x41, 0x29, 0x8F, 0x06, 0xDB, 0xF1, 0x5E, + 0x21, 0xB7, 0x5E, 0x42, 0x9C, 0x7D, 0x21, 0xC8, + 0x78, 0x5E, 0x6E, 0x19, 0x77, 0x21, 0x41, 0xF6, + 0x5D, 0x2F, 0x6A, 0xC1, 0xCB, 0xB8, 0x62, 0x4B, + 0xDD, 0xD4, 0xBE, 0x2E, 0xAD, 0x05, 0x6E, 0x4F, + 0x96, 0x4C, 0x02, 0x5D, 0x75, 0xE7, 0x0E, 0x50, + 0xA5, 0x45, 0xB3, 0x47, 0xAC, 0xAE, 0xED, 0x0C, + 0x94, 0x6A, 0x5E, 0x4A, 0x66, 0xE9, 0x2A, 0x2A, + 0x5B, 0x00, 0xA7, 0xEC, 0xDE, 0xE7, 0xE8, 0xAA, + 0x01, 0x51, 0x4F, 0x2B, 0x67, 0x95, 0x2E, 0xA7, + 0xE3, 0x05, 0x9D, 0x45, 0x40, 0x68, 0x7F, 0xBB, + 0x98, 0xCB, 0x54, 0x8C, 0xEB, 0x29, 0xAD, 0xE2, + 0xA6, 0x8C, 0xBB, 0x33, 0xCD, 0xB2, 0x79, 0x6E, + 0x37, 0x01, 0xEB, 0xF6, 0x19, 0x70, 0x08, 0x08, + 0x17, 0x4F, 0x8E, 0x45, 0x2A, 0x8A, 0x97, 0x91, + 0x8D, 0x4F, 0x53, 0x4B, 0x3F, + + 0xCA, 0x0D, 0x28, 0x4B, 0x0A, 0x4D, 0xD4, 0x03, + 0xDA, 0xAA, 0x7E, 0x33, 0x7D, 0x98, 0x9D, 0xE0, + 0x5B, 0x29, 0x94, 0x19, 0xA4, 0xD8, 0xC7, 0xF8, + 0x11, 0xF0, 0xD9, 0x18, 0x9A, 0x17, 0x1A, 0x14, + 0xE1, 0x4E, 0x4F, 0x09, 0x45, 0x7D, 0x17, 0xE9, + 0xB7, 0xA2, 0x6B, 0x0A, 0xE6, 0x17, 0x57, 0x05, + 0x24, 0xDC, 0xC6, 0x75, 0xAE, 0xBC, 0xAE, 0x32, + 0x1B, 0x60, 0x5A, 0x5D, 0x77, 0xB4, 0x42, 0x48, + 0x99, 0xE7, 0xB5, 0xEB, 0xE5, 0x7A, 0x10, 0x93, + 0x5D, 0x81, 0xCA, 0x01, 0x2F, 0x41, 0xD4, 0x0F, + 0xDE, 0xBE, 0xC4, 0x59, 0xC3, 0xDB, 0xDE, 0xF3, + 0x14, 0xFC, 0xC5, 0x8B, 0x22, 0xCC, 0x6A, 0x3F, + 0x0C, 0xCC, 0x53, 0x3D, 0x39, 0x0C, 0x83, 0xED, + 0x51, 0x54, 0x4A, 0xD9, 0x2D, 0x0C, 0x5E, 0x1D, + 0x0B, 0x82, 0x73, 0x0C, 0x41, 0xCD, 0x8D, 0xD0, + 0xD9, 0xD3, 0x7C, 0x8D, 0x3D, 0xFA, 0xA9, 0x51, + 0xE5, 0x13, 0x8B, 0x81, 0x32, 0x62, 0xAB, 0xB6, + 0xB1, 0xF2, 0x1C, 0x5C, 0xE6, 0x9A, 0xB0, 0x83, + 0x40, 0x92, 0xB8, 0x85, 0x22, 0x7E, 0x53, 0x9A, + 0x0F, 0x87, 0xDD, 0xBA, 0x20, 0x83, 0xE4, 0x93, + 0xDE, 0x35, 0x8E, 0xBD, 0x2F, 0xA3, 0x04, 0xF2, + 0x87, 0x62, 0x9E, 0xD9, 0x72, 0xBC, 0xC7, 0x0C, + 0x8F, 0x6F, 0x48, 0x93, 0x0D, 0x48, 0x9B, 0x21, + 0xA7, 0xD4, 0x12, 0x0C, 0x06, 0x61, 0x9D, 0xC2, + 0x45, 0x51, 0x17, 0x6D, 0xD2, 0x95, + + 0x15, 0x94, 0x79, 0xF5, 0xEB, 0x7F, 0xDD, 0x18, + 0x2E, 0x9C, 0x4B, 0xA0, 0xD2, 0x3D, 0xEF, 0x37, + 0x82, 0xDB, 0xDC, 0xFB, 0x8D, 0x07, 0x87, 0x30, + 0x89, 0x79, 0x31, 0xB1, 0x27, 0xAF, 0x6B, 0x9B, + 0x0C, 0xAB, 0xA3, 0x72, 0xA4, 0xE3, 0x96, 0x7B, + 0x61, 0x4F, 0xB4, 0x0D, 0xD1, 0x64, 0x9D, 0xD5, + 0x3D, 0x0F, 0x24, 0x3C, 0xFF, 0x39, 0xA3, 0x08, + 0x7E, 0xD0, 0x4A, 0x14, 0xF2, 0x16, 0x08, 0x12, + 0x58, 0x6F, 0xE4, 0xC3, 0xEE, 0x77, 0xDA, 0xF0, + 0x21, 0x0E, 0x8A, 0xFB, 0x19, 0x46, 0x08, 0xCF, + 0x47, 0x93, 0x76, 0xE8, 0x15, 0x7F, 0x07, 0xC7, + 0x44, 0x4A, 0x35, 0xA9, 0x65, 0x58, 0xF6, 0x21, + 0xCA, 0x2E, 0xAD, 0xE1, 0x21, 0xE2, 0x25, 0x8F, + 0x87, 0xA4, 0xFD, 0xE1, 0xEF, 0xE9, 0x25, 0x70, + 0x7E, 0x41, 0x4D, 0x5A, 0x20, 0xC6, 0x0C, 0xD4, + 0xE5, 0x49, 0xF3, 0xB9, 0xDB, 0xE5, 0xCE, 0x21, + 0x1D, 0xE5, 0xA9, 0x73, 0x5E, 0x91, 0xFA, 0x86, + 0x5A, 0x25, 0x52, 0x27, 0xF5, 0x4C, 0x92, 0xC1, + 0x14, 0x52, 0x0E, 0x78, 0x6E, 0xCD, 0x8F, 0x0F, + 0xD5, 0x40, 0x42, 0x01, 0xD1, 0x6A, 0xD2, 0xCE, + 0xD8, 0xC0, 0x6A, 0x3E, 0x82, 0xF0, 0x41, 0x0D, + 0x44, 0x24, 0xE2, 0x43, 0xA3, 0xE8, 0x68, 0x12, + 0x46, 0x2E, 0x7A, 0x72, 0x04, 0xE0, 0xE6, 0x38, + 0x4D, 0x62, 0x7F, 0x02, 0x59, 0x17, 0x8F, 0xB9, + 0x75, 0x74, 0x25, 0x7F, 0x95, 0xBF, 0x9C, + + 0xC5, 0x38, 0x2D, 0xA3, 0x3D, 0xF8, 0x27, 0x28, + 0x56, 0x3E, 0x35, 0x24, 0x8D, 0x8F, 0x56, 0xBD, + 0xA9, 0x6D, 0x58, 0x99, 0x12, 0xC7, 0xAB, 0x4A, + 0xE0, 0xF7, 0xDE, 0x1E, 0xF0, 0x40, 0x89, 0x0C, + 0xE4, 0x7B, 0xF8, 0x7B, 0x8C, 0x1B, 0x6F, 0xC1, + 0xD4, 0x6E, 0xEF, 0x45, 0x2E, 0x7E, 0x03, 0x0F, + 0x3B, 0x0F, 0x48, 0x28, 0x8D, 0x8C, 0xAF, 0x9E, + 0xB3, 0x55, 0x6B, 0xA7, 0xAA, 0x03, 0x94, 0x21, + 0xED, 0x7A, 0x92, 0x81, 0x12, 0x37, 0xAF, 0x4B, + 0xE1, 0x34, 0x62, 0xC6, 0x7D, 0x5E, 0xA8, 0xD6, + 0x9A, 0xB8, 0x12, 0xF0, 0xB8, 0x7E, 0x92, 0x88, + 0xDB, 0x67, 0x97, 0x56, 0x88, 0xA4, 0x22, 0x18, + 0xFC, 0xC3, 0xCD, 0x1B, 0x12, 0x09, 0xC9, 0xD6, + 0x71, 0xE1, 0x76, 0xED, 0x95, 0x61, 0x96, 0xF9, + 0xB1, 0xFA, 0x16, 0x9F, 0xDC, 0x3A, 0x3C, 0x9A, + 0xFB, 0x25, 0x0A, 0x0C, 0x8B, 0xFB, 0xD1, 0x7E, + 0x13, 0x27, 0x5B, 0x24, 0x95, 0xC4, 0x6B, 0xAC, + 0x32, 0xB5, 0x1D, 0xC9, 0x96, 0x67, 0x84, 0x43, + 0x5C, 0xAD, 0xD2, 0xF2, 0x15, 0xDA, 0x83, 0x84, + 0x71, 0xB2, 0x14, 0x04, 0x20, 0xDD, 0x04, 0x78, + 0x6E, 0xCD, 0x7F, 0x73, 0xD1, 0xAD, 0xBE, 0xD4, + 0x4F, 0xED, 0xF8, 0xE9, 0xEA, 0x07, 0xBD, 0xB7, + 0xDD, 0x0F, 0xFB, 0xF5, 0x88, 0x6A, 0x3E, 0x8A, + 0xCD, 0x79, 0xBA, 0xDE, 0x98, 0xB3, 0x74, 0x3D, + 0x0F, 0x92, 0x40, 0x10, 0xE4, 0x26, 0x85, 0x2D, + + 0x0E, 0x46, 0xBD, 0x8B, 0xDC, 0xE8, 0x28, 0x6A, + 0x31, 0x78, 0xE6, 0xDE, 0x37, 0xC0, 0x68, 0x2D, + 0xE0, 0x38, 0xD0, 0x2F, 0x28, 0xBB, 0xA2, 0xF4, + 0x8F, 0x82, 0x35, 0xDF, 0x0F, 0xB9, 0x31, 0x98, + 0xF6, 0x03, 0x66, 0x80, 0xCF, 0xDA, 0xF0, 0x0B, + 0x4C, 0x62, 0x2F, 0x6E, 0x7A, 0x1B, 0x80, 0x98, + 0x7A, 0xDB, 0x1F, 0x2B, 0xC2, 0x2F, 0x96, 0x80, + 0x58, 0x98, 0x21, 0xFC, 0xBB, 0x1A, 0x13, 0xAF, + 0x56, 0xA5, 0x4A, 0x89, 0xDB, 0xF0, 0x28, 0x97, + 0x43, 0xB6, 0x45, 0xBF, 0x58, 0x76, 0x78, 0x8A, + 0x83, 0xF8, 0x6E, 0xC1, 0x90, 0x99, 0x59, 0x0A, + 0xF2, 0x1E, 0x95, 0x8A, 0x13, 0x58, 0xC0, 0xC7, + 0x8F, 0x27, 0xD9, 0x53, 0xEC, 0xD0, 0x24, 0xFC, + 0x83, 0x60, 0x74, 0x24, 0x2D, 0xD5, 0xE5, 0x51, + 0x27, 0x0E, 0x62, 0x17, 0xD4, 0xAA, 0xF1, 0x30, + 0x6F, 0x35, 0x4C, 0xE0, 0x91, 0x2D, 0xDA, 0x11, + 0x68, 0xDF, 0x6F, 0xEA, 0x29, 0xC0, 0xB4, 0x75, + 0x04, 0x4E, 0xB7, 0x49, 0x47, 0x19, 0xA1, 0x7F, + 0x99, 0x0B, 0x45, 0x95, 0x3F, 0x6A, 0x97, 0x8B, + 0xA8, 0x27, 0x1C, 0xBF, 0x07, 0x7D, 0x30, 0xD9, + 0x97, 0xD7, 0xA5, 0x2D, 0x22, 0x23, 0xF7, 0x1D, + 0xFD, 0x4A, 0xE9, 0xA8, 0xFD, 0x10, 0xE4, 0xEF, + 0x0F, 0xBB, 0x54, 0x2B, 0xFC, 0x74, 0xDE, 0x55, + 0xF7, 0x51, 0xBF, 0x8B, 0x73, 0x38, 0xF8, 0x5E, + 0xFF, 0x8C, 0x96, 0xFF, 0xFA, 0x82, 0x67, 0xC7, + 0xA6, + + 0x78, 0xC3, 0xA1, 0xCB, 0x49, 0x80, 0x93, 0x83, + 0x6D, 0x3D, 0x8C, 0xB6, 0x38, 0x79, 0x45, 0xCF, + 0x65, 0x32, 0x71, 0xE6, 0x00, 0x24, 0x23, 0x45, + 0x47, 0x37, 0x84, 0x54, 0x1B, 0x7E, 0x25, 0x5B, + 0x86, 0xA5, 0x55, 0x07, 0xB4, 0x0B, 0xCD, 0x23, + 0x6B, 0xF3, 0x33, 0xCB, 0xCA, 0x7C, 0xBC, 0x7F, + 0x43, 0x50, 0x02, 0xA2, 0x45, 0x1C, 0xD3, 0x32, + 0x7C, 0x28, 0x6D, 0xA1, 0x24, 0x2A, 0x58, 0x29, + 0xC1, 0xB2, 0x6C, 0x71, 0x1A, 0xA4, 0xA9, 0xB3, + 0x27, 0xAB, 0xBC, 0xC6, 0x9F, 0x9D, 0x92, 0xB6, + 0x70, 0x99, 0xA6, 0x9B, 0xAD, 0x8A, 0x59, 0xC9, + 0x08, 0xE0, 0xFB, 0x92, 0xB0, 0x0B, 0x03, 0x75, + 0xBA, 0xC0, 0x88, 0x56, 0xF2, 0x8B, 0xCD, 0x22, + 0xB8, 0x2B, 0xEA, 0xD8, 0x79, 0xD1, 0xA5, 0xF4, + 0x98, 0xEF, 0x71, 0xE9, 0xFE, 0xBC, 0x0F, 0xEA, + 0x5B, 0x8D, 0x96, 0xF6, 0xB7, 0x03, 0xEF, 0xA6, + 0x61, 0x05, 0x8F, 0xB3, 0x0D, 0xB0, 0xCA, 0xC1, + 0xCF, 0x25, 0xB2, 0x87, 0x98, 0x13, 0xB8, 0xEC, + 0xA8, 0x54, 0xF6, 0xF5, 0x5F, 0xB2, 0x11, 0x52, + 0x14, 0xD7, 0x9E, 0x36, 0x93, 0x62, 0x3B, 0x24, + 0x92, 0x9C, 0x63, 0xB7, 0x2D, 0x18, 0xAA, 0x10, + 0x89, 0x84, 0x29, 0x16, 0xF0, 0xDF, 0x7C, 0x31, + 0x0C, 0x3E, 0x25, 0xB3, 0x72, 0x50, 0xE3, 0x51, + 0xDC, 0x12, 0x58, 0xA7, 0xED, 0x38, 0x43, 0x92, + 0x1B, 0xA5, 0x54, 0xE1, 0xC6, 0x56, 0x30, 0x65, + 0x0A, 0xA5, + + 0x02, 0xF5, 0xEC, 0x03, 0x39, 0xF9, 0x74, 0xC8, + 0xAC, 0xC7, 0xF7, 0xE5, 0x62, 0x30, 0xB3, 0x0E, + 0xBA, 0x02, 0xD6, 0x48, 0x76, 0x84, 0xDC, 0x9F, + 0xF0, 0x79, 0x39, 0x06, 0xA9, 0x82, 0x53, 0x00, + 0xAB, 0x09, 0x0C, 0x16, 0xDE, 0xC1, 0x05, 0xC8, + 0x2C, 0x92, 0x02, 0x61, 0xEA, 0x87, 0x8C, 0xFC, + 0x8A, 0x0B, 0x9F, 0x04, 0x9F, 0x51, 0x5F, 0x7B, + 0x4F, 0xAC, 0xF1, 0x4F, 0x9A, 0x29, 0x61, 0x42, + 0x8C, 0xD7, 0x23, 0x49, 0x63, 0xEB, 0xFD, 0x2C, + 0xDC, 0x76, 0xF1, 0xF0, 0xC8, 0xA2, 0x97, 0x5C, + 0x20, 0x23, 0x7C, 0x9F, 0x5D, 0xDE, 0x59, 0x87, + 0x3A, 0x33, 0xE6, 0x77, 0x83, 0xE8, 0xF7, 0xA8, + 0x31, 0x7E, 0xFA, 0x02, 0x79, 0x96, 0x13, 0xCB, + 0x08, 0xB3, 0x0E, 0x18, 0x2E, 0x2C, 0x48, 0x1B, + 0x8D, 0xF7, 0x40, 0x0F, 0xD0, 0x95, 0x29, 0x99, + 0xA9, 0x8C, 0xF5, 0xBA, 0xC3, 0xE7, 0x1E, 0x3F, + 0x34, 0x13, 0x02, 0x24, 0xF0, 0x4E, 0xC6, 0xCD, + 0xEE, 0xA1, 0xF0, 0x0F, 0xAE, 0xB8, 0x31, 0x0F, + 0x5F, 0x34, 0x4F, 0xAC, 0x17, 0x52, 0x26, 0x84, + 0xA0, 0x65, 0x88, 0x2E, 0xA2, 0x75, 0xC0, 0x18, + 0x6A, 0x2C, 0xDD, 0x0F, 0xF9, 0x0E, 0xFA, 0x56, + 0xBB, 0x71, 0xAD, 0x69, 0xCB, 0x96, 0x31, 0xCF, + 0x66, 0x1C, 0x87, 0x71, 0x77, 0x11, 0x39, 0xB7, + 0xE9, 0x25, 0x7A, 0x88, 0x8A, 0x0C, 0xE9, 0xCD, + 0x0F, 0xB9, 0x82, 0x18, 0x66, 0x2A, 0xD1, 0x91, + 0xD9, 0x4F, 0x8D, + + 0xE9, 0xF8, 0x7F, 0xAE, 0x1B, 0x66, 0x68, 0x95, + 0x73, 0xE2, 0x74, 0x01, 0xE1, 0x7D, 0x2A, 0x45, + 0xC0, 0x88, 0x27, 0xB0, 0x1A, 0x6F, 0xD0, 0x5E, + 0xA6, 0x05, 0x9D, 0x18, 0xB8, 0xA2, 0x86, 0x0E, + 0xB3, 0xF7, 0x79, 0xC3, 0x41, 0xBE, 0x1D, 0x34, + 0x69, 0x19, 0x07, 0x2F, 0x81, 0x48, 0xFC, 0xC1, + 0x35, 0xA4, 0x98, 0xFF, 0xAA, 0xA7, 0xF9, 0xC7, + 0x32, 0x63, 0xBB, 0x56, 0xA7, 0xBE, 0x31, 0x88, + 0xA1, 0x35, 0x57, 0x17, 0x1B, 0x00, 0x5A, 0x0C, + 0xAF, 0x48, 0xE8, 0xA6, 0x63, 0x5F, 0x6B, 0xB9, + 0x4D, 0x44, 0x0E, 0xCB, 0xA2, 0xDB, 0x7E, 0xBD, + 0xC1, 0x99, 0x7A, 0x02, 0xAE, 0xDF, 0x01, 0xA6, + 0x19, 0x8A, 0xA0, 0x91, 0xA8, 0x4E, 0x34, 0xF6, + 0x4A, 0x53, 0xD3, 0x44, 0xF6, 0x3C, 0x5A, 0x2D, + 0x16, 0xAD, 0x5F, 0x09, 0x0B, 0xE2, 0xE8, 0xD0, + 0xFE, 0x09, 0x6F, 0x7A, 0xDB, 0x86, 0x57, 0xA9, + 0x7B, 0x06, 0x7D, 0xC5, 0x99, 0x4C, 0xA5, 0xE2, + 0xEA, 0x18, 0x0A, 0xFD, 0xB6, 0xF2, 0x03, 0xEA, + 0xEC, 0xAF, 0x7B, 0xA1, 0x9C, 0x31, 0xDF, 0x17, + 0x74, 0x6D, 0x01, 0x9E, 0xBF, 0x76, 0x63, 0x2D, + 0x5B, 0xA0, 0xF6, 0xD1, 0xC2, 0x01, 0x40, 0xDF, + 0xE1, 0x81, 0xD6, 0xCB, 0x67, 0xC7, 0x96, 0x6C, + 0xE6, 0x34, 0x9E, 0xA5, 0x23, 0xCA, 0x9B, 0x06, + 0x96, 0x45, 0xFA, 0xB8, 0x96, 0x3A, 0x66, 0x32, + 0x5F, 0xF1, 0x92, 0xA1, 0x64, 0xF8, 0x2B, 0x90, + 0x65, 0x5D, 0xD7, 0xF8, + + 0x8F, 0xEC, 0x60, 0x90, 0x57, 0x5B, 0x8B, 0x0F, + 0xF5, 0xF9, 0xF1, 0xA6, 0x11, 0x9A, 0xE5, 0x47, + 0x50, 0x3A, 0x7D, 0x92, 0x1C, 0x2D, 0xBE, 0x3F, + 0x0B, 0x75, 0x18, 0x78, 0xD1, 0x29, 0xBA, 0x37, + 0x3F, 0xD6, 0x89, 0xE6, 0x14, 0x54, 0x58, 0x70, + 0x0F, 0x17, 0x62, 0x4E, 0xE3, 0x8E, 0xC0, 0x18, + 0x7B, 0x5C, 0xCB, 0x31, 0x95, 0x16, 0x9E, 0xEC, + 0x0B, 0x7B, 0xF4, 0x1D, 0xC6, 0x30, 0xB4, 0xBD, + 0xAA, 0x29, 0x33, 0x94, 0xCC, 0x30, 0x98, 0x44, + 0x20, 0x4E, 0xF3, 0x50, 0x9C, 0x75, 0x5C, 0xB0, + 0xB9, 0x1F, 0x60, 0xDF, 0xFA, 0x57, 0xAF, 0xC1, + 0x69, 0x50, 0xFD, 0x09, 0x9B, 0x62, 0xEB, 0xE5, + 0x9C, 0x09, 0xAB, 0x83, 0x34, 0xDE, 0x9F, 0x48, + 0xF7, 0x2B, 0x21, 0xB7, 0xC5, 0x90, 0x15, 0xFF, + 0x9D, 0x5F, 0xD7, 0x5A, 0x4F, 0x6D, 0xB7, 0x8C, + 0xD5, 0x74, 0x17, 0xEB, 0xD2, 0xF5, 0xE2, 0xCF, + 0x6E, 0xE0, 0xE3, 0xCD, 0xD7, 0x11, 0xD7, 0xA6, + 0x89, 0xF4, 0xDE, 0xEF, 0xEB, 0x77, 0xCD, 0xA3, + 0x2C, 0x1F, 0xDF, 0xED, 0x93, 0xA6, 0x51, 0xC8, + 0xD4, 0x45, 0x72, 0x11, 0x7F, 0xC8, 0x68, 0x86, + 0x4B, 0x6E, 0x13, 0x92, 0x2B, 0xDA, 0x6F, 0xE4, + 0x86, 0xBB, 0xF4, 0x71, 0x8E, 0xA4, 0x2A, 0x55, + 0x3C, 0xC6, 0x37, 0x67, 0x96, 0xFF, 0x40, 0xF9, + 0x1E, 0xDD, 0x95, 0x5A, 0x89, 0x0C, 0x07, 0x59, + 0x90, 0x4F, 0xCF, 0x83, 0xE8, 0x0B, 0x93, 0x73, + 0xAA, 0x8A, 0x15, 0x0A, 0x21, + + 0x8F, 0xB7, 0xC6, 0xB8, 0x22, 0xAE, 0x29, 0x71, + 0x39, 0xA5, 0x99, 0xC3, 0x6F, 0xBD, 0x8D, 0xF2, + 0x74, 0x24, 0x7E, 0xFF, 0x4B, 0x35, 0x63, 0xF4, + 0xD2, 0xA8, 0x55, 0xCD, 0x1A, 0x1A, 0x40, 0x64, + 0x45, 0x61, 0xD8, 0x26, 0xA0, 0x23, 0x3E, 0xDC, + 0x57, 0x05, 0xB8, 0xC4, 0xC0, 0x0C, 0xF4, 0x2E, + 0x3F, 0xBE, 0x36, 0xFB, 0x84, 0xDF, 0x5A, 0x19, + 0x87, 0x78, 0x6B, 0x31, 0x2D, 0xB5, 0x03, 0x98, + 0x92, 0x3C, 0x5F, 0xD7, 0x5F, 0x06, 0x2A, 0x00, + 0x5C, 0x7C, 0xF1, 0x4F, 0x70, 0x36, 0xBB, 0xE0, + 0xDC, 0x5B, 0x9A, 0x10, 0x87, 0x13, 0x05, 0x58, + 0xB2, 0x9C, 0xEB, 0xFE, 0xCF, 0x82, 0x03, 0x18, + 0x6A, 0x37, 0x8D, 0x11, 0xD9, 0x15, 0x1E, 0x27, + 0x54, 0x8E, 0x7A, 0x30, 0x13, 0xD8, 0x1A, 0xBB, + 0x6C, 0xAD, 0xC4, 0xFD, 0x61, 0x2E, 0x03, 0xEB, + 0x2C, 0x2D, 0x01, 0x4A, 0x3A, 0xCA, 0xDF, 0x23, + 0x8E, 0x39, 0x81, 0x6D, 0x62, 0xEE, 0x12, 0x89, + 0xC6, 0xA3, 0x1D, 0x38, 0xA9, 0xDE, 0x5D, 0x1C, + 0x3F, 0xAE, 0x79, 0xAC, 0x2A, 0x5B, 0xCD, 0x94, + 0x9F, 0xE3, 0xB8, 0x93, 0x21, 0x4E, 0x0E, 0xB4, + 0x78, 0xE6, 0xC2, 0x69, 0x7D, 0x58, 0x26, 0xDD, + 0xE1, 0xC0, 0xAB, 0xF9, 0x9F, 0xDE, 0xB0, 0xB5, + 0x24, 0x78, 0x4C, 0x5A, 0x42, 0x91, 0x8E, 0x03, + 0x95, 0xAA, 0x08, 0xD4, 0x56, 0x6D, 0x64, 0x41, + 0xC7, 0xC9, 0x2E, 0xEC, 0xE7, 0x1A, 0xEB, 0xE8, + 0x24, 0x8D, 0xE3, 0x31, 0xA5, 0x11, + + 0x05, 0x2F, 0xC0, 0x7D, 0xD7, 0xC5, 0x12, 0xF3, + 0x37, 0x94, 0x79, 0xEF, 0x44, 0xEE, 0xB0, 0xD0, + 0xED, 0x39, 0xCB, 0x5E, 0x64, 0xE9, 0x83, 0xD2, + 0x76, 0xF0, 0x65, 0x47, 0x25, 0x1D, 0x0D, 0x6B, + 0x16, 0x23, 0x98, 0xE5, 0x1D, 0xF2, 0xB1, 0x68, + 0x0D, 0xBA, 0xFE, 0x9D, 0x1A, 0x1D, 0xBA, 0x5F, + 0x92, 0x6F, 0x17, 0x76, 0xF5, 0xB3, 0xBA, 0x5A, + 0x71, 0x0C, 0x72, 0xFA, 0x32, 0xCC, 0x7B, 0x17, + 0x52, 0xB7, 0xF9, 0x86, 0x70, 0xF7, 0x39, 0x22, + 0xA1, 0xF6, 0xB3, 0xAA, 0xE9, 0xC5, 0x8B, 0x69, + 0xE4, 0x48, 0xD2, 0xAA, 0x8E, 0xAF, 0x7C, 0xE5, + 0x77, 0xE1, 0x33, 0xEC, 0x30, 0x20, 0xB8, 0xCD, + 0x3E, 0xD6, 0xB6, 0xD0, 0x64, 0x86, 0x39, 0x01, + 0x0D, 0x4F, 0xAB, 0xF7, 0x7B, 0xBE, 0x97, 0x21, + 0xCD, 0x96, 0x69, 0x16, 0x73, 0x61, 0x7D, 0x84, + 0x21, 0x3A, 0xA0, 0x07, 0xDE, 0xCA, 0x17, 0x0E, + 0x82, 0xA4, 0x8B, 0x2D, 0xE4, 0x99, 0x11, 0x3C, + 0x3C, 0x5B, 0x16, 0xB8, 0x00, 0xEE, 0xD1, 0xDF, + 0x68, 0x30, 0x0E, 0x12, 0x5E, 0x12, 0x4F, 0x1B, + 0xBE, 0xC2, 0x9B, 0x99, 0x64, 0xDB, 0xCE, 0x29, + 0xE5, 0x8E, 0x66, 0x96, 0x65, 0x37, 0x6E, 0xE2, + 0xE9, 0x05, 0x34, 0x56, 0x8D, 0xDF, 0x24, 0x34, + 0x09, 0x77, 0xB0, 0x76, 0xDD, 0x1E, 0x76, 0x5D, + 0x67, 0x7D, 0xCC, 0xCF, 0xB2, 0x7E, 0x15, 0x5E, + 0xE0, 0x4E, 0x80, 0x4B, 0xE0, 0xD2, 0x5A, 0xD4, + 0x1F, 0x5F, 0x6E, 0x65, 0x9C, 0x07, 0xBD, + + 0x08, 0x49, 0x9F, 0x83, 0x46, 0x0F, 0x0E, 0x04, + 0x94, 0x6C, 0x3F, 0x75, 0x4D, 0x98, 0xE5, 0x5D, + 0x12, 0xCB, 0xF6, 0x53, 0x0D, 0xFD, 0x30, 0x75, + 0x92, 0x02, 0xA9, 0x69, 0xD3, 0x71, 0xBC, 0x28, + 0xBC, 0x18, 0x88, 0x48, 0x27, 0x0B, 0x3B, 0x5C, + 0x2C, 0xFF, 0x7C, 0xFA, 0xB1, 0x02, 0x00, 0x79, + 0x6D, 0x8A, 0x39, 0x3F, 0xF2, 0x11, 0xA8, 0xA3, + 0x95, 0x68, 0xCA, 0x97, 0x1E, 0xD9, 0x80, 0xD8, + 0x9D, 0x4C, 0x62, 0x3E, 0xA5, 0xD0, 0x61, 0xE3, + 0x0D, 0x3D, 0xBA, 0xE7, 0x0E, 0x7F, 0x23, 0xBF, + 0x3D, 0x04, 0xB4, 0x1F, 0x20, 0x49, 0x5A, 0x40, + 0x50, 0x59, 0x2C, 0x09, 0x05, 0x5B, 0x8D, 0x07, + 0xA9, 0x79, 0x75, 0x13, 0xFF, 0xB5, 0x33, 0x58, + 0x41, 0x5D, 0x1D, 0xF1, 0x39, 0x54, 0x3A, 0x41, + 0x09, 0xDB, 0x5F, 0xC7, 0x5F, 0x7A, 0xE6, 0x27, + 0xA4, 0xFF, 0x20, 0xE8, 0x5F, 0x25, 0x89, 0xCB, + 0x77, 0x8D, 0xD7, 0x79, 0x16, 0x50, 0x68, 0xE0, + 0x24, 0xEC, 0x87, 0x60, 0xC2, 0x75, 0x87, 0xB8, + 0xAD, 0x66, 0x64, 0xF1, 0x6B, 0x01, 0x7E, 0xB0, + 0xE3, 0x79, 0x7E, 0x84, 0x29, 0x51, 0x47, 0x24, + 0xB9, 0x5D, 0xC6, 0x13, 0xEC, 0x0D, 0x5E, 0x8B, + 0x3E, 0x68, 0x97, 0x74, 0xB5, 0x1C, 0xE6, 0x9E, + 0x74, 0xD1, 0x9A, 0xE6, 0x1A, 0x6C, 0xFD, 0xBB, + 0x27, 0xC1, 0x73, 0xB1, 0x4E, 0xAC, 0x97, 0x1B, + 0x8B, 0xB2, 0x0A, 0x6A, 0x67, 0xA0, 0xA9, 0xDD, + 0x88, 0xF0, 0x55, 0x58, 0x10, 0x79, 0x9C, 0xC5, + + 0xAF, 0x48, 0x98, 0xFF, 0xF1, 0xC9, 0x67, 0xB3, + 0x74, 0xA9, 0xAC, 0xF5, 0x17, 0x7D, 0x95, 0x53, + 0x6A, 0xDD, 0xD6, 0x44, 0x3B, 0x09, 0x32, 0x78, + 0x2E, 0xEC, 0x19, 0x33, 0xD8, 0x4C, 0xFD, 0xE6, + 0xCE, 0x4A, 0x08, 0x78, 0xB5, 0xE8, 0x90, 0x1B, + 0x32, 0x04, 0x51, 0xC1, 0x4A, 0x1A, 0x69, 0xA2, + 0x0A, 0x76, 0x86, 0x73, 0x4B, 0x4C, 0xF7, 0x62, + 0xF7, 0xD0, 0x2E, 0x91, 0x8B, 0x7C, 0xFA, 0x8F, + 0xC9, 0xBE, 0x49, 0x9B, 0x1C, 0x58, 0xF8, 0x14, + 0xD1, 0x07, 0x4A, 0x4D, 0xE4, 0xB3, 0x83, 0x0A, + 0xD3, 0xF9, 0xDD, 0xC1, 0x2F, 0x84, 0x15, 0x02, + 0xA8, 0xB7, 0xB8, 0x45, 0x05, 0x99, 0x71, 0xEE, + 0xC6, 0x3B, 0xC4, 0xC3, 0x6F, 0x10, 0x63, 0x17, + 0x11, 0xD0, 0x1F, 0x1C, 0x63, 0xB9, 0x3F, 0x3D, + 0x68, 0x0A, 0x39, 0x25, 0x95, 0xB9, 0x81, 0xA7, + 0x04, 0x64, 0x6B, 0x2E, 0x52, 0xD0, 0x56, 0x04, + 0x99, 0x4F, 0x1B, 0x8E, 0xB0, 0x29, 0xA5, 0x3C, + 0x17, 0x46, 0xEC, 0x36, 0xC2, 0xD0, 0xAD, 0x92, + 0xB7, 0x58, 0x70, 0x33, 0xA9, 0xA3, 0xD8, 0xB7, + 0xF1, 0x04, 0xA7, 0xFA, 0x9D, 0x2C, 0x6B, 0x5A, + 0x52, 0x89, 0xAC, 0xD0, 0x12, 0x44, 0x8F, 0x7E, + 0x97, 0xA1, 0x82, 0x82, 0xB2, 0xEA, 0x47, 0x60, + 0x9B, 0x22, 0x32, 0x74, 0x77, 0x62, 0xE3, 0x46, + 0xF5, 0x55, 0x79, 0xEF, 0x9D, 0xB0, 0x2A, 0xC8, + 0x10, 0x2E, 0x7B, 0x3F, 0x00, 0xE8, 0x04, 0xA0, + 0x7E, 0x7F, 0x34, 0xB2, 0x35, 0x0E, 0x3A, 0x2F, + 0x65, + + 0x0C, 0x94, 0x90, 0x76, 0x67, 0xAB, 0xE0, 0x58, + 0x2E, 0x22, 0xF4, 0x48, 0xE7, 0x3E, 0x44, 0xB6, + 0x6D, 0xD4, 0xBD, 0x46, 0xEB, 0x9E, 0x01, 0xF9, + 0xB3, 0x06, 0x6F, 0xBA, 0xEE, 0x06, 0x1F, 0xB9, + 0x22, 0xE3, 0x49, 0x27, 0x80, 0x86, 0xB5, 0x38, + 0x47, 0x77, 0x14, 0x68, 0x0B, 0xF2, 0x6C, 0x9A, + 0xF1, 0x1F, 0x20, 0xED, 0x86, 0xF9, 0xEE, 0x04, + 0x92, 0xBC, 0xAB, 0xB8, 0x6B, 0x4A, 0x9D, 0x63, + 0xEB, 0x1B, 0x69, 0x18, 0x3D, 0x81, 0x4D, 0x16, + 0x68, 0x63, 0xF1, 0x96, 0x96, 0x0B, 0x51, 0x66, + 0x93, 0xFE, 0x49, 0xEA, 0xF6, 0x55, 0x8A, 0x2A, + 0xD7, 0x2E, 0xD0, 0x2D, 0xBF, 0x85, 0xD5, 0x97, + 0x9D, 0x8D, 0x2D, 0xD3, 0xB3, 0x62, 0x61, 0xB6, + 0x3B, 0x7B, 0xD1, 0x17, 0xB3, 0xB4, 0x94, 0x2D, + 0xEC, 0x76, 0xD1, 0x39, 0x75, 0x39, 0xFA, 0xAA, + 0x5B, 0x47, 0x0D, 0x9F, 0xBC, 0xEC, 0x12, 0x4F, + 0x76, 0x7C, 0xCB, 0xAD, 0x19, 0x80, 0xCF, 0x65, + 0x7F, 0x1E, 0x64, 0x2D, 0xE7, 0xF1, 0x4B, 0x22, + 0x9D, 0xEE, 0x46, 0x48, 0xBA, 0x54, 0x32, 0x5D, + 0x33, 0xE0, 0x81, 0xF6, 0x9C, 0x2F, 0x1C, 0xBC, + 0x17, 0xCA, 0x34, 0x0C, 0x9A, 0xEF, 0x27, 0x8E, + 0x19, 0xD8, 0x8C, 0xC4, 0xED, 0x0C, 0x88, 0xBA, + 0x60, 0x8B, 0xCF, 0x84, 0xB8, 0xF0, 0x25, 0x8C, + 0x9E, 0xEA, 0x00, 0xE0, 0xB6, 0xC1, 0xAA, 0x02, + 0x51, 0xD9, 0xFF, 0xDA, 0x87, 0x9D, 0x62, 0xB1, + 0xD1, 0x52, 0x7D, 0xE8, 0xB1, 0xC2, 0x45, 0x7D, + 0x4D, 0xF8, + + 0x20, 0xC9, 0xAD, 0x20, 0xED, 0x26, 0x96, 0x8F, + 0xF9, 0xEA, 0xD1, 0x3C, 0x54, 0xA2, 0x45, 0xAF, + 0x13, 0x85, 0x90, 0x43, 0xC3, 0xFD, 0x90, 0xBA, + 0x19, 0xAF, 0xB3, 0x6F, 0x3E, 0xD7, 0x01, 0xB3, + 0xCD, 0x1F, 0xAC, 0x35, 0x48, 0x29, 0x7E, 0xE3, + 0x7B, 0x5F, 0x2A, 0x48, 0x86, 0x27, 0x43, 0xAA, + 0x9C, 0x30, 0x6A, 0x4B, 0x13, 0x77, 0x2D, 0xE5, + 0xB4, 0xB2, 0x66, 0x82, 0xC0, 0x4E, 0xDE, 0x81, + 0x18, 0x9E, 0x4A, 0xC4, 0x0A, 0x20, 0x2E, 0x70, + 0xED, 0x7D, 0x48, 0x28, 0xF8, 0xE4, 0x68, 0x01, + 0xEF, 0x54, 0x43, 0x42, 0x3A, 0xA8, 0x65, 0xC6, + 0xF5, 0x94, 0x19, 0x8A, 0x23, 0x75, 0xBD, 0x08, + 0xAE, 0xA4, 0x78, 0x42, 0xD1, 0xF4, 0xF0, 0xA3, + 0x5A, 0x21, 0x74, 0x1B, 0x90, 0xD8, 0x05, 0x99, + 0xA7, 0x22, 0x7C, 0x79, 0xE9, 0x7D, 0xFF, 0xB2, + 0x1F, 0x04, 0x37, 0xAE, 0xFA, 0xD5, 0x93, 0x97, + 0x32, 0x7E, 0xFD, 0xBB, 0xAC, 0x6E, 0x96, 0xA5, + 0x4F, 0xB4, 0x89, 0x21, 0xB1, 0x3F, 0xB2, 0x3D, + 0xFA, 0x08, 0xCE, 0xED, 0x70, 0x23, 0xAF, 0x75, + 0x22, 0xDE, 0x2E, 0x12, 0x37, 0xE4, 0xB9, 0x92, + 0xA2, 0xB0, 0x4D, 0x56, 0xDE, 0x5E, 0xD3, 0x7F, + 0xBF, 0xBD, 0x5E, 0x6F, 0x4A, 0xBA, 0xC3, 0x27, + 0x22, 0x19, 0xA3, 0x4E, 0x90, 0x2E, 0x72, 0x2E, + 0xEB, 0x6B, 0x48, 0xD5, 0xC6, 0x00, 0x94, 0x56, + 0x29, 0x74, 0x13, 0x0B, 0x85, 0xCF, 0xE5, 0x51, + 0x83, 0xB5, 0x20, 0x49, 0xF0, 0xC6, 0x08, 0xEE, + 0x18, 0x26, 0x19, + + 0x74, 0x41, 0x6D, 0x4E, 0x6C, 0x5E, 0x82, 0xB9, + 0xA6, 0x6E, 0x53, 0x6C, 0xEF, 0x7E, 0x43, 0x36, + 0x6A, 0xAA, 0x94, 0x99, 0xAA, 0x92, 0x44, 0xE6, + 0x41, 0x1D, 0x09, 0xE6, 0x5D, 0xF5, 0x8D, 0xCF, + 0x34, 0xE3, 0xAC, 0x10, 0x0B, 0x5D, 0xC1, 0x59, + 0xB7, 0x60, 0x6C, 0xCB, 0x75, 0x67, 0x85, 0x40, + 0xB8, 0x06, 0x54, 0xE0, 0xB7, 0xB9, 0x4E, 0x4A, + 0x52, 0xD0, 0x39, 0x9F, 0x2C, 0x01, 0xFE, 0xB2, + 0x09, 0x73, 0xA1, 0x6C, 0xD6, 0x0E, 0xB4, 0x8F, + 0x40, 0xA2, 0x15, 0x12, 0x88, 0x3D, 0x65, 0x49, + 0x0C, 0xE1, 0xF5, 0x95, 0xF4, 0xC0, 0xF2, 0x63, + 0x5F, 0xF6, 0x1B, 0xF2, 0x30, 0xC8, 0x5B, 0xE9, + 0xC0, 0x84, 0x0C, 0x6E, 0xDF, 0xC4, 0x19, 0xB9, + 0xCE, 0xA6, 0x49, 0xA5, 0x64, 0xE9, 0x66, 0xC3, + 0x6C, 0xE5, 0x93, 0x76, 0xAC, 0x59, 0x65, 0x0E, + 0xE6, 0xF5, 0x80, 0xCE, 0x1C, 0x72, 0x9A, 0x53, + 0xB0, 0xC7, 0xB6, 0x11, 0x65, 0xDE, 0xEC, 0x89, + 0x18, 0xD8, 0x05, 0x6E, 0x41, 0xD2, 0x31, 0xBD, + 0xDB, 0xE5, 0xEF, 0xF5, 0x76, 0x5C, 0xBD, 0x03, + 0x6D, 0x04, 0x00, 0x16, 0x64, 0x19, 0xFE, 0x4B, + 0x73, 0x9C, 0xDB, 0x31, 0x13, 0x50, 0xD6, 0xCB, + 0xE4, 0xFC, 0x73, 0x89, 0x0F, 0x50, 0x31, 0xBF, + 0x0E, 0xAB, 0xD8, 0xF2, 0x9C, 0xB8, 0x04, 0x88, + 0xAD, 0xE3, 0x9F, 0xDA, 0x0D, 0xE3, 0x5B, 0x34, + 0x94, 0xCB, 0x50, 0x61, 0xA4, 0x01, 0x03, 0xFA, + 0x4D, 0x85, 0xE4, 0x29, 0xFC, 0x11, 0x77, 0x97, + 0x75, 0x6D, 0xF3, 0xC1, + + 0x16, 0x34, 0x6D, 0x2E, 0x36, 0x03, 0xC4, 0x4E, + 0xC5, 0xB4, 0xF8, 0xFA, 0x20, 0x49, 0x81, 0x68, + 0x7E, 0xC8, 0xC2, 0x13, 0x18, 0x84, 0x0C, 0x84, + 0xE8, 0xBF, 0xD9, 0x0D, 0x1B, 0x9F, 0xF0, 0x1F, + 0x8D, 0x41, 0x56, 0x5F, 0xBB, 0x1A, 0x21, 0x5F, + 0x0B, 0x7E, 0x79, 0xFC, 0x7C, 0x72, 0xD5, 0x80, + 0x64, 0x8A, 0xFD, 0xC5, 0xF9, 0xDC, 0x61, 0x27, + 0x75, 0x65, 0x0A, 0x2C, 0x2A, 0x4C, 0xE9, 0x7A, + 0x3A, 0xFA, 0x1E, 0xE6, 0x2A, 0x31, 0xFD, 0xD1, + 0xA0, 0xD8, 0xBF, 0xAB, 0x40, 0x61, 0x87, 0x9F, + 0xA8, 0x65, 0x18, 0x7C, 0x87, 0xEE, 0xA2, 0x93, + 0xC2, 0xE4, 0xED, 0xEF, 0x5C, 0x3D, 0x09, 0xC8, + 0xD7, 0x75, 0x76, 0xF2, 0x1A, 0x9E, 0x19, 0xB8, + 0x44, 0xE8, 0xBE, 0x4F, 0x71, 0xD2, 0xEF, 0x0A, + 0x83, 0x1C, 0xFC, 0xA5, 0x6F, 0x43, 0x0A, 0x1D, + 0xDA, 0xD7, 0x4D, 0x14, 0xE3, 0x5C, 0x32, 0x74, + 0x45, 0x19, 0xC4, 0xF4, 0x3D, 0x5A, 0x6E, 0xEC, + 0x5E, 0x54, 0xF1, 0xB3, 0xF1, 0x4B, 0x05, 0x52, + 0x92, 0x41, 0x49, 0x27, 0x18, 0x40, 0x38, 0x4E, + 0x0A, 0x7D, 0xD1, 0x31, 0x0A, 0xC8, 0x82, 0x60, + 0x5A, 0xBE, 0x4A, 0x5A, 0xAE, 0xBE, 0x86, 0x7D, + 0x2B, 0x27, 0x07, 0x2C, 0xFA, 0x65, 0xB0, 0x73, + 0x50, 0x68, 0x58, 0x36, 0x50, 0x74, 0x61, 0x57, + 0xAB, 0xF4, 0xE8, 0xCD, 0xEE, 0xBB, 0x29, 0x68, + 0x0E, 0x6C, 0x38, 0x0E, 0x0C, 0xDC, 0x79, 0x12, + 0xC7, 0xF4, 0x25, 0x74, 0x66, 0xD6, 0xC1, 0xF6, + 0xF0, 0xF6, 0x1D, 0x27, 0xA1, + + 0xFA, 0x0C, 0xB5, 0xEE, 0x97, 0x04, 0x06, 0xF8, + 0x04, 0xE8, 0xBF, 0x1C, 0x3D, 0x19, 0x5A, 0x1C, + 0x04, 0xBC, 0x6F, 0x76, 0x50, 0x3D, 0x0C, 0xE7, + 0xCE, 0xE5, 0xDE, 0x6F, 0xA1, 0xE6, 0xA3, 0xE4, + 0x3C, 0xC6, 0x17, 0xBF, 0xDC, 0xDF, 0x56, 0xFC, + 0x63, 0xE9, 0x9F, 0x57, 0x0C, 0xF0, 0xA8, 0x19, + 0x1B, 0x8A, 0x33, 0xA3, 0xBF, 0xA8, 0xCD, 0xC4, + 0x24, 0x71, 0xA2, 0x4D, 0x2D, 0xF4, 0x3E, 0xB7, + 0xF9, 0xAD, 0x3B, 0xA9, 0xDF, 0xCF, 0x81, 0xBD, + 0x9A, 0x5A, 0x45, 0xC0, 0x94, 0xA2, 0xAF, 0x9A, + 0x86, 0xF6, 0xBF, 0x3B, 0x99, 0xDB, 0x1E, 0x41, + 0xFD, 0xA5, 0x57, 0xB3, 0x2E, 0x7B, 0x73, 0x4F, + 0x6A, 0xBF, 0xAD, 0x5F, 0x87, 0xFC, 0xA7, 0xEC, + 0xC9, 0xD6, 0x70, 0xDA, 0xED, 0x75, 0x84, 0x50, + 0x61, 0x7C, 0x73, 0x18, 0x8D, 0x7B, 0xF2, 0x47, + 0x85, 0x75, 0x0A, 0x19, 0xBF, 0xFE, 0x3B, 0x81, + 0x81, 0x15, 0x57, 0xD8, 0xE7, 0x13, 0xD6, 0xF6, + 0x0D, 0x49, 0xB9, 0x71, 0x23, 0x51, 0x3F, 0x6A, + 0x50, 0x70, 0xB6, 0x42, 0x18, 0xE9, 0xD9, 0xF5, + 0xEF, 0x60, 0x15, 0xE3, 0x73, 0xCD, 0x49, 0x09, + 0x4A, 0x87, 0x05, 0x5D, 0x45, 0xE1, 0x40, 0xAC, + 0x6E, 0xA0, 0xD0, 0x1C, 0xD4, 0xFE, 0x76, 0xE4, + 0xD8, 0x4E, 0x3E, 0x93, 0xC9, 0xC8, 0xC7, 0x4A, + 0x49, 0x8E, 0x79, 0x46, 0x5D, 0xB0, 0xEB, 0xDC, + 0x46, 0x88, 0x25, 0x5B, 0xE4, 0xE2, 0x0E, 0x0C, + 0xC9, 0xD3, 0xA2, 0x79, 0x1A, 0xF2, 0x94, 0xF2, + 0xC2, 0xE2, 0x98, 0x6E, 0x99, 0x87, + + 0xDE, 0xEE, 0x45, 0x68, 0x9D, 0xC8, 0x03, 0xC0, + 0x02, 0x41, 0x3D, 0x9F, 0x33, 0x70, 0x73, 0x62, + 0x96, 0x25, 0x02, 0xF4, 0x74, 0x5D, 0xE2, 0xF1, + 0x67, 0xF2, 0xFD, 0xE1, 0x65, 0x47, 0x33, 0x6D, + 0xF4, 0x86, 0x11, 0x7C, 0xD0, 0x48, 0x02, 0x7C, + 0xA7, 0x26, 0x85, 0x27, 0xBE, 0x13, 0x96, 0x85, + 0x40, 0x1F, 0x6A, 0xB0, 0x0D, 0x8E, 0xD2, 0x02, + 0xD7, 0x03, 0xFD, 0x45, 0xC8, 0x08, 0xED, 0x3E, + 0x32, 0x04, 0x30, 0x48, 0x63, 0xA3, 0x65, 0x8C, + 0x11, 0x9D, 0x7B, 0x65, 0xE9, 0x02, 0x37, 0xF3, + 0x9D, 0x0B, 0x5E, 0xAD, 0x9D, 0xFF, 0x7D, 0x56, + 0x3D, 0x50, 0x74, 0x37, 0xAD, 0x39, 0x06, 0x85, + 0x10, 0xC5, 0x69, 0x37, 0x57, 0x3C, 0x4F, 0xE8, + 0x4A, 0xAC, 0x1D, 0x06, 0xDA, 0x62, 0xB3, 0x3E, + 0x0B, 0xB0, 0xE4, 0x1F, 0x3F, 0x2F, 0x29, 0x88, + 0x14, 0xC1, 0xAE, 0xD1, 0x62, 0x89, 0xB7, 0xC5, + 0x51, 0x72, 0xFE, 0xE3, 0xDF, 0x56, 0x9E, 0xDB, + 0x37, 0xE4, 0x64, 0xAC, 0x99, 0x08, 0xF7, 0x87, + 0xFA, 0xCE, 0x5B, 0xF3, 0xCD, 0x21, 0xF6, 0x8A, + 0xED, 0xA3, 0x7B, 0xEC, 0x3F, 0x30, 0x14, 0x3D, + 0x00, 0xD4, 0x40, 0xBE, 0x35, 0x36, 0x89, 0xCF, + 0x29, 0xDA, 0x13, 0xE8, 0x69, 0x0D, 0xDE, 0xE8, + 0x05, 0x22, 0x0D, 0x02, 0x18, 0xDB, 0x12, 0xAC, + 0x01, 0x12, 0xD0, 0xEF, 0xD2, 0x82, 0x21, 0xC9, + 0xF7, 0x52, 0x94, 0x98, 0x0E, 0xE7, 0xCB, 0x9C, + 0x3F, 0xD1, 0x0D, 0xA6, 0x0C, 0x53, 0x60, 0x8B, + 0x13, 0x3F, 0x77, 0xD5, 0xDC, 0xC5, 0xE2, + + 0x7D, 0x3B, 0xAE, 0x67, 0x64, 0x19, 0xC8, 0x66, + 0xF1, 0x0F, 0x31, 0xAD, 0x43, 0x35, 0xD4, 0x44, + 0x2C, 0xA5, 0x5D, 0x76, 0x78, 0x70, 0x4C, 0x2F, + 0x71, 0x11, 0xB9, 0xF3, 0x69, 0x16, 0xFA, 0x09, + 0x42, 0xE7, 0x6C, 0x09, 0x2E, 0xDA, 0xEB, 0x9A, + 0x53, 0x82, 0x1D, 0x31, 0x3E, 0x3E, 0xD8, 0x48, + 0xEA, 0x86, 0xE8, 0x76, 0x8E, 0xC6, 0xA7, 0xEA, + 0xBB, 0x58, 0x7D, 0xB7, 0x39, 0x76, 0xEC, 0xE6, + 0x35, 0x19, 0xAA, 0xFA, 0x6E, 0x98, 0x2C, 0x38, + 0xC2, 0xA2, 0x0E, 0x36, 0xA9, 0x4F, 0x88, 0xDA, + 0x6E, 0x3D, 0x1C, 0xFB, 0x25, 0xF5, 0x96, 0x1E, + 0x18, 0xBE, 0xEB, 0x0A, 0x48, 0xB8, 0x5A, 0x42, + 0xD3, 0x77, 0xEB, 0xD1, 0x06, 0x93, 0x4D, 0x56, + 0xB0, 0x60, 0xAA, 0x72, 0x8B, 0xC4, 0x29, 0x2E, + 0xE5, 0x36, 0x53, 0x46, 0xA3, 0x4F, 0x27, 0xED, + 0xED, 0x0A, 0x19, 0xCC, 0xF1, 0x1C, 0xBB, 0x0C, + 0x43, 0x2C, 0x6E, 0x40, 0xCF, 0x0F, 0x58, 0x23, + 0xC0, 0xE1, 0x5F, 0x2A, 0x63, 0x06, 0xD0, 0x91, + 0x97, 0x9C, 0x16, 0x0E, 0x0A, 0xE6, 0xAF, 0x1D, + 0x7C, 0x07, 0xD6, 0xC4, 0x64, 0x01, 0xB9, 0xE4, + 0x6D, 0x75, 0x92, 0x4F, 0x35, 0x1B, 0x4C, 0x37, + 0xCE, 0x05, 0x88, 0x06, 0x08, 0x1C, 0xD6, 0x75, + 0xE2, 0x07, 0x2E, 0x54, 0x58, 0xA1, 0x00, 0x39, + 0xA4, 0x80, 0xBF, 0xB2, 0x1F, 0xC4, 0x69, 0xC1, + 0x3D, 0x4B, 0xFE, 0x7F, 0x20, 0x1F, 0x49, 0x8B, + 0x08, 0xC6, 0x33, 0x86, 0xEB, 0x7B, 0x3E, 0x16, + 0xDE, 0xE3, 0x34, 0xD8, 0x66, 0x04, 0x55, 0xBA, + + 0xBE, 0x42, 0xC4, 0xEA, 0xAB, 0x31, 0xF5, 0x2E, + 0xF3, 0xF6, 0xBC, 0x86, 0xB6, 0xA4, 0x48, 0x74, + 0x15, 0xE9, 0xA7, 0x65, 0xE6, 0xDA, 0xF0, 0x94, + 0x5F, 0x1F, 0xC4, 0xCA, 0xD3, 0xFE, 0x66, 0x78, + 0x03, 0xE3, 0xBE, 0x9D, 0xE8, 0xAC, 0x45, 0x29, + 0xFA, 0x59, 0xE0, 0xBA, 0xD0, 0xAC, 0x62, 0x9A, + 0xA6, 0x6F, 0x6C, 0x5B, 0x0E, 0x86, 0xC8, 0x3E, + 0xD6, 0xE2, 0x84, 0x31, 0x79, 0xD1, 0xAF, 0xCB, + 0x78, 0x1D, 0xF5, 0x1D, 0x76, 0x72, 0xCC, 0xAA, + 0x7A, 0x22, 0x35, 0xBD, 0x3A, 0x26, 0x7B, 0x2B, + 0x08, 0xD8, 0x27, 0x26, 0x07, 0xA2, 0xAA, 0x6E, + 0x30, 0x8C, 0x38, 0x70, 0x40, 0x3C, 0x61, 0x9F, + 0x5C, 0x57, 0x72, 0x9B, 0x5B, 0x4C, 0x20, 0xB0, + 0x84, 0x1A, 0x85, 0x0C, 0xF3, 0x3E, 0x5B, 0x67, + 0x3D, 0xF2, 0xB4, 0x4F, 0xB1, 0xE6, 0x3D, 0xDC, + 0xF1, 0x3C, 0x49, 0x9A, 0x38, 0x95, 0x47, 0xF2, + 0xCA, 0x43, 0xB7, 0x0A, 0x80, 0x06, 0x9F, 0x06, + 0x4D, 0x02, 0x98, 0x44, 0xB0, 0x8B, 0x73, 0xC6, + 0x26, 0x5B, 0x14, 0xA1, 0xF6, 0x64, 0x37, 0xDE, + 0xF2, 0x22, 0x1A, 0x59, 0x85, 0xAE, 0xFD, 0x5A, + 0xE5, 0x71, 0xC8, 0xCF, 0x6C, 0xFA, 0x64, 0x27, + 0xFF, 0x55, 0x45, 0x88, 0xA4, 0x5A, 0x50, 0x82, + 0x47, 0x61, 0x4C, 0xE5, 0x43, 0x9A, 0xA9, 0x5A, + 0xCC, 0xA3, 0xCA, 0x1B, 0xA0, 0x28, 0x65, 0x6E, + 0xA4, 0xCE, 0xE2, 0x17, 0x41, 0x07, 0x50, 0x31, + 0x76, 0xCB, 0x61, 0x14, 0xE3, 0x50, 0x68, 0x2B, + 0xF2, 0x96, 0x6B, 0xCF, 0xFE, 0xF7, 0x91, 0xD6, + 0x6F, + + 0x55, 0x68, 0x17, 0x44, 0xA4, 0xAE, 0x0A, 0xBE, + 0xB6, 0xF4, 0x72, 0xAA, 0x55, 0x65, 0x33, 0x22, + 0xC5, 0x86, 0x02, 0xC5, 0xB6, 0x13, 0x9B, 0x68, + 0x1E, 0xE8, 0xFD, 0x2D, 0xEB, 0x9E, 0xF2, 0x72, + 0xA3, 0xA7, 0x38, 0xC1, 0xDB, 0xDB, 0x72, 0x03, + 0xE9, 0x7E, 0x5B, 0x55, 0x12, 0x0A, 0x8B, 0x87, + 0x89, 0xA5, 0xB7, 0x93, 0xFA, 0x8E, 0x80, 0xAA, + 0xB5, 0xDC, 0xA4, 0x78, 0xD7, 0x76, 0x21, 0x7B, + 0xF0, 0x1B, 0x41, 0x89, 0x9F, 0x9C, 0x41, 0xE6, + 0xDE, 0x3A, 0xF6, 0x16, 0x66, 0xDF, 0xF1, 0xC9, + 0x59, 0x75, 0x18, 0xBE, 0xC5, 0xE3, 0x55, 0xA7, + 0x39, 0xF5, 0x04, 0xBA, 0x3A, 0x11, 0x53, 0x36, + 0x69, 0x58, 0x49, 0xC3, 0xB1, 0x4E, 0x65, 0x88, + 0xDA, 0x46, 0xB8, 0x79, 0x58, 0x32, 0x18, 0xBA, + 0x3E, 0x3C, 0x5C, 0x02, 0x34, 0xFD, 0xD1, 0x2F, + 0x32, 0xCA, 0x4D, 0x1A, 0x68, 0x3B, 0x73, 0xC6, + 0xD0, 0xA8, 0x71, 0x24, 0xA8, 0xD5, 0x82, 0x83, + 0x26, 0x99, 0xCA, 0x8E, 0xC9, 0x55, 0xF2, 0x92, + 0xD1, 0x95, 0x4E, 0xA7, 0x5F, 0x16, 0x56, 0xD7, + 0xEA, 0xCF, 0xD3, 0xC7, 0xD7, 0xEA, 0x79, 0xF4, + 0x12, 0xC4, 0x7E, 0x5B, 0xB3, 0x1D, 0xAE, 0x0A, + 0xFD, 0xE1, 0x9F, 0x27, 0xCC, 0x95, 0xC6, 0x03, + 0xFD, 0x2F, 0xD3, 0xC5, 0x4E, 0xEF, 0x42, 0x10, + 0xF4, 0x6B, 0x24, 0x78, 0x53, 0xC5, 0xE1, 0xB6, + 0xB7, 0x80, 0xBD, 0xEB, 0x65, 0x5B, 0xA8, 0xE5, + 0x8E, 0xE5, 0x7F, 0x1E, 0x5B, 0x93, 0xA4, 0xDD, + 0xD5, 0xAB, 0xF2, 0x4A, 0x75, 0xE0, 0xAC, 0xC1, + 0x0C, 0x2D, + + 0x3A, 0x63, 0xF3, 0x06, 0x48, 0xB8, 0x7A, 0x18, + 0x11, 0x6F, 0x96, 0x89, 0x2C, 0x01, 0x1F, 0x47, + 0x73, 0xF4, 0x9E, 0x28, 0x77, 0xE8, 0xCD, 0x57, + 0x36, 0x53, 0xB4, 0x33, 0x62, 0x83, 0x51, 0x7A, + 0x4A, 0x4D, 0xC3, 0xF7, 0x58, 0x19, 0x73, 0xB5, + 0xFF, 0x36, 0x3E, 0x63, 0xB0, 0xD1, 0x12, 0x07, + 0xB9, 0x17, 0xF1, 0xC6, 0x78, 0x79, 0xA1, 0x2D, + 0x4F, 0x00, 0xAF, 0xCD, 0xA3, 0x73, 0x09, 0xDD, + 0x9C, 0x3D, 0x8E, 0x6B, 0x2B, 0xA5, 0xF5, 0x91, + 0x1C, 0x2B, 0xFE, 0xB8, 0xE5, 0x6E, 0xA5, 0x9D, + 0x5D, 0x5C, 0x6A, 0xF2, 0xA1, 0x7E, 0x82, 0xB0, + 0x47, 0xB5, 0x53, 0xE4, 0x49, 0x00, 0x5B, 0x60, + 0x5C, 0x94, 0x6E, 0xD1, 0xDE, 0x27, 0x7C, 0xC2, + 0xA2, 0xFF, 0xB4, 0xAF, 0x5F, 0x52, 0x0F, 0x5B, + 0x3B, 0xA5, 0x48, 0x93, 0x90, 0x97, 0x30, 0x10, + 0x30, 0x96, 0x84, 0x68, 0x8D, 0xB4, 0x47, 0x9E, + 0x77, 0x23, 0x2A, 0x96, 0xA9, 0x29, 0x2E, 0xBC, + 0x40, 0x59, 0x92, 0xE4, 0x55, 0xD7, 0x30, 0x2E, + 0x62, 0x15, 0xA6, 0xEA, 0x77, 0x5D, 0x2C, 0x1C, + 0x10, 0xF5, 0x8A, 0x43, 0x2B, 0x72, 0xC3, 0xD0, + 0x9E, 0xB9, 0x80, 0x4C, 0x8D, 0xCD, 0x5B, 0xB5, + 0xAD, 0xF6, 0x44, 0xDD, 0x9D, 0x37, 0x8E, 0x28, + 0xF1, 0xB5, 0xCE, 0xF2, 0xDD, 0x35, 0x6F, 0x63, + 0xB3, 0x32, 0xAC, 0x13, 0x4B, 0xC9, 0x67, 0x58, + 0x63, 0x2F, 0xB4, 0x23, 0x0C, 0xA3, 0x4F, 0xF8, + 0xB6, 0x52, 0xE7, 0xD1, 0xA7, 0x0D, 0x0D, 0x5B, + 0x66, 0x1D, 0x77, 0x82, 0xCA, 0xE5, 0x11, 0x62, + 0xF4, 0xA9, 0x95, + + 0xE7, 0x1E, 0xF9, 0x44, 0xCD, 0x9F, 0xB5, 0xC5, + 0x80, 0xDC, 0x78, 0xA9, 0x4E, 0xAB, 0xD3, 0x1D, + 0xA9, 0xC6, 0xCC, 0x67, 0x49, 0xC2, 0x9C, 0x99, + 0x60, 0x86, 0xBA, 0x95, 0xDD, 0x10, 0x02, 0x37, + 0xE8, 0x2B, 0xF9, 0x2B, 0xB3, 0x97, 0x44, 0xDA, + 0xEF, 0x65, 0xB6, 0xB2, 0x0D, 0xA4, 0xAB, 0xFE, + 0xD7, 0x19, 0x07, 0xBD, 0x5B, 0xCC, 0x56, 0x78, + 0x90, 0xBA, 0x9C, 0x85, 0x7B, 0xD7, 0xDE, 0xD2, + 0x07, 0x29, 0xB2, 0x82, 0xF1, 0x37, 0x7A, 0x87, + 0xB9, 0x04, 0xF6, 0xE2, 0xC9, 0xF7, 0xDD, 0x1D, + 0xA4, 0x25, 0xB0, 0x18, 0x49, 0x72, 0x6F, 0x02, + 0xB1, 0x49, 0xB9, 0x15, 0xA6, 0x00, 0x87, 0x23, + 0x88, 0xED, 0xB7, 0xD5, 0xA2, 0x12, 0xC2, 0xA6, + 0xF3, 0x8A, 0x8C, 0x50, 0xE2, 0xC9, 0xBC, 0x62, + 0x50, 0xF8, 0x2C, 0xB1, 0x11, 0x00, 0x36, 0x79, + 0x13, 0x75, 0xFB, 0x87, 0xDA, 0x49, 0x59, 0x42, + 0x57, 0xDD, 0x7C, 0xE4, 0xFA, 0xD1, 0x5B, 0xF9, + 0xB5, 0xB8, 0x56, 0xB0, 0x98, 0xF9, 0x63, 0xB1, + 0x1B, 0xEC, 0xF7, 0x05, 0x4C, 0xBC, 0x32, 0x1C, + 0x59, 0xC8, 0x75, 0x7E, 0xBC, 0xAD, 0xCF, 0x32, + 0x1A, 0x3D, 0xAE, 0x1F, 0x27, 0x01, 0x85, 0x60, + 0xCB, 0xDE, 0xAB, 0x39, 0xFD, 0x1E, 0x45, 0x82, + 0x36, 0xC1, 0xC3, 0x69, 0xA7, 0xE4, 0x5B, 0x16, + 0x36, 0x5A, 0xD5, 0x20, 0x5E, 0x4D, 0x1C, 0xBA, + 0x6E, 0x28, 0x30, 0x66, 0xDA, 0x6C, 0x66, 0x42, + 0xD4, 0x9A, 0x98, 0x31, 0xF8, 0x11, 0x8A, 0xB5, + 0xF9, 0x9E, 0x96, 0xE9, 0x42, 0x0D, 0xDF, 0x28, + 0x2D, 0x03, 0xC6, 0xFF, + + 0x05, 0x1F, 0xCE, 0x41, 0xE4, 0x8C, 0x74, 0x81, + 0x36, 0x78, 0x17, 0x9E, 0x28, 0xCC, 0xA0, 0xA8, + 0x03, 0x13, 0xEB, 0x8D, 0xD9, 0xF5, 0x75, 0x8C, + 0x58, 0x0B, 0xA5, 0x3F, 0xB5, 0x5D, 0x48, 0x94, + 0xA9, 0x7D, 0x61, 0x34, 0x5F, 0x07, 0xA2, 0x84, + 0x6A, 0xF9, 0x6C, 0xCB, 0x4C, 0x5D, 0x2D, 0xB2, + 0x05, 0x0B, 0x03, 0x61, 0x4A, 0x68, 0xB3, 0xA2, + 0x08, 0x33, 0x68, 0xB0, 0xD9, 0x95, 0x63, 0x98, + 0xBA, 0xF9, 0x70, 0x81, 0xDC, 0x34, 0xD2, 0xA3, + 0x5C, 0x5E, 0x85, 0x22, 0x8D, 0xEB, 0xEE, 0x81, + 0xEC, 0xEE, 0x43, 0xAC, 0x70, 0x93, 0x2C, 0xBC, + 0x66, 0x33, 0x37, 0xD9, 0xE1, 0x71, 0x2F, 0xD0, + 0x5A, 0xB8, 0x32, 0x8C, 0xE7, 0x98, 0x13, 0x8C, + 0x61, 0x94, 0x6C, 0xAA, 0x38, 0xCF, 0xAD, 0xF6, + 0xBE, 0x40, 0xA9, 0x6F, 0x02, 0xFA, 0x16, 0x5C, + 0x70, 0x2E, 0x55, 0xCC, 0x5F, 0x5A, 0xF7, 0x07, + 0x95, 0xB0, 0x18, 0xB7, 0xF0, 0x4A, 0xFA, 0xCD, + 0xB4, 0xA8, 0x92, 0x3F, 0x71, 0xED, 0x8F, 0x54, + 0xC0, 0x35, 0x1F, 0x2E, 0xC7, 0xF2, 0xBE, 0x4B, + 0x96, 0x4F, 0x05, 0xB7, 0x3E, 0x9E, 0x40, 0xA9, + 0x2A, 0xC7, 0x1C, 0x2D, 0x91, 0x5A, 0xBB, 0x31, + 0xB1, 0x4F, 0xE1, 0x3F, 0x34, 0x27, 0x4D, 0xAD, + 0x19, 0x34, 0xC5, 0x4F, 0x2C, 0x32, 0x24, 0x3E, + 0x9E, 0x50, 0x1F, 0xC2, 0x57, 0x63, 0xFF, 0x45, + 0xBE, 0x73, 0x72, 0xED, 0xE6, 0x01, 0x35, 0x33, + 0xCD, 0x14, 0xC2, 0xF4, 0xFB, 0xEF, 0x47, 0x55, + 0x3F, 0x4B, 0xD0, 0x05, 0xF0, 0x38, 0x2A, 0x37, + 0xAF, 0x02, 0xD9, 0x25, 0xDD, + + 0x20, 0xCE, 0x9F, 0xBC, 0x8E, 0x72, 0x30, 0xBB, + 0xE1, 0xC8, 0x3C, 0x08, 0x89, 0x34, 0x73, 0x4C, + 0x8D, 0x0C, 0x00, 0x1E, 0xA4, 0xD4, 0xDA, 0x55, + 0x18, 0x8C, 0xF0, 0xA4, 0x2F, 0xE9, 0x06, 0x30, + 0xDE, 0x73, 0xDA, 0x14, 0xA8, 0x82, 0xC3, 0x94, + 0xAE, 0x12, 0xC5, 0x55, 0x5B, 0x15, 0x42, 0x75, + 0x7A, 0xC3, 0x79, 0x05, 0x09, 0x63, 0x9A, 0xC4, + 0xD4, 0xCA, 0x1D, 0x08, 0x75, 0x68, 0x78, 0x07, + 0x7D, 0xD2, 0x37, 0xEB, 0x8B, 0x25, 0xAC, 0xF4, + 0x90, 0x5F, 0x4A, 0x2D, 0xE4, 0x4F, 0x08, 0x3B, + 0x3D, 0xF3, 0x9F, 0xA4, 0x8E, 0xEE, 0xE6, 0x47, + 0x4F, 0x3A, 0x6D, 0xBC, 0xB7, 0xA9, 0x4C, 0x1D, + 0x82, 0x4E, 0x7A, 0x29, 0xC9, 0x01, 0xFF, 0xC6, + 0x6F, 0x63, 0x4C, 0xE3, 0x0A, 0x10, 0x3F, 0x07, + 0xB4, 0x3F, 0x71, 0x55, 0x8D, 0xE3, 0xEF, 0xFC, + 0x76, 0xF6, 0x0A, 0xC3, 0xE9, 0xA9, 0x28, 0x03, + 0xB0, 0xDF, 0x15, 0x0E, 0xD0, 0x68, 0x17, 0x7D, + 0xC7, 0xC2, 0x51, 0x1A, 0x0D, 0x24, 0x21, 0x21, + 0x45, 0xFF, 0xEA, 0x17, 0xFB, 0xBF, 0x59, 0x63, + 0x03, 0x4F, 0x62, 0xEF, 0x1C, 0xDA, 0xF5, 0x00, + 0x8F, 0x9D, 0x2B, 0x81, 0x62, 0x9F, 0xED, 0xA6, + 0x6D, 0xA0, 0x57, 0x16, 0x08, 0x9E, 0x07, 0xB9, + 0xF3, 0x8B, 0x78, 0xA8, 0x6E, 0x15, 0x53, 0x26, + 0xE5, 0x7A, 0xE4, 0xBA, 0x09, 0xC6, 0x25, 0x41, + 0x1A, 0x24, 0x9B, 0xE0, 0x16, 0x62, 0xCF, 0x67, + 0xB6, 0xA1, 0xEF, 0x8B, 0x4D, 0xCC, 0xC3, 0xB6, + 0xB6, 0x89, 0x6C, 0x79, 0x02, 0x56, 0x79, 0x87, + 0xF4, 0xA7, 0x4B, 0x1D, 0xBC, 0xC0, + + 0x34, 0xF6, 0x75, 0xEA, 0x0F, 0x48, 0x36, 0xB3, + 0x59, 0x85, 0x62, 0x0F, 0xEF, 0x08, 0xA8, 0xEB, + 0x6F, 0x83, 0x96, 0x3D, 0xDA, 0x81, 0x49, 0xF3, + 0x9D, 0xBD, 0x99, 0x93, 0x3B, 0x86, 0xF3, 0x9F, + 0xEF, 0x0F, 0x16, 0x0F, 0xBD, 0x58, 0x11, 0x93, + 0x1E, 0xF2, 0xAB, 0x31, 0xB0, 0x2E, 0x8B, 0x3D, + 0x57, 0x97, 0x6F, 0x54, 0xA2, 0x75, 0x04, 0xBE, + 0x9E, 0xF2, 0x57, 0x23, 0xC7, 0x3C, 0x4A, 0x4B, + 0xF0, 0xBC, 0x61, 0x53, 0x88, 0xC5, 0x91, 0x34, + 0x58, 0xEE, 0x16, 0xA3, 0xB7, 0x68, 0xDE, 0x60, + 0x99, 0x50, 0xF6, 0x3B, 0x14, 0x55, 0xC0, 0xE7, + 0xEE, 0x34, 0xA4, 0x29, 0xED, 0xD5, 0x5E, 0x6A, + 0xD2, 0xAD, 0x09, 0xCD, 0x21, 0x63, 0x7D, 0xAD, + 0xCC, 0xB1, 0x15, 0xD4, 0x4A, 0x12, 0x7A, 0xD2, + 0xB5, 0xE3, 0x79, 0x7A, 0x88, 0xFE, 0x57, 0x77, + 0xB9, 0xE6, 0x08, 0xE4, 0xA4, 0x62, 0x7A, 0xED, + 0xBF, 0x4F, 0xC2, 0xD8, 0x66, 0x4A, 0x3D, 0xA8, + 0xEC, 0xC6, 0x1C, 0xE7, 0xAF, 0xC4, 0x2B, 0x3B, + 0xB5, 0x5D, 0xF0, 0x33, 0xBA, 0xF2, 0x61, 0x99, + 0x92, 0xCF, 0x34, 0x19, 0x99, 0x43, 0xF5, 0x9F, + 0xF6, 0x64, 0x5A, 0x40, 0x87, 0x53, 0x2D, 0x16, + 0x75, 0x68, 0x31, 0xAA, 0x61, 0xCF, 0x1F, 0xC3, + 0x32, 0x5B, 0x78, 0xFB, 0x8D, 0x67, 0x1F, 0x16, + 0x80, 0x3A, 0x7E, 0x05, 0x45, 0x7A, 0x5E, 0x43, + 0x0A, 0x2E, 0xE1, 0x22, 0xA8, 0x96, 0x79, 0xD5, + 0xC4, 0x2E, 0xE0, 0x1F, 0x43, 0x1B, 0x8D, 0xDC, + 0xBA, 0xCF, 0x7D, 0xF2, 0xD1, 0x70, 0xF6, 0x65, + 0x44, 0xCE, 0x2B, 0x08, 0x0D, 0x43, 0xC4, + + 0xF1, 0x1E, 0x0E, 0xFC, 0x7A, 0x10, 0x39, 0x3A, + 0x1E, 0xAD, 0x9E, 0x81, 0xC9, 0x04, 0xDB, 0x02, + 0x15, 0xC4, 0x9F, 0x6F, 0xD8, 0xF4, 0xCE, 0x77, + 0x00, 0x46, 0xB9, 0x0C, 0xD1, 0x4B, 0x1D, 0x6C, + 0x44, 0xFA, 0x19, 0x09, 0xB6, 0xEB, 0x34, 0x4A, + 0xC2, 0xDF, 0xF3, 0xF2, 0xE7, 0x3A, 0xE3, 0x6C, + 0xD4, 0xAF, 0x22, 0xFF, 0x40, 0xBC, 0x5C, 0x4A, + 0xBA, 0x7E, 0x54, 0xB1, 0xDB, 0xDF, 0x02, 0x2D, + 0xC2, 0xAD, 0x7B, 0x3E, 0x4C, 0x8E, 0x42, 0x97, + 0xDA, 0x00, 0xAA, 0x9F, 0x6A, 0x33, 0xB1, 0xFF, + 0xBB, 0x5E, 0x5F, 0x10, 0xE6, 0xA0, 0xF3, 0x25, + 0x8B, 0xAC, 0x36, 0xCF, 0xB6, 0x4A, 0x4D, 0x12, + 0x77, 0xDA, 0x17, 0xC5, 0xD5, 0xD2, 0xCB, 0xA1, + 0x95, 0xBB, 0xF1, 0xC9, 0xD8, 0xD3, 0xC1, 0xCC, + 0x67, 0xCE, 0xAB, 0x2B, 0x6A, 0x69, 0x41, 0x7C, + 0x7A, 0x47, 0x25, 0xE8, 0x39, 0xDC, 0x29, 0x31, + 0x36, 0x20, 0xA0, 0x58, 0x2A, 0x1A, 0xF0, 0x22, + 0xB9, 0x04, 0x10, 0xDA, 0x42, 0x2A, 0x6E, 0x1F, + 0x78, 0x01, 0x7C, 0x05, 0xE3, 0x1B, 0xA1, 0xC3, + 0x1A, 0x26, 0xA6, 0xF6, 0xBF, 0x54, 0xE9, 0x45, + 0x7F, 0x87, 0x08, 0xB8, 0xAF, 0xF0, 0xF0, 0xB4, + 0x96, 0xA4, 0xE3, 0xC0, 0x6A, 0x0B, 0xF9, 0x49, + 0x89, 0x1B, 0x9E, 0x69, 0x42, 0xD8, 0x21, 0x57, + 0x20, 0xC3, 0x95, 0xE5, 0xC6, 0x98, 0x06, 0x5E, + 0x69, 0x1F, 0x14, 0x8E, 0x9F, 0xF8, 0x58, 0x7C, + 0xEE, 0x13, 0xC7, 0x96, 0xC1, 0xE7, 0x71, 0x88, + 0x96, 0x54, 0xF9, 0xA7, 0xD6, 0x56, 0x7B, 0x8C, + 0xA5, 0x44, 0x42, 0xD3, 0x6B, 0x2E, 0x2E, 0x03, + + 0x7D, 0x39, 0xA9, 0xFF, 0x6A, 0x3D, 0x14, 0x95, + 0x1C, 0xB6, 0xD8, 0x13, 0x4E, 0x41, 0xF5, 0xD5, + 0x3F, 0x0F, 0xEC, 0x55, 0x1E, 0xAC, 0x95, 0x79, + 0x28, 0x5D, 0xBD, 0xBE, 0xC4, 0x7D, 0x31, 0x1F, + 0x64, 0x11, 0x9D, 0x0B, 0x8F, 0x06, 0xAB, 0x23, + 0x2D, 0x8D, 0xC4, 0x10, 0xE7, 0x20, 0x95, 0x60, + 0x76, 0xFC, 0xFA, 0xE3, 0x64, 0xC3, 0x67, 0x32, + 0xFA, 0x3D, 0x5D, 0x8F, 0xEA, 0xE3, 0x4F, 0x95, + 0x0A, 0x37, 0x4A, 0x30, 0x98, 0x93, 0x76, 0x20, + 0x7F, 0xB7, 0xA9, 0xE0, 0x6C, 0x42, 0xE7, 0xF8, + 0x57, 0xC8, 0xBE, 0xD3, 0x7A, 0x1E, 0xBE, 0xF8, + 0x30, 0x8C, 0x29, 0x95, 0x71, 0x29, 0xFD, 0x79, + 0x0E, 0x1D, 0xDD, 0x4B, 0x41, 0xE9, 0x5F, 0x7E, + 0xF5, 0x4C, 0x7F, 0xA2, 0x10, 0x06, 0x57, 0x86, + 0xAB, 0x10, 0xBF, 0x5A, 0xFE, 0xA2, 0x5F, 0xE8, + 0x70, 0x8F, 0xCC, 0x00, 0xD9, 0x86, 0x86, 0x2C, + 0x31, 0x2E, 0xF9, 0xAD, 0x56, 0x0C, 0xE9, 0xB9, + 0x42, 0x39, 0x3B, 0x0D, 0x47, 0x60, 0x6F, 0xF9, + 0xFB, 0xE8, 0xBE, 0xFC, 0x7D, 0x0B, 0xA3, 0xEC, + 0x09, 0x6E, 0x0E, 0x0B, 0x6D, 0xEC, 0x60, 0x17, + 0xE9, 0x34, 0x65, 0x6F, 0xA4, 0x84, 0x61, 0x13, + 0xB2, 0xA6, 0xD3, 0x56, 0x06, 0xD9, 0xAE, 0x9A, + 0x57, 0x78, 0x93, 0xF2, 0x86, 0xA5, 0x39, 0x27, + 0x20, 0x36, 0x7A, 0xE7, 0xDE, 0x63, 0x02, 0x90, + 0x52, 0x3B, 0x7D, 0x32, 0x24, 0xFA, 0x77, 0x3A, + 0xFF, 0x6D, 0x6D, 0x38, 0xB1, 0xDC, 0x78, 0x06, + 0x5A, 0xCE, 0x72, 0xE9, 0x72, 0x22, 0x72, 0x60, + 0x90, 0x25, 0x72, 0x00, 0x28, 0xA2, 0xC0, 0x4A, + 0x31, + + 0xFE, 0xCE, 0x1F, 0x8E, 0xEF, 0xB9, 0x81, 0xD1, + 0x1D, 0x7F, 0x88, 0x64, 0xF5, 0x96, 0xE5, 0xD0, + 0xCD, 0x3E, 0xBE, 0x2D, 0x39, 0x22, 0x61, 0xBA, + 0xC9, 0x91, 0xA1, 0xF6, 0x6E, 0x5F, 0x7E, 0x2A, + 0x90, 0xD0, 0x26, 0xB7, 0x6D, 0x1B, 0xAE, 0xE2, + 0x54, 0xDE, 0x79, 0xF8, 0x39, 0x1E, 0xA0, 0xFC, + 0x65, 0xE1, 0x45, 0x22, 0x7A, 0x1A, 0x06, 0x0A, + 0xBB, 0x59, 0x41, 0xF5, 0xCF, 0xC0, 0x4C, 0x76, + 0xB7, 0x98, 0xF8, 0x2F, 0x32, 0xE6, 0x5B, 0xC3, + 0x70, 0xF3, 0xF5, 0x18, 0xC5, 0x0B, 0xAF, 0x82, + 0x4A, 0x17, 0xEF, 0xA5, 0x96, 0xE2, 0x20, 0xBA, + 0xE5, 0x23, 0x55, 0xD2, 0x85, 0xA3, 0x1C, 0x1E, + 0x46, 0xDA, 0xCF, 0xD3, 0xAC, 0x56, 0xF6, 0x94, + 0xFE, 0x14, 0xBB, 0x15, 0xD6, 0xA0, 0xF3, 0x5B, + 0x5C, 0x62, 0xA3, 0xEC, 0xF8, 0x23, 0x75, 0x62, + 0x5D, 0xB9, 0x47, 0x46, 0x25, 0xF1, 0x9E, 0x88, + 0xC0, 0xF7, 0x5A, 0xC2, 0xA7, 0x97, 0xEA, 0xB1, + 0x35, 0x70, 0x02, 0x33, 0x29, 0xA9, 0xF9, 0x36, + 0xC8, 0xF0, 0x70, 0xCB, 0xD0, 0xD4, 0x07, 0x26, + 0xD6, 0x9C, 0x62, 0x37, 0xEB, 0xDD, 0xE1, 0x71, + 0x10, 0x49, 0x52, 0x86, 0x71, 0xC1, 0xB7, 0x78, + 0xE4, 0x85, 0xC2, 0x34, 0x3E, 0x83, 0xF2, 0xBE, + 0x31, 0x51, 0x65, 0xF4, 0x42, 0xE4, 0x66, 0x85, + 0xE8, 0xEF, 0xDD, 0xD9, 0xE0, 0x41, 0x3E, 0x25, + 0xF2, 0x9D, 0xCA, 0x48, 0xF3, 0x55, 0x99, 0x83, + 0xD6, 0xF3, 0x96, 0x37, 0xF5, 0x18, 0x90, 0x2E, + 0xA4, 0xA7, 0xE1, 0x08, 0x89, 0xFA, 0x54, 0x22, + 0xE8, 0x1D, 0x8E, 0xFE, 0x94, 0xED, 0x86, 0xF2, + 0x21, 0xE4, + + 0xE1, 0x07, 0xBB, 0xC9, 0x51, 0x1C, 0xA7, 0x07, + 0xE9, 0x61, 0x25, 0xB4, 0x69, 0x8D, 0x72, 0xF1, + 0xF0, 0x12, 0xBB, 0x01, 0xD0, 0xCA, 0x4F, 0x29, + 0x42, 0x6B, 0x97, 0x7E, 0xC1, 0x8B, 0x1D, 0x9B, + 0x64, 0xD9, 0x21, 0x24, 0xF3, 0xE0, 0xEA, 0x72, + 0xD3, 0x5E, 0xEB, 0x71, 0x27, 0xEE, 0xEF, 0x09, + 0x3A, 0xDF, 0x73, 0x2E, 0xBE, 0x30, 0x57, 0xCD, + 0xEB, 0x49, 0x28, 0x83, 0x58, 0xB6, 0xBB, 0x91, + 0x90, 0xAC, 0x38, 0xBF, 0xC5, 0x6A, 0x6F, 0x6E, + 0x2F, 0xFF, 0x98, 0xD2, 0xFF, 0xE1, 0xB6, 0x5C, + 0x10, 0x3F, 0xBB, 0xA5, 0xDB, 0xC8, 0x56, 0x64, + 0x0D, 0xD6, 0xBE, 0xD2, 0x76, 0xF0, 0x33, 0xA0, + 0x37, 0xD1, 0xC6, 0xF1, 0x32, 0x96, 0x84, 0x17, + 0xE9, 0x4D, 0x37, 0x1B, 0x76, 0x60, 0x5F, 0xAA, + 0xD0, 0x70, 0xEF, 0xA7, 0x83, 0x48, 0xC6, 0xAE, + 0xB0, 0xCF, 0x6B, 0x72, 0x2D, 0x43, 0x7A, 0x7C, + 0x18, 0x25, 0x6B, 0xC7, 0x16, 0x60, 0x5C, 0x5D, + 0xF9, 0x1E, 0xE4, 0x0B, 0xAD, 0x7B, 0x06, 0x23, + 0xCA, 0xB8, 0x46, 0x05, 0xFC, 0x47, 0xBF, 0xCC, + 0xF7, 0x78, 0x9A, 0xB6, 0xDE, 0x12, 0xA0, 0x5D, + 0x1C, 0x1B, 0xF5, 0x36, 0x77, 0x9E, 0x8F, 0x95, + 0x7E, 0x3F, 0x5F, 0x82, 0xE7, 0xF5, 0x3D, 0xF6, + 0xEF, 0x13, 0x95, 0xB5, 0xEE, 0x3B, 0x1F, 0x0E, + 0x20, 0x02, 0xEA, 0x85, 0xFB, 0x09, 0x84, 0xCE, + 0x93, 0x8D, 0x48, 0x67, 0xCC, 0xA0, 0x98, 0x2E, + 0xFF, 0xD4, 0x5D, 0x45, 0x81, 0x0D, 0x43, 0xA5, + 0xD5, 0xC9, 0xD1, 0x4D, 0x5B, 0x3A, 0x9F, 0x4D, + 0x85, 0x8E, 0x95, 0x6A, 0xD7, 0x98, 0x85, 0x95, + 0x6F, 0x3E, 0x1E, + + 0x8F, 0x79, 0x64, 0x19, 0x20, 0xB6, 0x73, 0xAD, + 0x05, 0xF9, 0x92, 0xB5, 0x64, 0xFD, 0x00, 0x8A, + 0xD1, 0x35, 0x86, 0xF9, 0x1B, 0x1C, 0x6C, 0x48, + 0x2A, 0x54, 0x07, 0xCD, 0x59, 0xD7, 0xC2, 0x1B, + 0xA5, 0xF6, 0x25, 0x80, 0xF2, 0xC7, 0x4D, 0xE6, + 0xC8, 0xB1, 0x6B, 0x02, 0x90, 0x19, 0x82, 0xCD, + 0xEB, 0x35, 0xC5, 0xB9, 0x9C, 0xE6, 0x18, 0xBF, + 0xD5, 0xFA, 0x16, 0x72, 0x5E, 0x41, 0x1B, 0x85, + 0xB3, 0xA6, 0x96, 0xA8, 0x3C, 0x84, 0x7A, 0xB9, + 0xCE, 0xA5, 0x18, 0xA8, 0xE5, 0x78, 0x9C, 0x1D, + 0x2D, 0x5F, 0x8C, 0x7C, 0x09, 0xF9, 0xE5, 0xFB, + 0xDD, 0x56, 0xD9, 0x95, 0x60, 0xB1, 0x12, 0xD6, + 0x86, 0x6C, 0x3B, 0xE8, 0x2C, 0x22, 0xE3, 0xAB, + 0xEF, 0xC5, 0x8E, 0x0A, 0x3F, 0x6E, 0x3A, 0x1B, + 0x5D, 0x39, 0x96, 0x1B, 0xC3, 0x8B, 0x59, 0x1C, + 0xC3, 0xE4, 0x10, 0xD7, 0x29, 0x43, 0x14, 0xEB, + 0x63, 0x88, 0x53, 0x6E, 0x44, 0x8F, 0xAD, 0x5A, + 0xDD, 0xF4, 0x8A, 0xD0, 0x20, 0x21, 0x02, 0x6B, + 0x81, 0x05, 0xB1, 0xC4, 0xD2, 0xA1, 0x48, 0x70, + 0xF7, 0x66, 0xE3, 0xBE, 0x15, 0xB5, 0xF5, 0x43, + 0x66, 0x12, 0x94, 0x56, 0x88, 0x31, 0xE6, 0xAC, + 0xC4, 0x54, 0x48, 0x9F, 0x51, 0x09, 0x3D, 0x95, + 0xA4, 0x83, 0x62, 0xD8, 0x47, 0x3D, 0xEB, 0xC4, + 0x5B, 0x6D, 0x38, 0x98, 0x93, 0x89, 0xF0, 0x1C, + 0x40, 0x00, 0x02, 0xE6, 0xCA, 0xDB, 0x95, 0xE6, + 0x8E, 0x44, 0xE0, 0x5F, 0xC9, 0x62, 0x55, 0x3F, + 0xC8, 0x48, 0x5D, 0x12, 0xE7, 0x01, 0x87, 0x5C, + 0x07, 0xF3, 0x0E, 0x0F, 0xD7, 0x89, 0x04, 0x3F, + 0x2A, 0x10, 0x70, 0xCC, + + 0x3C, 0xB9, 0x3D, 0x28, 0x90, 0xEB, 0x21, 0x7D, + 0x9E, 0xCC, 0x4D, 0xA5, 0x84, 0xF6, 0x96, 0x93, + 0x5D, 0x12, 0x84, 0x9E, 0x71, 0x56, 0x5C, 0xD4, + 0x71, 0xB9, 0x9F, 0xDF, 0x2B, 0x51, 0x43, 0xC5, + 0x1E, 0x98, 0xBE, 0x8D, 0xB8, 0xFE, 0x3A, 0x9C, + 0x22, 0x25, 0xF2, 0x0F, 0x7F, 0x52, 0xB3, 0x9A, + 0xB6, 0xD8, 0x31, 0xE9, 0xC3, 0x9A, 0x86, 0x89, + 0xB6, 0x1B, 0x63, 0x3F, 0xC0, 0x37, 0x7D, 0xFF, + 0x80, 0xC3, 0x28, 0x0D, 0xF1, 0x09, 0xB1, 0xD1, + 0x38, 0xA8, 0x99, 0xD9, 0x81, 0x32, 0x62, 0x16, + 0xDE, 0x2D, 0x16, 0x07, 0x78, 0x5D, 0xCB, 0xB8, + 0x3E, 0xB8, 0x01, 0x5A, 0xB5, 0x21, 0x6E, 0xA1, + 0x7B, 0xD8, 0x64, 0x00, 0xFC, 0x6B, 0xDF, 0xA4, + 0x4D, 0x07, 0x81, 0xEE, 0x76, 0x69, 0x43, 0x9F, + 0x90, 0x09, 0x24, 0x26, 0x72, 0xC5, 0x2F, 0xF5, + 0x50, 0x31, 0xC9, 0xB9, 0x3B, 0xAE, 0xDE, 0x1F, + 0x6B, 0xCC, 0xFA, 0xC6, 0xCB, 0xBC, 0xEC, 0x75, + 0x91, 0x3A, 0x0C, 0x5D, 0x13, 0xB9, 0x3E, 0x16, + 0x3C, 0xE4, 0xAC, 0x56, 0x60, 0x0C, 0x92, 0x2D, + 0x05, 0xD6, 0x1A, 0x01, 0xC4, 0x94, 0xAD, 0x19, + 0xEE, 0x3C, 0x79, 0x5D, 0x5C, 0x49, 0x8A, 0x67, + 0xAF, 0x19, 0x1B, 0xBA, 0x2C, 0x30, 0xA2, 0x47, + 0xF2, 0x35, 0x00, 0x15, 0xF0, 0x7B, 0xC1, 0xA2, + 0xB3, 0x9C, 0x49, 0x83, 0x81, 0x26, 0x71, 0x7E, + 0xF0, 0x34, 0x01, 0x02, 0xC0, 0xDE, 0x44, 0xFA, + 0xFA, 0x5F, 0x84, 0xBA, 0x23, 0x16, 0x39, 0x29, + 0x77, 0xD7, 0x5D, 0x17, 0xBA, 0x9A, 0x39, 0x93, + 0xBE, 0xBB, 0x01, 0x67, 0xD6, 0x23, 0x2B, 0x7F, + 0x9E, 0x29, 0x61, 0xCC, 0x4D, + + 0xCE, 0x24, 0x29, 0x8D, 0x54, 0x03, 0x84, 0x07, + 0xF8, 0x5A, 0x69, 0x9A, 0x3E, 0x01, 0xF5, 0x63, + 0xFC, 0x16, 0xB6, 0xF0, 0x20, 0x39, 0x81, 0x10, + 0xCD, 0x82, 0xD5, 0x5D, 0xAE, 0xED, 0xA6, 0x86, + 0xA5, 0xFF, 0xF8, 0x85, 0x6E, 0x0A, 0x36, 0x2F, + 0x6B, 0xF6, 0x62, 0xAB, 0x12, 0xE6, 0x41, 0x2B, + 0x3D, 0x4D, 0xDB, 0x80, 0x26, 0xC9, 0xAD, 0xE4, + 0x93, 0x05, 0x0C, 0x28, 0x23, 0xFF, 0x9B, 0xBF, + 0x85, 0x43, 0x82, 0xCD, 0x6E, 0x5F, 0xD4, 0x08, + 0x31, 0xFA, 0x52, 0x78, 0x99, 0x15, 0xD0, 0xD5, + 0xBD, 0x7C, 0xD4, 0x91, 0xCB, 0x0F, 0x53, 0xE7, + 0x5D, 0x53, 0xE8, 0xDA, 0xAB, 0x32, 0xDF, 0xD8, + 0xCF, 0xF5, 0x95, 0xF1, 0x41, 0xCF, 0x37, 0x2B, + 0xF0, 0x51, 0x93, 0x33, 0xAA, 0xFF, 0x36, 0x07, + 0x3C, 0xF9, 0x40, 0x18, 0x05, 0xA6, 0x1B, 0xEF, + 0x9E, 0x5E, 0x71, 0x77, 0x75, 0xD2, 0x23, 0xD6, + 0x0F, 0x5A, 0x84, 0x73, 0x6C, 0x23, 0x17, 0x18, + 0x78, 0x84, 0x41, 0xEC, 0x76, 0xD5, 0x66, 0xDE, + 0xF3, 0x22, 0x6E, 0x03, 0xD0, 0xF0, 0xFE, 0x67, + 0x8C, 0x4B, 0x4F, 0x43, 0xC8, 0x94, 0xBE, 0x8D, + 0x3C, 0x34, 0xA9, 0x1E, 0xC2, 0x27, 0x64, 0x90, + 0x13, 0x85, 0xA7, 0xC1, 0xC9, 0xF3, 0x6E, 0x50, + 0x0B, 0xD5, 0xD1, 0x38, 0xB0, 0x53, 0x0D, 0xDC, + 0x5A, 0xE8, 0x6B, 0x30, 0x91, 0xA2, 0x73, 0xDA, + 0x5C, 0xB3, 0x77, 0x36, 0xBC, 0xE1, 0xBF, 0xE0, + 0x85, 0x3B, 0xAC, 0x73, 0x13, 0xD1, 0x72, 0x0D, + 0x36, 0x47, 0xAE, 0x77, 0xA9, 0x91, 0x3B, 0x65, + 0x3F, 0x20, 0xED, 0x65, 0x02, 0x98, 0xAE, 0x1B, + 0x6B, 0x22, 0x17, 0x9F, 0x10, 0x64, + + 0x43, 0xA6, 0xF3, 0x60, 0x88, 0x3A, 0x74, 0x66, + 0xA5, 0x48, 0xB2, 0x8F, 0x82, 0x94, 0xCC, 0xFD, + 0xCB, 0x15, 0x13, 0x60, 0x9B, 0xE4, 0x97, 0x96, + 0x2E, 0x36, 0xF9, 0xB3, 0x7B, 0xEC, 0xF1, 0xC3, + 0x54, 0xF0, 0xBB, 0xA0, 0xA8, 0x4A, 0x28, 0xB7, + 0x3A, 0x2E, 0xEC, 0x36, 0x5D, 0x0F, 0x26, 0xDE, + 0x3A, 0x24, 0x53, 0x13, 0x3F, 0xEA, 0x7C, 0x03, + 0xEB, 0x83, 0x53, 0x76, 0x8E, 0x83, 0xBC, 0x15, + 0x77, 0x04, 0xCC, 0xEF, 0x16, 0x7A, 0x40, 0xBA, + 0x14, 0xAB, 0x79, 0xB3, 0x2C, 0x18, 0xCE, 0xEC, + 0xC0, 0xF0, 0x66, 0x61, 0x99, 0xF7, 0x31, 0x3C, + 0x69, 0x66, 0xB5, 0xC0, 0xF4, 0x22, 0x4E, 0xCC, + 0x8A, 0xE9, 0xAC, 0x94, 0xC7, 0xC4, 0xC0, 0x88, + 0xCB, 0xF3, 0x4C, 0x49, 0xD1, 0x87, 0x55, 0xB8, + 0x15, 0x34, 0x47, 0x4B, 0xF0, 0x5B, 0xB3, 0xBB, + 0x77, 0x15, 0xED, 0x3F, 0xF8, 0x4F, 0xD3, 0x9D, + 0xFE, 0x8C, 0xD5, 0x4C, 0xE1, 0x2B, 0x74, 0x6F, + 0xA3, 0xBF, 0x03, 0x8A, 0xDF, 0xC2, 0xBF, 0xC5, + 0x27, 0x0F, 0xAF, 0x53, 0x42, 0x9A, 0x69, 0x39, + 0x33, 0xC7, 0x73, 0x67, 0x2E, 0xCE, 0x88, 0xD6, + 0xCF, 0xBB, 0x7C, 0xCB, 0x0D, 0x84, 0x0E, 0xB9, + 0x54, 0xFD, 0x51, 0x9F, 0xE3, 0x62, 0x87, 0xC7, + 0x5F, 0x08, 0xFE, 0x4C, 0x2E, 0xEA, 0x7F, 0xF2, + 0xC6, 0xB8, 0xEB, 0xDC, 0x35, 0x02, 0x1A, 0x6C, + 0xEB, 0xC0, 0x73, 0x82, 0x34, 0x52, 0xBE, 0x55, + 0x14, 0xB9, 0x36, 0x3D, 0xC8, 0x88, 0x03, 0x51, + 0x35, 0x7D, 0x89, 0x1B, 0x36, 0xA9, 0xD4, 0x03, + 0xA0, 0x86, 0x66, 0x90, 0x10, 0xC0, 0xBD, 0x39, + 0x5D, 0xB5, 0x4A, 0x67, 0x49, 0x2A, 0xBC, + + 0x05, 0x95, 0xB2, 0x93, 0x14, 0x73, 0xB3, 0x70, + 0x89, 0xE4, 0xCC, 0x29, 0x8C, 0x60, 0x49, 0x46, + 0xBD, 0xC5, 0x33, 0x19, 0x32, 0xEE, 0xC7, 0xFA, + 0x6B, 0x93, 0x8B, 0x09, 0x75, 0x6D, 0xC6, 0x81, + 0xFA, 0xE9, 0xB1, 0x91, 0xBE, 0x74, 0xDF, 0x09, + 0xB6, 0xDB, 0x98, 0x67, 0x26, 0x7E, 0x8C, 0x0F, + 0x27, 0x5D, 0x38, 0x22, 0xCE, 0x70, 0x60, 0x7A, + 0xA1, 0xA5, 0x2F, 0xCC, 0x71, 0x18, 0x0C, 0x17, + 0x3D, 0x2A, 0xDD, 0xEC, 0xFA, 0xCB, 0x5B, 0x0E, + 0xD4, 0x4C, 0x3D, 0xED, 0x8A, 0xDE, 0xA5, 0x3D, + 0x1E, 0x35, 0xE4, 0xAB, 0xAF, 0x14, 0x5F, 0xF0, + 0x3F, 0xA0, 0x11, 0x5A, 0xC5, 0x64, 0x40, 0xEC, + 0x11, 0x90, 0x70, 0xDE, 0x01, 0x7F, 0xAD, 0xAE, + 0xE1, 0x30, 0x94, 0x6E, 0x1E, 0xE3, 0x22, 0xCD, + 0xC3, 0x91, 0x25, 0x77, 0x25, 0x9A, 0x62, 0xFA, + 0x43, 0xAC, 0x32, 0x8E, 0xE7, 0xB7, 0xF2, 0xD0, + 0x54, 0xD4, 0xC4, 0x6C, 0xB3, 0x88, 0xDD, 0xDA, + 0x82, 0xD6, 0xA4, 0x33, 0xC1, 0xAA, 0x42, 0xD1, + 0x26, 0x0C, 0xE7, 0xEC, 0x81, 0x87, 0x50, 0x56, + 0x00, 0xC0, 0xAC, 0xEE, 0x96, 0xBC, 0x67, 0xEB, + 0x03, 0x58, 0x38, 0x67, 0x71, 0x1B, 0xD8, 0x59, + 0xA2, 0xC5, 0x77, 0xA4, 0xD5, 0x39, 0x95, 0x13, + 0x60, 0x3C, 0x4F, 0x28, 0xD4, 0xB2, 0x9E, 0x15, + 0x3D, 0xB5, 0x24, 0x37, 0x32, 0xCC, 0x2C, 0x22, + 0xD3, 0x02, 0xAF, 0x60, 0x7F, 0x8E, 0x56, 0x59, + 0xC7, 0x52, 0x1A, 0xF7, 0x43, 0x70, 0x6E, 0xE6, + 0xFF, 0x2C, 0x94, 0x22, 0x85, 0x28, 0xEF, 0x24, + 0x83, 0x7F, 0x80, 0xAF, 0x4E, 0x7F, 0x37, 0x21, + 0x78, 0x9B, 0x31, 0x2E, 0x3A, 0xC0, 0xCE, 0x49, + + 0x1C, 0x1A, 0x99, 0x58, 0xBA, 0x52, 0xC5, 0x41, + 0xFB, 0x0A, 0x17, 0xAF, 0x23, 0x3B, 0xBF, 0x3D, + 0x5B, 0x70, 0x45, 0xB9, 0xC2, 0xF4, 0xD5, 0xB2, + 0xF9, 0x5E, 0x82, 0xC0, 0x19, 0x99, 0x3F, 0xCB, + 0x22, 0xEC, 0x10, 0xF3, 0x26, 0xBA, 0xC7, 0xD4, + 0x9B, 0x2D, 0xFF, 0x3C, 0x39, 0xE4, 0xFB, 0x4D, + 0xC5, 0x5A, 0x01, 0x43, 0xBC, 0x83, 0x9F, 0x17, + 0x4E, 0xDC, 0xFE, 0x4B, 0x2E, 0x3C, 0x67, 0xA9, + 0x2A, 0xDB, 0xC4, 0x28, 0xB6, 0xD7, 0xB1, 0x24, + 0xDB, 0x10, 0xE6, 0x27, 0xF9, 0x29, 0xFC, 0x63, + 0x57, 0x72, 0xB6, 0xA0, 0x8C, 0x25, 0x7F, 0x67, + 0x94, 0x70, 0xBB, 0xBF, 0x2F, 0x60, 0xF4, 0xF3, + 0x98, 0x85, 0xBD, 0xE6, 0x50, 0xDC, 0xA6, 0x0E, + 0x96, 0xE7, 0x9F, 0x3C, 0x4F, 0xAE, 0xC7, 0x95, + 0xDB, 0x67, 0xEC, 0x20, 0x54, 0x2B, 0x34, 0xFA, + 0x04, 0x98, 0x77, 0xEE, 0xAD, 0xE4, 0x65, 0x94, + 0x94, 0xD6, 0x6D, 0xE0, 0x81, 0x15, 0x1F, 0x10, + 0x0F, 0x34, 0xEE, 0x64, 0x47, 0xD4, 0x15, 0x07, + 0xF4, 0x38, 0xC5, 0xEE, 0x06, 0x6F, 0x8A, 0xE6, + 0x7A, 0x18, 0x2E, 0x2E, 0xAB, 0xE7, 0xD9, 0x5C, + 0xD6, 0x58, 0xE4, 0x22, 0x21, 0xFB, 0xE0, 0x34, + 0x46, 0xE1, 0xBE, 0x89, 0x17, 0xC7, 0x8A, 0xD7, + 0x22, 0xE6, 0xE4, 0xDF, 0xA8, 0xCE, 0xFF, 0x2D, + 0xC4, 0x63, 0xF8, 0x83, 0x70, 0x3B, 0x4F, 0xB6, + 0x62, 0xA0, 0x50, 0x46, 0x30, 0xB1, 0x3E, 0xE8, + 0x2E, 0xBE, 0xFC, 0xAA, 0x5C, 0xF5, 0x68, 0xB3, + 0x47, 0xBF, 0x65, 0x3E, 0x64, 0x72, 0x6F, 0x90, + 0x89, 0x88, 0x2F, 0x82, 0x35, 0xE9, 0xF6, 0xD1, + 0xFA, 0xEE, 0x25, 0xDC, 0xA0, 0x04, 0xD7, 0x0B, + 0x1E, + + 0xED, 0x32, 0x54, 0xE8, 0x2D, 0x03, 0x35, 0xD5, + 0x55, 0x62, 0x1F, 0xD2, 0x2A, 0xE5, 0x69, 0x47, + 0xD6, 0xD8, 0x92, 0xBC, 0x3D, 0xF1, 0xAB, 0x57, + 0xFE, 0x03, 0x12, 0xCE, 0xAD, 0xF8, 0x1D, 0x37, + 0xB2, 0x40, 0x72, 0xD0, 0xC8, 0x38, 0xE2, 0xD1, + 0xA4, 0x0F, 0xA0, 0x7F, 0x19, 0x72, 0xAF, 0x84, + 0x53, 0x8B, 0x71, 0x66, 0xBF, 0x56, 0x19, 0x3A, + 0xEA, 0x27, 0xFD, 0xF5, 0xB6, 0x43, 0xD4, 0xB8, + 0x8B, 0x27, 0xE8, 0x82, 0x0B, 0x24, 0x3C, 0xD7, + 0xFF, 0x31, 0xF3, 0x6D, 0x15, 0x5D, 0xBD, 0x1D, + 0x93, 0x0F, 0x98, 0x4E, 0x16, 0x00, 0x94, 0xEE, + 0xE6, 0x27, 0xBA, 0x19, 0xC2, 0xCE, 0x73, 0x63, + 0x0C, 0x71, 0xEC, 0x27, 0x70, 0x69, 0x2E, 0x96, + 0xA3, 0x20, 0x98, 0xCF, 0x2B, 0xDF, 0x50, 0x07, + 0xC8, 0x10, 0xCD, 0xA0, 0x6B, 0x9E, 0xBF, 0xEC, + 0xD3, 0x6E, 0x94, 0x70, 0x6B, 0x3F, 0xE1, 0x73, + 0x10, 0xDB, 0x9E, 0x98, 0x5B, 0x5B, 0x97, 0xB9, + 0x07, 0xF4, 0xD6, 0x55, 0xEC, 0x52, 0x86, 0x0E, + 0x14, 0xD5, 0xF9, 0x5F, 0xA9, 0xFB, 0xE1, 0x96, + 0x6E, 0xB1, 0xA8, 0xB0, 0xD3, 0xB1, 0x44, 0x08, + 0xF8, 0xBC, 0x97, 0xC8, 0xF7, 0xE8, 0xD5, 0xC9, + 0xD7, 0x9F, 0x2C, 0xCA, 0xC6, 0x35, 0x26, 0x4F, + 0x2F, 0x83, 0xC0, 0x88, 0x91, 0x8D, 0x9A, 0xD7, + 0xFC, 0x8A, 0x2A, 0x9C, 0x06, 0x8C, 0xFA, 0xC3, + 0xF3, 0xD0, 0x0A, 0x21, 0xE3, 0x4E, 0xDF, 0x6A, + 0xB4, 0x00, 0x9F, 0xF4, 0xB6, 0x6E, 0x41, 0xF2, + 0x1A, 0x5E, 0xC8, 0x8B, 0x36, 0xC2, 0x81, 0xDA, + 0x0C, 0x74, 0x20, 0xC5, 0x41, 0x60, 0xD1, 0x2A, + 0xEB, 0xAA, 0x51, 0xCB, 0x38, 0x10, 0x94, 0x8B, + 0xA2, 0x89, + + 0x90, 0xBB, 0xE5, 0x2E, 0xDE, 0xBA, 0xB1, 0x0F, + 0x5C, 0xB8, 0x39, 0x67, 0xF9, 0x10, 0x1A, 0x72, + 0x33, 0xA4, 0x9B, 0x47, 0x5C, 0xC3, 0x97, 0x66, + 0xA9, 0x31, 0xC6, 0xD6, 0xD1, 0x64, 0x51, 0x19, + 0x3A, 0xAC, 0xBF, 0x99, 0x1B, 0x1C, 0xEC, 0x32, + 0x4F, 0x69, 0xC7, 0x6D, 0xBD, 0x4A, 0xEE, 0x29, + 0x75, 0x66, 0x42, 0xF7, 0x40, 0x58, 0xE5, 0x05, + 0x84, 0x18, 0x8A, 0x82, 0xE8, 0x8E, 0x13, 0x19, + 0x89, 0x6F, 0xC1, 0xDD, 0x43, 0x0C, 0xC5, 0x63, + 0xE2, 0xC4, 0xCC, 0x33, 0x61, 0xF4, 0xE6, 0x6B, + 0x9B, 0x39, 0x5E, 0xA1, 0x8E, 0x80, 0x8C, 0x13, + 0x1B, 0xE5, 0xD4, 0x4D, 0x7A, 0xDD, 0xF4, 0x52, + 0x51, 0x0C, 0x0B, 0x1F, 0x23, 0x52, 0xC8, 0x52, + 0xF8, 0xD4, 0x88, 0x3E, 0xAE, 0xB8, 0x98, 0xF8, + 0x76, 0xB3, 0xAD, 0x6B, 0x20, 0xD4, 0x83, 0x87, + 0x68, 0x45, 0x5C, 0xA8, 0xB3, 0xA0, 0xA4, 0xB9, + 0xE7, 0x16, 0x06, 0x48, 0xEF, 0x45, 0x33, 0x29, + 0xAB, 0x75, 0xE6, 0x9D, 0x83, 0x74, 0xF9, 0xF7, + 0x22, 0xA0, 0xC5, 0xFF, 0x9E, 0x35, 0x6A, 0x46, + 0x7B, 0x80, 0x21, 0xE7, 0x47, 0xD8, 0x4C, 0x37, + 0xB1, 0x96, 0x05, 0xBD, 0xE2, 0x1B, 0xDF, 0xE9, + 0x34, 0x4B, 0xBF, 0x03, 0x72, 0xB8, 0x47, 0x6E, + 0xE4, 0xB8, 0xDB, 0x63, 0xD6, 0xC2, 0x62, 0x8E, + 0xA8, 0x28, 0x1F, 0xC3, 0x1C, 0x35, 0x1F, 0x2C, + 0xCD, 0x24, 0xAF, 0x6A, 0x38, 0x1D, 0xA1, 0xC8, + 0x07, 0xA7, 0xB7, 0x04, 0x5A, 0xC6, 0x08, 0x4E, + 0x29, 0xBB, 0xA2, 0x19, 0xFE, 0x54, 0x2F, 0xD8, + 0xF3, 0xB3, 0x3E, 0x60, 0xFF, 0x27, 0xB1, 0x88, + 0x2F, 0x2C, 0xB7, 0x66, 0x09, 0x97, 0xE5, 0x09, + 0x77, 0x5F, 0x53, + + 0xD3, 0xA5, 0x23, 0x44, 0x45, 0xCA, 0x7B, 0xF1, + 0x84, 0xF2, 0x59, 0x2F, 0x75, 0xC6, 0xC3, 0x49, + 0xEC, 0x4C, 0xA2, 0x4A, 0xF9, 0xD1, 0xBB, 0x1D, + 0xBB, 0x30, 0x2E, 0x18, 0xDD, 0xFF, 0xC8, 0x77, + 0x2D, 0x8F, 0x09, 0x6F, 0x65, 0x5A, 0x64, 0x6B, + 0xEB, 0x8A, 0xDF, 0xA2, 0x50, 0xBA, 0x5A, 0x54, + 0x79, 0xE5, 0x14, 0x69, 0x42, 0x34, 0xE8, 0xA0, + 0x62, 0xA5, 0x3A, 0x7A, 0x49, 0xB1, 0x8B, 0xA3, + 0xD8, 0x73, 0x57, 0x70, 0xE3, 0x93, 0x04, 0xE4, + 0x59, 0x86, 0x8A, 0x7B, 0xBE, 0x65, 0x48, 0x16, + 0xB5, 0xE6, 0xAF, 0xC3, 0xBD, 0xFF, 0x3C, 0xDF, + 0xF6, 0xCB, 0x60, 0xE4, 0xBA, 0x77, 0xF4, 0x4B, + 0x95, 0x79, 0xCE, 0xD6, 0x36, 0xBD, 0xBF, 0x36, + 0x42, 0x53, 0x15, 0xA3, 0x77, 0xB3, 0x9F, 0x68, + 0x98, 0x6E, 0xB5, 0x01, 0x16, 0xA2, 0x26, 0x79, + 0xB0, 0x1A, 0x65, 0x45, 0x30, 0xBB, 0x00, 0x4F, + 0xE4, 0x7A, 0xD0, 0x31, 0xE5, 0x77, 0xD4, 0xC6, + 0x6A, 0x72, 0xC5, 0xF2, 0xC1, 0xFC, 0x60, 0x74, + 0x99, 0xD5, 0x25, 0xF2, 0x7B, 0x6D, 0xFE, 0x32, + 0x1C, 0x90, 0xD0, 0xF9, 0xF0, 0xAE, 0x93, 0x20, + 0xB4, 0xA0, 0xF7, 0xA5, 0x75, 0xE4, 0x83, 0x94, + 0x80, 0x8C, 0x1E, 0x18, 0xD7, 0x1C, 0xA4, 0x5F, + 0x0A, 0x08, 0xD2, 0xB1, 0x27, 0xC4, 0x0D, 0xA4, + 0xBE, 0xE0, 0x36, 0x69, 0x19, 0x14, 0x9A, 0x58, + 0x9C, 0xD5, 0x36, 0xE5, 0x46, 0xE1, 0x94, 0x66, + 0xC3, 0x77, 0x78, 0x52, 0xF9, 0x2B, 0xDA, 0x95, + 0xDC, 0xED, 0x6A, 0x6E, 0x58, 0xE9, 0x16, 0x34, + 0x2A, 0x6B, 0xD5, 0x4E, 0xF2, 0x7E, 0xB8, 0x96, + 0xEB, 0x9B, 0x4D, 0xD3, 0xFD, 0xB4, 0x33, 0x0D, + 0x13, 0x26, 0x42, 0x3E, + + 0xF6, 0x0F, 0x59, 0xEC, 0xB0, 0xE1, 0x3D, 0x20, + 0xB8, 0xD7, 0x3D, 0xF9, 0x59, 0xCF, 0xEF, 0xA9, + 0xD7, 0xB2, 0x47, 0xBC, 0x87, 0x54, 0xD3, 0x7E, + 0xF9, 0x2C, 0xDF, 0xB6, 0x0F, 0x86, 0x13, 0x02, + 0x1C, 0xFA, 0xC0, 0x26, 0xDB, 0x49, 0x3B, 0x36, + 0x60, 0xA2, 0x64, 0xD8, 0x91, 0xB1, 0x7C, 0x35, + 0xEA, 0x2F, 0x5E, 0xF6, 0x99, 0xE2, 0x0B, 0x74, + 0x1F, 0x76, 0xF6, 0x8F, 0x7E, 0x58, 0x63, 0x3E, + 0xB6, 0xA2, 0xF3, 0x0A, 0xD4, 0xA5, 0x19, 0xB7, + 0x45, 0x08, 0xDB, 0x52, 0x17, 0x1D, 0xB1, 0x00, + 0x23, 0x0B, 0xD4, 0xC0, 0x86, 0x52, 0x49, 0xA8, + 0x33, 0xAB, 0x0A, 0xD1, 0x51, 0x9C, 0x5D, 0x4B, + 0x23, 0xF1, 0x93, 0x17, 0x6A, 0x48, 0xA6, 0x2F, + 0x9D, 0x0D, 0x9A, 0x09, 0xBD, 0x19, 0xA0, 0x39, + 0x60, 0xDE, 0xD1, 0xE4, 0x42, 0xD8, 0x34, 0xC0, + 0x1D, 0xD3, 0x88, 0x29, 0xA8, 0x48, 0x75, 0x5C, + 0xDF, 0x1D, 0xD4, 0x09, 0x53, 0x5E, 0x5D, 0x0A, + 0x9A, 0x2D, 0x95, 0x83, 0xF6, 0x85, 0x2F, 0xAD, + 0x8C, 0xFF, 0x55, 0xD8, 0x19, 0x56, 0xEE, 0x38, + 0xD2, 0x25, 0x30, 0x27, 0x21, 0xB5, 0x10, 0xED, + 0x24, 0xFB, 0x0E, 0x22, 0x55, 0xA2, 0xC3, 0x31, + 0xDC, 0x1E, 0xA0, 0xC7, 0x4B, 0x1B, 0xC0, 0x14, + 0x95, 0x25, 0x8F, 0x37, 0x3C, 0x75, 0x61, 0x44, + 0x0B, 0x09, 0x81, 0x8C, 0x32, 0xE5, 0x59, 0x3D, + 0xE3, 0xD5, 0x45, 0xDF, 0x11, 0xFF, 0x61, 0x9E, + 0x01, 0x10, 0x5E, 0x91, 0x70, 0x18, 0x1D, 0x76, + 0xC9, 0x74, 0x3C, 0xDD, 0x30, 0xB2, 0x01, 0xF5, + 0xCD, 0xCD, 0x9F, 0xFC, 0xE3, 0x7A, 0xB9, 0xD1, + 0x7F, 0xA4, 0x3C, 0x2E, 0x2C, 0x28, 0x25, 0xCC, + 0x6C, 0x56, 0x3A, 0x80, 0xF9, + + 0x7E, 0x13, 0xEF, 0xE6, 0x6B, 0x69, 0x25, 0xE0, + 0x81, 0xE9, 0x64, 0x4E, 0xE9, 0xFC, 0x00, 0x0C, + 0xDD, 0x81, 0x66, 0xB1, 0xC9, 0x12, 0xC4, 0x98, + 0x5F, 0x71, 0x2F, 0xFC, 0xFB, 0x24, 0x9E, 0x49, + 0x5E, 0xFB, 0x20, 0xB9, 0x56, 0x06, 0x23, 0xD3, + 0xD5, 0x80, 0x51, 0x25, 0xE6, 0x33, 0x55, 0xC0, + 0x48, 0x28, 0x32, 0xDB, 0xBB, 0x48, 0x6D, 0xF6, + 0x30, 0xA7, 0x4D, 0xC6, 0x54, 0x7A, 0x63, 0x5E, + 0x54, 0x7A, 0xE4, 0xEB, 0x71, 0x03, 0xA7, 0x65, + 0x1F, 0x36, 0x25, 0x32, 0xD0, 0x3F, 0xA1, 0x07, + 0xAB, 0xE0, 0x17, 0x57, 0x18, 0x06, 0xCB, 0x2B, + 0x78, 0x9F, 0xC3, 0xBD, 0x57, 0x13, 0x4B, 0x0A, + 0x52, 0xFC, 0x87, 0x39, 0xFF, 0xE6, 0xE8, 0x89, + 0x1D, 0x65, 0x61, 0x36, 0x6B, 0x93, 0xB8, 0xA9, + 0x71, 0x4B, 0xB3, 0xD4, 0x87, 0xBF, 0xAF, 0x89, + 0xB7, 0x57, 0x8F, 0x11, 0x3D, 0x36, 0xF2, 0x13, + 0x89, 0x92, 0x7C, 0xD6, 0xFE, 0x18, 0x9F, 0x4A, + 0x7F, 0xBE, 0x34, 0x1F, 0xAF, 0xCB, 0xFB, 0xCA, + 0x74, 0x7B, 0x4D, 0x93, 0x71, 0x45, 0xE4, 0xA0, + 0x4E, 0xE7, 0x4B, 0x9E, 0xFE, 0x45, 0x13, 0xDB, + 0xE9, 0x26, 0xD8, 0x75, 0xA5, 0xB6, 0x8A, 0x5E, + 0xF7, 0x7A, 0xE1, 0xA6, 0x78, 0xEB, 0xBA, 0xBA, + 0xC6, 0xB2, 0xC2, 0xCF, 0x04, 0x8A, 0x72, 0x6C, + 0xA2, 0x53, 0xDE, 0xC8, 0x91, 0xDB, 0x05, 0x46, + 0xA4, 0x86, 0x0C, 0xB8, 0xD0, 0x0F, 0x15, 0x10, + 0xD4, 0xC3, 0xA1, 0x53, 0xEA, 0x9F, 0x9A, 0x20, + 0x5A, 0xB3, 0xE8, 0x7A, 0x47, 0xC8, 0xCF, 0x87, + 0xA3, 0x7D, 0x54, 0x5C, 0xF1, 0x84, 0xE2, 0x5E, + 0x8A, 0x98, 0x8E, 0x7F, 0xAE, 0x18, 0x2F, 0x97, + 0xB3, 0x5D, 0x84, 0xFF, 0xEC, 0xAB, + + 0x81, 0x65, 0x82, 0x11, 0xCB, 0x36, 0xCA, 0xE9, + 0x04, 0x0E, 0x35, 0x85, 0xBE, 0xB3, 0x99, 0xDF, + 0xCF, 0xD5, 0x77, 0x38, 0xD4, 0xDC, 0xFE, 0x0D, + 0x01, 0xD3, 0x06, 0x2E, 0x83, 0x2E, 0x3A, 0x8C, + 0x90, 0x84, 0x17, 0x74, 0x28, 0x79, 0xFB, 0x3C, + 0x4B, 0x6D, 0x22, 0x43, 0xD6, 0x54, 0x6C, 0x6D, + 0x04, 0x36, 0xD7, 0x75, 0x95, 0xA0, 0xA8, 0xD1, + 0x42, 0xAC, 0x4B, 0x97, 0xAD, 0x69, 0x3F, 0xBA, + 0x2C, 0x0B, 0xC2, 0xCD, 0x3D, 0x70, 0x31, 0xEE, + 0xE6, 0x9E, 0x81, 0x28, 0x42, 0xAF, 0x67, 0x5D, + 0x3C, 0xFE, 0xDA, 0xF8, 0x89, 0x2F, 0xF8, 0xA0, + 0x19, 0x4E, 0x77, 0x87, 0x45, 0xBC, 0x0D, 0xB6, + 0xC0, 0x16, 0xE3, 0x4D, 0x46, 0x03, 0x89, 0x70, + 0xB0, 0xF3, 0xD3, 0x6F, 0x9A, 0x30, 0xD7, 0x51, + 0x05, 0xAE, 0xCC, 0x3A, 0xC5, 0x68, 0x56, 0x93, + 0x7B, 0x0F, 0x37, 0x96, 0x0E, 0x91, 0xD5, 0x7D, + 0xF9, 0xA6, 0xEB, 0x2C, 0x4E, 0xF6, 0x19, 0x0A, + 0xBA, 0x0F, 0x84, 0xA9, 0xD1, 0xFC, 0xD4, 0x72, + 0x0B, 0xCD, 0x79, 0xDE, 0x05, 0x49, 0x62, 0x4A, + 0x89, 0xEE, 0xB5, 0x6D, 0x5C, 0x28, 0xA7, 0xC7, + 0x96, 0x99, 0xF1, 0x0D, 0x35, 0xE2, 0xB3, 0x0E, + 0x20, 0xDB, 0x0A, 0xAD, 0x32, 0x72, 0x42, 0x40, + 0x60, 0x68, 0x39, 0xE1, 0xD9, 0x2E, 0x74, 0x90, + 0x59, 0xC7, 0xF1, 0xAE, 0x35, 0xC4, 0x0D, 0x8B, + 0xCE, 0xB1, 0xDC, 0x60, 0xD7, 0x03, 0x5A, 0x03, + 0xCD, 0x6F, 0xED, 0xC8, 0x8A, 0xF2, 0x5A, 0xE5, + 0x88, 0xBB, 0x24, 0xA5, 0x6C, 0x8E, 0x95, 0x84, + 0xF3, 0x02, 0x78, 0x5E, 0x7C, 0x8E, 0xDF, 0xCF, + 0xB1, 0x48, 0x5F, 0x16, 0x73, 0x38, 0x46, 0x69, + 0x3E, 0x41, 0x82, 0xBE, 0x29, 0xEC, 0xAF, + + 0x35, 0x6C, 0x92, 0x8B, 0x5B, 0xA9, 0xD1, 0x04, + 0x67, 0x82, 0x3C, 0x7E, 0x26, 0xA3, 0x0B, 0x58, + 0xD2, 0xC7, 0xC2, 0x3E, 0xC3, 0xDF, 0x79, 0x69, + 0xA6, 0x73, 0x38, 0xDC, 0x01, 0x60, 0x17, 0x37, + 0xD7, 0x46, 0xF4, 0xE6, 0x62, 0x72, 0xC7, 0x09, + 0xDB, 0x6F, 0x45, 0xDE, 0x59, 0x85, 0x48, 0xA1, + 0xA4, 0xF7, 0x6D, 0x83, 0x52, 0xC0, 0x64, 0xE3, + 0x4B, 0xFB, 0xCD, 0xAB, 0x62, 0x10, 0xF7, 0x40, + 0x07, 0xAA, 0x51, 0x83, 0xCF, 0xD8, 0xFD, 0xC1, + 0x35, 0x27, 0x0A, 0xCE, 0x21, 0x5E, 0x85, 0xF8, + 0x10, 0x0C, 0xAD, 0xCE, 0xD3, 0xB2, 0xF0, 0xCB, + 0x03, 0x4A, 0xE1, 0x8E, 0x8E, 0xF1, 0x05, 0xE3, + 0x1E, 0xBD, 0x35, 0xB3, 0x41, 0xEA, 0x39, 0x93, + 0xAD, 0xE2, 0xBC, 0xFD, 0x68, 0xCF, 0xAF, 0xDE, + 0x3F, 0xB3, 0xDD, 0xB3, 0x1A, 0x4C, 0x81, 0x6D, + 0x3E, 0x7E, 0x76, 0xA3, 0xB8, 0x26, 0x9D, 0xA4, + 0xD9, 0xAC, 0x88, 0x71, 0xEC, 0xE9, 0x4F, 0xA5, + 0xBF, 0x53, 0xB6, 0xFF, 0x52, 0xDA, 0xCB, 0x9B, + 0x2B, 0x67, 0xAF, 0x50, 0xEE, 0xCF, 0xA5, 0xD0, + 0x35, 0xAA, 0xB5, 0xE9, 0x34, 0x8E, 0x62, 0xD6, + 0x11, 0x94, 0xD0, 0xA7, 0xEF, 0xB9, 0xA2, 0x19, + 0x22, 0x17, 0xE4, 0x20, 0x35, 0xB6, 0x60, 0x8F, + 0x26, 0xAA, 0x3C, 0x41, 0xC8, 0xFC, 0xBC, 0xEE, + 0x5E, 0x42, 0xF6, 0x9B, 0x79, 0xBA, 0xAA, 0x4D, + 0x59, 0x18, 0xC9, 0x00, 0x9F, 0x28, 0x17, 0x3A, + 0x1F, 0xFC, 0x3B, 0x4C, 0xD5, 0xBF, 0x1B, 0x72, + 0x88, 0x5D, 0x22, 0x57, 0x7A, 0x47, 0x8A, 0x2C, + 0x19, 0x97, 0x80, 0x9A, 0x1C, 0x8A, 0x70, 0xA7, + 0xDA, 0xEB, 0xAE, 0x21, 0x81, 0x93, 0x63, 0xE9, + 0xDE, 0x12, 0xB2, 0xA6, 0xC3, 0x57, 0xD0, 0xD7, + + 0x04, 0xCD, 0xA5, 0x8D, 0x82, 0x25, 0xCC, 0xAE, + 0xDB, 0x8A, 0xBB, 0xCE, 0x96, 0x7D, 0x69, 0x62, + 0x5F, 0x34, 0x39, 0x4C, 0x82, 0xD3, 0xC6, 0x1E, + 0x27, 0x44, 0xDF, 0x66, 0xF0, 0x66, 0xF1, 0xA4, + 0x3E, 0xC8, 0x72, 0x58, 0x7C, 0x78, 0x50, 0x1B, + 0xAE, 0x6D, 0x22, 0x40, 0x27, 0x70, 0x6C, 0x12, + 0x1D, 0x44, 0x70, 0x1B, 0xC6, 0xD5, 0x76, 0x76, + 0xDD, 0xC4, 0x63, 0xE9, 0x17, 0xFF, 0xE4, 0x93, + 0x67, 0x24, 0xE0, 0x2C, 0x51, 0x3E, 0xCA, 0x62, + 0xC7, 0x0B, 0x47, 0x84, 0xDF, 0x98, 0x94, 0x85, + 0x40, 0xF0, 0xAA, 0xFC, 0x74, 0xCC, 0x15, 0x7E, + 0x9B, 0x14, 0x2D, 0x78, 0x38, 0x88, 0x12, 0x4D, + 0xB3, 0xE4, 0x64, 0xEB, 0x38, 0x3C, 0x0F, 0xC7, + 0x08, 0x26, 0x06, 0x9D, 0xD4, 0xCD, 0xCD, 0xCA, + 0x10, 0xA1, 0x9B, 0xCD, 0x03, 0x92, 0x75, 0x32, + 0xA5, 0x0F, 0xAA, 0xA4, 0xB0, 0xFE, 0x6B, 0xB3, + 0x47, 0xFD, 0x58, 0x9F, 0x69, 0x6D, 0xD5, 0x93, + 0xE7, 0x46, 0x92, 0xF5, 0x6B, 0xA7, 0x07, 0xBC, + 0x5E, 0xE1, 0x9D, 0x7D, 0xF6, 0xB5, 0x01, 0x64, + 0x6D, 0xDE, 0x24, 0x8F, 0xEF, 0x17, 0x4F, 0x74, + 0x3A, 0x13, 0x2B, 0x67, 0x05, 0x3C, 0x3B, 0xE2, + 0x17, 0x01, 0xD7, 0xAC, 0x2C, 0xD2, 0xEE, 0x89, + 0xE7, 0x50, 0xAB, 0x0B, 0x3A, 0x03, 0x6A, 0x05, + 0x2B, 0xAF, 0x7A, 0x71, 0x6D, 0x59, 0x13, 0x22, + 0xD4, 0x7F, 0xE0, 0xD4, 0xD5, 0xE6, 0x37, 0x4E, + 0x81, 0x04, 0xB1, 0x5C, 0xE4, 0x81, 0xE4, 0xE0, + 0x1D, 0x11, 0xF6, 0x3E, 0xE7, 0xB8, 0x7E, 0xDB, + 0xB3, 0x72, 0xE9, 0x63, 0x6B, 0xB7, 0xAA, 0x20, + 0x94, 0x14, 0x8E, 0x1C, 0x6D, 0xD3, 0xF4, 0xE6, + 0x3F, 0x14, 0xDD, 0x3F, 0xA3, 0x8A, 0x5B, 0xC3, + 0x0A, + + 0x10, 0x5A, 0xA3, 0x65, 0x9B, 0x9C, 0x5D, 0xBD, + 0x92, 0x5A, 0x84, 0x1B, 0x18, 0xA6, 0x4A, 0x43, + 0x69, 0xA2, 0xD2, 0x67, 0x8C, 0xC5, 0x90, 0xD6, + 0x33, 0xD7, 0xE5, 0x12, 0xEF, 0xCB, 0x68, 0x24, + 0xB9, 0x66, 0xF6, 0x69, 0xA5, 0x2D, 0x43, 0xDD, + 0xFC, 0x2C, 0x08, 0x3E, 0x71, 0xF3, 0x33, 0xF1, + 0xED, 0xBA, 0x0E, 0x8B, 0xE6, 0xF3, 0xA7, 0x90, + 0x61, 0xD9, 0x3B, 0xC6, 0xB6, 0x89, 0xEF, 0xE3, + 0x9C, 0x55, 0xCC, 0x92, 0xE0, 0xB7, 0xA8, 0xA2, + 0xBE, 0x6E, 0x3B, 0xF4, 0xC2, 0x47, 0x21, 0x0E, + 0x1B, 0xD8, 0x76, 0x19, 0x99, 0xCE, 0x97, 0x02, + 0x25, 0x37, 0x28, 0xC0, 0x8E, 0x13, 0x6E, 0xB7, + 0x1B, 0x6B, 0x7C, 0x40, 0x3E, 0x78, 0xD1, 0xD1, + 0x55, 0x84, 0xA7, 0x1E, 0x82, 0x5C, 0x5D, 0x8E, + 0x14, 0x2E, 0xF4, 0x5B, 0x76, 0xC7, 0xCB, 0x82, + 0x74, 0xB3, 0x6F, 0xF5, 0xFD, 0xDE, 0x74, 0xD5, + 0xBD, 0xFB, 0x37, 0xA9, 0x54, 0x90, 0x97, 0x08, + 0x5C, 0xA7, 0x13, 0x07, 0xC4, 0x8C, 0x0C, 0x51, + 0x5D, 0x22, 0x71, 0xFE, 0x25, 0x77, 0x13, 0x23, + 0x3C, 0x57, 0x04, 0xEC, 0xE5, 0xD4, 0x41, 0x4E, + 0xE7, 0xFA, 0x1B, 0x4A, 0x31, 0xE1, 0xC6, 0x35, + 0x99, 0x0D, 0x8B, 0xAC, 0x37, 0xAB, 0x38, 0x8C, + 0xEC, 0x59, 0x44, 0x8A, 0x5A, 0xD9, 0x7A, 0x99, + 0x95, 0x33, 0x7E, 0x65, 0xEF, 0x78, 0x63, 0xE2, + 0x6D, 0xFE, 0x7D, 0xAC, 0xA5, 0x05, 0x13, 0xB7, + 0x8E, 0xF2, 0x9B, 0x76, 0x7E, 0x6C, 0x54, 0x86, + 0xA2, 0xAA, 0x2F, 0x3B, 0x38, 0x46, 0x1D, 0x4E, + 0xDB, 0x36, 0x42, 0x88, 0x0D, 0x0C, 0x7B, 0xAF, + 0xAC, 0x58, 0x0B, 0x24, 0x99, 0xCB, 0x5E, 0x9D, + 0x6D, 0xDC, 0xE8, 0xE1, 0xDC, 0xD5, 0xC3, 0x31, + 0xBE, 0xD5, + + 0xB0, 0xD1, 0xFC, 0x60, 0x3A, 0x61, 0x99, 0x7C, + 0xDD, 0x25, 0x09, 0x27, 0x2F, 0x4F, 0xEA, 0x8D, + 0x71, 0x0C, 0xE3, 0x47, 0x8A, 0xBC, 0xB6, 0xBD, + 0x8B, 0x6E, 0xFC, 0x63, 0x4A, 0x49, 0x59, 0xA4, + 0x4D, 0x2C, 0x8B, 0xDB, 0x9C, 0xFA, 0x23, 0xAE, + 0xDF, 0xBA, 0x3A, 0x1A, 0x21, 0xED, 0x74, 0x3F, + 0x20, 0xC1, 0x66, 0xCF, 0xBB, 0x21, 0x96, 0x57, + 0xD4, 0x9A, 0xFF, 0xCD, 0xB6, 0x21, 0xE4, 0xA6, + 0xC1, 0x38, 0xDA, 0x54, 0x20, 0x9B, 0x21, 0xC9, + 0x22, 0x5D, 0x1E, 0xCD, 0x48, 0x8C, 0x85, 0xE2, + 0xCA, 0x4D, 0xE3, 0xE3, 0x82, 0x9F, 0x3A, 0xB9, + 0xC1, 0xB9, 0x01, 0x50, 0x1F, 0x13, 0xB0, 0xDF, + 0x12, 0x2D, 0x09, 0x85, 0xA2, 0xD7, 0x0D, 0x9D, + 0x01, 0x92, 0xC9, 0xEC, 0xE5, 0x50, 0x58, 0xF0, + 0xF8, 0x58, 0x7F, 0xD1, 0xCF, 0xFD, 0x77, 0xBD, + 0x73, 0x6B, 0x9B, 0x9D, 0x35, 0x25, 0x1D, 0x52, + 0x6B, 0xF0, 0x8E, 0x78, 0xF2, 0xCF, 0x52, 0x11, + 0x17, 0xF3, 0xB2, 0x20, 0x80, 0x22, 0x90, 0x7B, + 0xBD, 0x1B, 0x32, 0x1D, 0x17, 0xF8, 0xB0, 0xB0, + 0xBC, 0x82, 0xC1, 0x20, 0x8E, 0x79, 0x17, 0x18, + 0x6B, 0x8D, 0xD3, 0x57, 0xBE, 0xB6, 0xB0, 0x48, + 0x23, 0x5D, 0x70, 0x50, 0xA7, 0x2C, 0x55, 0xC4, + 0x39, 0x88, 0x5C, 0x7C, 0xA5, 0x74, 0x43, 0xA4, + 0x8E, 0x02, 0x4C, 0x8E, 0x7E, 0x05, 0x73, 0x6B, + 0x89, 0x44, 0xAA, 0x84, 0xF4, 0xD4, 0x0F, 0xB9, + 0xB1, 0x8B, 0x0D, 0xD5, 0x38, 0x32, 0xBD, 0x84, + 0x49, 0xB6, 0x3E, 0xD9, 0x65, 0xAB, 0x96, 0x2A, + 0x6A, 0x47, 0xD5, 0x0E, 0x21, 0x4F, 0xF7, 0xF9, + 0xE4, 0xE9, 0x46, 0xA1, 0x09, 0x7A, 0x7B, 0x0E, + 0x10, 0x3D, 0x3D, 0xE8, 0xB1, 0xBB, 0x07, 0x1A, + 0x54, 0xBA, 0xC2, + + 0x3C, 0xDD, 0x72, 0x30, 0xB1, 0xD2, 0x79, 0x40, + 0x8E, 0x03, 0x65, 0x7D, 0x82, 0x70, 0x77, 0xEB, + 0x77, 0x9B, 0x6C, 0x46, 0x20, 0x27, 0x40, 0xCF, + 0xAA, 0xF3, 0x2D, 0xBC, 0xF8, 0xE0, 0xA4, 0x04, + 0xAE, 0x72, 0xE7, 0x95, 0x38, 0xA2, 0xAA, 0x45, + 0x7F, 0xDB, 0x72, 0x58, 0x0C, 0x30, 0x70, 0x4E, + 0x8A, 0xE2, 0xDD, 0x2D, 0xA8, 0x03, 0x9A, 0xEB, + 0x99, 0x96, 0x8E, 0x9E, 0xC1, 0x65, 0x8C, 0xB9, + 0xD1, 0xE1, 0xED, 0xEC, 0x3C, 0x3F, 0xA8, 0xAB, + 0x54, 0x81, 0x2C, 0x16, 0x77, 0x3B, 0x6A, 0xB9, + 0x09, 0x51, 0xF0, 0x98, 0x38, 0xF3, 0x69, 0xC6, + 0x07, 0xB6, 0x98, 0x2C, 0x00, 0xDA, 0xFF, 0xD4, + 0x4B, 0x51, 0x00, 0xF4, 0xF4, 0xC6, 0x59, 0xFF, + 0x4D, 0x2F, 0xF0, 0x55, 0x91, 0x33, 0x58, 0xAC, + 0x0B, 0xA7, 0x43, 0x4B, 0x11, 0xDA, 0x6F, 0x3A, + 0x7A, 0xF1, 0xE5, 0xE4, 0x58, 0xB0, 0xA3, 0x5D, + 0x89, 0xDD, 0x6B, 0x84, 0x28, 0x04, 0x1E, 0x0B, + 0xD5, 0x3F, 0x5E, 0x87, 0x47, 0xE5, 0xCC, 0xCC, + 0xF8, 0x46, 0xD0, 0x41, 0xD4, 0x89, 0xC6, 0x31, + 0x4B, 0x9B, 0xF6, 0x64, 0xCB, 0xDE, 0x11, 0x36, + 0x7F, 0x1C, 0xC4, 0x4A, 0x10, 0xA0, 0xF0, 0xE2, + 0xA8, 0x9D, 0x80, 0x22, 0xAF, 0xFA, 0x1D, 0xCB, + 0x99, 0x08, 0x56, 0x12, 0x0C, 0xE8, 0x23, 0xF9, + 0x13, 0x68, 0x7C, 0x89, 0x5A, 0xAC, 0x00, 0x25, + 0x0A, 0xEF, 0xDD, 0xF1, 0xF4, 0xDC, 0xC1, 0x92, + 0x71, 0x12, 0xF2, 0xBF, 0x7F, 0xA9, 0x30, 0x5C, + 0x75, 0x43, 0x4F, 0x19, 0x7C, 0xD6, 0x39, 0xF2, + 0xDC, 0xAB, 0xE7, 0x36, 0x75, 0x26, 0x2C, 0xC5, + 0x4D, 0xBF, 0xD0, 0x4A, 0x20, 0x26, 0x50, 0x9A, + 0x31, 0x41, 0x29, 0xD0, 0x73, 0x53, 0x7A, 0x81, + 0xF4, 0x5D, 0xDB, 0x8E, + + 0x21, 0x55, 0x95, 0x56, 0x2A, 0xFA, 0x88, 0x2E, + 0xC9, 0x9B, 0x1E, 0xE9, 0xA4, 0x4C, 0x1A, 0xC7, + 0xFB, 0xF6, 0xA5, 0xE8, 0x5A, 0xFB, 0xF6, 0x53, + 0x4C, 0xCD, 0x0F, 0xE7, 0x0A, 0x97, 0x73, 0x95, + 0x7C, 0x0E, 0x11, 0x82, 0x07, 0x8F, 0x77, 0x2B, + 0xDE, 0x4D, 0x95, 0xDB, 0x27, 0x6D, 0x70, 0x89, + 0xC8, 0x5C, 0x1B, 0xC8, 0xEE, 0xEE, 0x8E, 0x75, + 0xDB, 0x1B, 0x5E, 0xF2, 0x1D, 0x6D, 0xD8, 0x8C, + 0x81, 0x3B, 0x58, 0x71, 0xBA, 0x12, 0x70, 0x12, + 0x6E, 0x8D, 0x13, 0x2E, 0x4B, 0x4A, 0x81, 0x93, + 0xA9, 0xAD, 0xA6, 0xB2, 0xB2, 0x4D, 0xE2, 0x26, + 0x2E, 0xC7, 0x31, 0xD1, 0xFB, 0x02, 0xAB, 0xE9, + 0x76, 0xBF, 0x84, 0xEF, 0xE2, 0x02, 0xDA, 0xDE, + 0x93, 0x08, 0x3F, 0x02, 0x37, 0x04, 0xC3, 0x37, + 0xB0, 0x5B, 0x7F, 0x04, 0x37, 0xC0, 0xA3, 0x67, + 0x3F, 0xFA, 0x31, 0x43, 0x10, 0x27, 0xA1, 0x36, + 0x3D, 0x56, 0xF9, 0x70, 0xDF, 0x6B, 0x51, 0x61, + 0x76, 0xD2, 0x22, 0xCC, 0x19, 0x66, 0x30, 0x63, + 0xBC, 0x25, 0x7A, 0x7E, 0x8B, 0x7B, 0x9D, 0xD7, + 0x49, 0x99, 0x67, 0x8C, 0x19, 0x2F, 0x51, 0x0F, + 0x01, 0xD7, 0x35, 0xF0, 0xA2, 0x20, 0x38, 0x03, + 0x9F, 0xCB, 0x42, 0x65, 0xC3, 0x6A, 0x0E, 0xA8, + 0xF6, 0x4F, 0xBF, 0x73, 0x3A, 0x1A, 0xDF, 0x48, + 0x03, 0x72, 0xF5, 0xB9, 0x94, 0x56, 0xD4, 0x46, + 0x93, 0xDD, 0x6B, 0xB3, 0xF9, 0x6B, 0xDF, 0xEC, + 0x88, 0x34, 0x6D, 0x39, 0x51, 0xEE, 0xFD, 0xA6, + 0x4B, 0xC7, 0x2B, 0xAE, 0xC6, 0x2A, 0x14, 0x15, + 0xDF, 0xB3, 0x7E, 0x02, 0x1C, 0x8D, 0xA8, 0xD4, + 0xDB, 0x19, 0xCF, 0x19, 0xEA, 0x8A, 0x14, 0xBC, + 0x67, 0xB1, 0x62, 0x35, 0x69, 0x84, 0x21, 0x08, + 0xF8, 0x5D, 0x44, 0x10, 0xD9, + + 0x0E, 0xE7, 0x88, 0x5B, 0x12, 0x67, 0xA8, 0x08, + 0x53, 0xD1, 0x65, 0x84, 0xE8, 0x9C, 0x12, 0xE3, + 0xBA, 0x44, 0x83, 0xA1, 0x1E, 0x73, 0xDE, 0x63, + 0x6C, 0xCC, 0xF7, 0xA4, 0x6B, 0xD0, 0x6B, 0xBF, + 0x3D, 0xAE, 0x4D, 0xF2, 0xB7, 0xF7, 0xC4, 0x30, + 0x2F, 0xB0, 0xEE, 0x09, 0x10, 0x20, 0xE1, 0x77, + 0x7B, 0xD4, 0xD1, 0x6B, 0x7F, 0x88, 0xA6, 0xE6, + 0x51, 0xD5, 0x4E, 0x9F, 0xFD, 0xFB, 0x1F, 0x4A, + 0x7C, 0xA7, 0x99, 0x32, 0x3F, 0x7C, 0xEA, 0xD9, + 0x36, 0xD4, 0x2C, 0xE1, 0xE0, 0x84, 0x2E, 0xFE, + 0x2A, 0x57, 0xA5, 0x56, 0xBD, 0xD3, 0x16, 0x8F, + 0x5A, 0x5A, 0x02, 0x20, 0xF1, 0x1B, 0xF0, 0x96, + 0x22, 0xBF, 0x1B, 0x24, 0xA4, 0xD2, 0x5A, 0x35, + 0x9E, 0xBA, 0x71, 0xB4, 0x5D, 0x13, 0x19, 0x8D, + 0x64, 0x4C, 0x5B, 0x22, 0xAA, 0x0E, 0x87, 0x1E, + 0xF6, 0x62, 0x8A, 0x94, 0xA6, 0x36, 0x03, 0xEA, + 0x6F, 0xB4, 0xFC, 0xA5, 0xC9, 0x98, 0x73, 0x54, + 0x38, 0x97, 0x6D, 0xC8, 0x08, 0x01, 0xCA, 0xD8, + 0x88, 0xE7, 0xAA, 0x9E, 0xCE, 0x3B, 0xA7, 0xBA, + 0x69, 0x50, 0xAB, 0x78, 0x9C, 0x83, 0x20, 0x23, + 0x31, 0x1C, 0xCC, 0x01, 0x52, 0xDF, 0x6F, 0xC2, + 0x56, 0x09, 0x57, 0xFF, 0xDE, 0x27, 0xD4, 0x17, + 0x4E, 0x6B, 0xD5, 0x60, 0x3A, 0x9F, 0xC8, 0xA4, + 0x1A, 0xAB, 0x82, 0x9A, 0x05, 0x15, 0xC8, 0x66, + 0x89, 0x40, 0x2A, 0x90, 0xF8, 0xEB, 0xD1, 0x2F, + 0x6E, 0x68, 0x13, 0xAD, 0x66, 0x39, 0x6C, 0xB1, + 0x7C, 0xEE, 0x33, 0xB7, 0xA6, 0x88, 0x69, 0xE1, + 0x32, 0x34, 0xA4, 0x87, 0xB1, 0x0C, 0xF3, 0x59, + 0xBE, 0x88, 0x72, 0x77, 0x76, 0x61, 0x45, 0x6F, + 0xF7, 0x75, 0x12, 0xB6, 0x03, 0x17, 0xE9, 0x2E, + 0x15, 0x93, 0xDA, 0xA9, 0xAE, 0x12, + + 0x4E, 0xB8, 0x67, 0xB1, 0x3C, 0x12, 0xCD, 0x1E, + 0x2D, 0xA3, 0x9C, 0xE5, 0xA6, 0x4A, 0x08, 0x0E, + 0x0D, 0x1C, 0x9B, 0xE7, 0xE5, 0x6D, 0x0A, 0x75, + 0xE0, 0xE4, 0x33, 0x3B, 0x43, 0x3D, 0x2C, 0x39, + 0x96, 0x43, 0x8F, 0xF6, 0xA4, 0xAA, 0x5E, 0x32, + 0xFE, 0x00, 0xCD, 0xD6, 0x9D, 0x75, 0x6F, 0x3A, + 0x20, 0x4E, 0x05, 0xB5, 0x7C, 0xE8, 0xDA, 0xE1, + 0x32, 0x10, 0xC0, 0x85, 0x8D, 0x34, 0x35, 0x12, + 0x41, 0x50, 0xBF, 0x58, 0xB8, 0xFF, 0x4E, 0x6B, + 0x04, 0x40, 0x76, 0xAD, 0xCC, 0x31, 0xDB, 0xF4, + 0x12, 0x7F, 0x30, 0x4E, 0x28, 0x28, 0x73, 0x67, + 0xED, 0x35, 0x38, 0x18, 0xD9, 0x9D, 0xEA, 0x68, + 0xFA, 0x65, 0x28, 0x7B, 0x99, 0x3A, 0x89, 0x7D, + 0x52, 0xE1, 0x59, 0x4C, 0x68, 0xC9, 0xDA, 0xFC, + 0x3D, 0x39, 0xE0, 0x81, 0xDB, 0x22, 0xBD, 0xBF, + 0xF3, 0xDA, 0x45, 0x1D, 0x3A, 0x8D, 0xF3, 0x97, + 0x9E, 0xDA, 0x2A, 0xD5, 0x4E, 0x2C, 0x23, 0x74, + 0x3A, 0xC9, 0x73, 0x4D, 0x24, 0x7E, 0x1A, 0x9B, + 0xF9, 0xD7, 0x21, 0x4D, 0x9A, 0x28, 0x21, 0x44, + 0xD4, 0xE8, 0x93, 0xC5, 0x4F, 0x9A, 0xF9, 0x66, + 0x09, 0xA5, 0x46, 0x31, 0x59, 0xB3, 0xCC, 0xF3, + 0xD4, 0x58, 0x9E, 0x8E, 0xB5, 0x35, 0x47, 0x5C, + 0x23, 0xC9, 0xFC, 0xE8, 0x64, 0x5D, 0x47, 0x6C, + 0x43, 0xAC, 0x47, 0x30, 0xAE, 0x40, 0x7A, 0x9E, + 0x1F, 0xF9, 0x74, 0x55, 0x10, 0x6F, 0x2A, 0xEB, + 0x50, 0x63, 0x51, 0xDD, 0x8E, 0x38, 0x6F, 0x27, + 0x1F, 0x21, 0x3C, 0xDC, 0x65, 0x63, 0xE4, 0xE8, + 0xD8, 0x63, 0x20, 0x67, 0x96, 0x22, 0xFD, 0xC3, + 0xA0, 0xDB, 0x90, 0x64, 0x56, 0xE6, 0xB0, 0xFE, + 0xB3, 0x7C, 0x74, 0x2C, 0x01, 0xD5, 0xCC, 0xD4, + 0x3C, 0x1B, 0xBA, 0x79, 0x68, 0x33, 0x7C, + + 0x3E, 0x82, 0xD7, 0x53, 0xF6, 0x16, 0xE3, 0x10, + 0x01, 0x36, 0xA8, 0xEF, 0x33, 0xCA, 0x26, 0xC9, + 0x21, 0xBF, 0xCA, 0x3B, 0x7D, 0x89, 0xC2, 0x59, + 0x1D, 0x57, 0x2B, 0x02, 0x05, 0xEA, 0x49, 0x99, + 0x31, 0x95, 0xFF, 0x0A, 0x45, 0x56, 0x1D, 0xB9, + 0xD3, 0x01, 0x44, 0xC4, 0x8C, 0x88, 0xAD, 0xC9, + 0x83, 0x6A, 0x35, 0x83, 0x81, 0xBC, 0x59, 0x0A, + 0xB0, 0x2A, 0x82, 0xE5, 0x89, 0x54, 0xA0, 0xAC, + 0x69, 0x1A, 0x63, 0x5B, 0x2C, 0x8A, 0x61, 0xAE, + 0x72, 0xD9, 0xDB, 0xF0, 0x6C, 0xAA, 0x8F, 0x61, + 0x46, 0x0C, 0x0A, 0x5D, 0xE6, 0x15, 0x92, 0x65, + 0x83, 0xCF, 0x80, 0xB6, 0x78, 0x9E, 0x87, 0x9B, + 0x13, 0xD5, 0x94, 0xD2, 0x1E, 0x7A, 0x28, 0x8B, + 0x14, 0x76, 0x0C, 0x7E, 0x41, 0xAE, 0x5B, 0x36, + 0x40, 0xD9, 0x30, 0x31, 0xFD, 0x73, 0x81, 0xCC, + 0x83, 0x22, 0x03, 0xFF, 0xB6, 0xCC, 0xFA, 0xD7, + 0x3A, 0x1C, 0xB4, 0xA8, 0x48, 0x47, 0xD9, 0xA9, + 0x54, 0xCE, 0x26, 0x92, 0x84, 0x19, 0x6D, 0x57, + 0x1B, 0x24, 0x3B, 0x5F, 0xBD, 0xBB, 0x7F, 0x18, + 0x97, 0xE2, 0x79, 0x81, 0x7B, 0x19, 0xB6, 0x9C, + 0x4F, 0xE6, 0xD5, 0x01, 0x8B, 0x41, 0xE5, 0x21, + 0x18, 0x76, 0x2A, 0x16, 0xC2, 0xEF, 0x20, 0xAC, + 0xB0, 0x0E, 0x58, 0x8C, 0x6D, 0xE7, 0x02, 0x35, + 0x15, 0x94, 0xDB, 0xC6, 0x58, 0xE3, 0xF0, 0x0F, + 0xBF, 0x6A, 0x63, 0xE0, 0x4A, 0x51, 0x32, 0x90, + 0x40, 0x84, 0xB6, 0x05, 0x92, 0xB1, 0x6B, 0xF8, + 0xCD, 0xCD, 0x7F, 0xF1, 0x43, 0x72, 0xBB, 0x8B, + 0x1E, 0xAF, 0xE5, 0x78, 0x75, 0x72, 0xE5, 0x72, + 0x14, 0x2D, 0x5B, 0x83, 0xD2, 0x40, 0x55, 0xFC, + 0x8C, 0x9B, 0xFB, 0xAE, 0xCC, 0x51, 0xBE, 0x56, + 0x9D, 0x4C, 0xC1, 0xFC, 0xF1, 0xC3, 0xC3, 0xAD, + + 0xA9, 0x2F, 0xB7, 0xB1, 0x24, 0x2E, 0xD1, 0x6E, + 0xAF, 0x5B, 0xC5, 0xF2, 0xF8, 0x5E, 0x43, 0x83, + 0xB4, 0x3D, 0x57, 0xD6, 0x16, 0x8D, 0x56, 0x31, + 0xE5, 0xEB, 0x44, 0x01, 0x9F, 0xCA, 0x6E, 0x77, + 0xC2, 0xAC, 0xAD, 0x84, 0x1E, 0x4B, 0x38, 0xCC, + 0x48, 0x36, 0xD6, 0x8E, 0xCA, 0xAD, 0x67, 0x3E, + 0x20, 0xD2, 0x3A, 0x84, 0xA2, 0x79, 0x60, 0xBD, + 0xA2, 0x1B, 0x7E, 0x41, 0x57, 0x2D, 0x52, 0x41, + 0x41, 0x1C, 0x32, 0x5F, 0xE4, 0x1B, 0x21, 0x55, + 0xA9, 0xAE, 0xFE, 0xB1, 0x80, 0xB2, 0x85, 0x0E, + 0x5A, 0xA8, 0x06, 0x4C, 0x73, 0x96, 0xC7, 0xDF, + 0x39, 0x6D, 0x5A, 0x69, 0x7F, 0x3C, 0xC1, 0x71, + 0xEF, 0xEC, 0x84, 0x23, 0x5F, 0x92, 0xD9, 0x07, + 0x2D, 0x73, 0x4A, 0xAB, 0x55, 0x46, 0xE8, 0xCC, + 0x46, 0x2C, 0x09, 0x7B, 0x65, 0xD7, 0xCF, 0x1A, + 0x3B, 0x27, 0xD3, 0x8C, 0x3B, 0x8A, 0xDC, 0xDB, + 0x36, 0x95, 0x06, 0x8D, 0x5B, 0x44, 0xBA, 0xBF, + 0x7E, 0x50, 0xAF, 0xC3, 0x63, 0x66, 0xB9, 0xD3, + 0xB0, 0xC4, 0x2C, 0x03, 0xBF, 0x7E, 0x70, 0xCF, + 0x93, 0xEB, 0x3D, 0xDC, 0x74, 0x16, 0x49, 0x0A, + 0x02, 0x98, 0x4F, 0x81, 0x6C, 0x29, 0x53, 0x39, + 0x25, 0xBA, 0xD4, 0x59, 0xF2, 0xB9, 0x92, 0xE6, + 0x83, 0x41, 0xE2, 0x7B, 0xE1, 0x6E, 0xA0, 0x8F, + 0xC4, 0xE8, 0x35, 0x76, 0x5A, 0x3A, 0xA4, 0x35, + 0x83, 0x85, 0x77, 0xA9, 0x63, 0x00, 0xA1, 0x29, + 0xD5, 0x43, 0x05, 0x59, 0xA3, 0x52, 0x3E, 0xE4, + 0xDA, 0xC1, 0x72, 0x36, 0x8F, 0x5E, 0xA0, 0x8B, + 0x88, 0x3D, 0xE9, 0x19, 0x11, 0x89, 0x3F, 0x0E, + 0xF1, 0x04, 0xA9, 0x4C, 0x87, 0xFF, 0x08, 0x89, + 0xF9, 0x13, 0xEB, 0xE7, 0x28, 0x41, 0x0B, 0x58, + 0xA4, 0xA9, 0x19, 0xC7, 0x4F, 0x34, 0xFD, 0x54, + 0x0A, + + 0x79, 0x14, 0x36, 0xA5, 0x6B, 0x3F, 0x22, 0xFA, + 0x12, 0x4B, 0x4F, 0xC4, 0x97, 0xD7, 0xC7, 0x61, + 0x94, 0x53, 0x53, 0xD3, 0xF5, 0x07, 0x64, 0x7D, + 0xE0, 0x11, 0x63, 0xC5, 0xE7, 0x4A, 0x88, 0xEC, + 0x6C, 0x0D, 0xA5, 0x05, 0x13, 0xE4, 0xC6, 0x94, + 0x3D, 0xEE, 0x87, 0xB1, 0x77, 0xCE, 0xB6, 0x7E, + 0xC9, 0x99, 0xB5, 0xAA, 0xB2, 0xC8, 0x0F, 0x5F, + 0xE9, 0x92, 0x9F, 0x1E, 0x49, 0x6D, 0xC8, 0xD7, + 0x6D, 0x1E, 0xD7, 0x63, 0x6D, 0xDD, 0x64, 0xCB, + 0xFF, 0x45, 0xB0, 0xF6, 0x89, 0xC5, 0xF3, 0xFB, + 0x82, 0xB9, 0x5B, 0xF1, 0x42, 0x4A, 0x8C, 0x9E, + 0xCD, 0x9D, 0xEF, 0x9D, 0x36, 0x79, 0x04, 0x7D, + 0x16, 0x6D, 0xC6, 0x66, 0xAA, 0xFE, 0x1D, 0xAB, + 0x4D, 0xDF, 0xF8, 0xE8, 0xC3, 0x12, 0xB3, 0xAD, + 0x49, 0xD1, 0x85, 0x7D, 0x2C, 0x76, 0xD9, 0x66, + 0x85, 0xA0, 0x81, 0x29, 0x2B, 0x13, 0x47, 0x1E, + 0x98, 0x77, 0x79, 0x3F, 0x23, 0xD2, 0xFC, 0xA5, + 0xA5, 0x64, 0xDF, 0xBB, 0x3E, 0xA8, 0x85, 0x98, + 0xB1, 0x34, 0x2B, 0xB7, 0xA0, 0xD1, 0x7A, 0x47, + 0x42, 0xFE, 0x98, 0x80, 0xBB, 0x21, 0x19, 0x4F, + 0xE0, 0x58, 0x2D, 0xD9, 0xE2, 0x0F, 0x46, 0x13, + 0xC7, 0x4A, 0x11, 0xDD, 0x49, 0xEC, 0x32, 0x0C, + 0x1C, 0x3C, 0x96, 0x75, 0x3C, 0xB4, 0x73, 0x07, + 0x3E, 0xBC, 0xA4, 0xB8, 0x17, 0x05, 0x38, 0x92, + 0x6B, 0x56, 0xF3, 0x28, 0xE3, 0x71, 0x73, 0x2A, + 0x30, 0x4C, 0xF5, 0xCF, 0x2A, 0xC5, 0x1B, 0x24, + 0x4E, 0xA0, 0x2C, 0x9A, 0xCA, 0x98, 0x8C, 0xCA, + 0x16, 0xD3, 0xA6, 0x77, 0xC7, 0x83, 0x3C, 0x25, + 0x96, 0x0D, 0x15, 0xBE, 0x33, 0x7E, 0xCA, 0x3A, + 0xC0, 0x37, 0x7C, 0x91, 0x04, 0xF0, 0x4D, 0x8D, + 0xAE, 0xBF, 0x6B, 0x99, 0x57, 0x63, 0x90, 0x10, + 0x41, 0xF1, + + 0x83, 0xD6, 0xF5, 0x41, 0x9F, 0x3B, 0x2C, 0x9B, + 0xAB, 0xAF, 0x1B, 0xAE, 0x69, 0xB4, 0xAA, 0x5B, + 0xF9, 0x5F, 0x0F, 0xC7, 0xD4, 0x7F, 0x90, 0xAF, + 0xB2, 0xBA, 0xAB, 0x86, 0x92, 0x8E, 0x66, 0x46, + 0xD5, 0x73, 0x08, 0xB7, 0x3E, 0x46, 0x86, 0xC0, + 0x1C, 0x72, 0xB1, 0xDF, 0x7A, 0x3A, 0xC4, 0xEF, + 0x85, 0x36, 0x21, 0x3C, 0x29, 0xB9, 0x68, 0xFD, + 0x48, 0xBD, 0x1A, 0x5C, 0x83, 0xA3, 0x02, 0xFD, + 0x67, 0xBF, 0xC3, 0xD1, 0x68, 0xE1, 0xB8, 0x91, + 0x57, 0xA5, 0x7B, 0xE8, 0x4B, 0x82, 0x7C, 0x76, + 0xEB, 0x1C, 0x38, 0x4C, 0x1E, 0x54, 0xE8, 0x66, + 0x47, 0x47, 0x8A, 0x41, 0xD7, 0x38, 0xCC, 0x8E, + 0xD8, 0x58, 0x20, 0x26, 0x79, 0x7B, 0x5A, 0xCF, + 0xD0, 0x0E, 0x6C, 0xE4, 0x91, 0x01, 0xE4, 0x95, + 0xF8, 0x6D, 0x7F, 0xFD, 0x38, 0x04, 0x41, 0x23, + 0x72, 0xC7, 0x32, 0xD9, 0x65, 0x4F, 0xCF, 0x3D, + 0xE8, 0xA3, 0xA7, 0x9C, 0xEF, 0x05, 0xDE, 0x12, + 0x70, 0x96, 0x04, 0x2C, 0x75, 0x06, 0xBE, 0xEC, + 0xA0, 0x37, 0x43, 0xF7, 0x39, 0x7E, 0x90, 0x39, + 0xAB, 0xFF, 0x97, 0x27, 0x05, 0xBC, 0x9D, 0xB9, + 0xDB, 0x8C, 0xFB, 0x10, 0x55, 0xC1, 0x63, 0xAA, + 0x59, 0xD3, 0x09, 0xC7, 0x49, 0xDB, 0x18, 0x2B, + 0x4A, 0xAE, 0x2B, 0x0E, 0xE0, 0x45, 0x3E, 0x96, + 0xF8, 0x82, 0x0F, 0xC4, 0xB7, 0x17, 0x3A, 0x0B, + 0x1D, 0x43, 0x81, 0x33, 0xA5, 0xDD, 0x26, 0x12, + 0xBF, 0xD9, 0x56, 0x13, 0xD8, 0xBB, 0xBE, 0xA8, + 0xAE, 0xCE, 0xF0, 0x99, 0x95, 0x66, 0xA0, 0xB5, + 0xD1, 0x94, 0xA3, 0xF8, 0x42, 0x3F, 0x3B, 0x0F, + 0x56, 0x9A, 0xBF, 0xAE, 0xEE, 0xC1, 0xA8, 0x25, + 0xFA, 0xF5, 0x0D, 0xAE, 0x4F, 0x40, 0x2B, 0xB2, + 0xEF, 0xB1, 0x61, 0xCF, 0x41, 0x6D, 0x96, 0x4D, + 0x8E, 0x0B, 0xFE, + + 0x24, 0xE7, 0xE5, 0x6C, 0xA2, 0x9D, 0xFB, 0xD1, + 0x90, 0xAA, 0xF0, 0xE7, 0xA6, 0x08, 0x5C, 0x80, + 0x69, 0xEE, 0x83, 0xE9, 0x8B, 0x70, 0x0B, 0x97, + 0xFB, 0xF0, 0xE5, 0x43, 0x81, 0x6D, 0xDC, 0xE9, + 0x4C, 0x14, 0xFA, 0xEA, 0x1F, 0x39, 0x31, 0x2A, + 0x60, 0x87, 0x10, 0x95, 0x19, 0x3A, 0x97, 0x09, + 0xF6, 0x7A, 0xDF, 0xF1, 0xAE, 0xB5, 0x43, 0x0D, + 0x6C, 0x1E, 0x2C, 0xC9, 0x2A, 0xF3, 0x76, 0xDA, + 0x6A, 0xEB, 0xE4, 0x92, 0x74, 0x82, 0xF1, 0x4C, + 0x64, 0x28, 0x01, 0xB0, 0xE1, 0xDF, 0x9A, 0x6B, + 0x89, 0x02, 0xE0, 0x43, 0xC6, 0x77, 0x18, 0xE8, + 0xC4, 0xC9, 0x98, 0xF2, 0x8F, 0xE0, 0xB4, 0x38, + 0x50, 0x81, 0xC7, 0xF4, 0x3F, 0xE2, 0xD9, 0xCB, + 0x5E, 0xE9, 0x5B, 0xC5, 0x98, 0xD2, 0xEB, 0xC6, + 0x13, 0x3E, 0xC1, 0x13, 0xFD, 0x54, 0x48, 0xA0, + 0x09, 0x64, 0x71, 0xE5, 0xCA, 0xC0, 0x75, 0x2D, + 0xF7, 0x89, 0x5B, 0x00, 0x05, 0xE1, 0x4B, 0xA8, + 0xDA, 0x12, 0x6A, 0x4E, 0xCE, 0xAB, 0x3A, 0xDF, + 0x4A, 0x90, 0xF4, 0xCA, 0x6C, 0x40, 0x00, 0xF9, + 0x9F, 0x92, 0xF7, 0x94, 0x53, 0x77, 0x55, 0x7F, + 0xA8, 0xA5, 0xBC, 0xC2, 0xC2, 0x45, 0xBB, 0x07, + 0x13, 0x4D, 0x9E, 0x0D, 0xCF, 0x49, 0xF1, 0x5A, + 0x69, 0xDF, 0x2D, 0x53, 0x54, 0xB7, 0x7A, 0xD4, + 0x2F, 0x1B, 0x99, 0x78, 0x84, 0x81, 0x04, 0xC4, + 0x5C, 0x26, 0x35, 0x01, 0xED, 0x75, 0xBC, 0x54, + 0x71, 0xA3, 0x96, 0x9E, 0xD4, 0x41, 0xCB, 0x05, + 0xB3, 0x08, 0x98, 0xAD, 0x93, 0x15, 0xF7, 0xC2, + 0xF4, 0xCB, 0xE6, 0xB7, 0xEB, 0x3F, 0xF7, 0x62, + 0x45, 0x40, 0xE3, 0x97, 0x58, 0xC4, 0x49, 0xED, + 0xBA, 0x92, 0xA3, 0x94, 0x1F, 0xCD, 0x66, 0x9C, + 0xA6, 0x85, 0x1F, 0xBF, 0xA1, 0xAA, 0x03, 0x1B, + 0xB2, 0x70, 0x9D, 0xE3, + + 0x0B, 0x45, 0xA1, 0x98, 0x47, 0xD8, 0xB2, 0x5D, + 0x41, 0xA7, 0x35, 0xE4, 0x41, 0x4B, 0x45, 0x6A, + 0x9A, 0x91, 0xA5, 0x66, 0xDA, 0x63, 0xE7, 0xB8, + 0x4E, 0x88, 0x0A, 0x80, 0x2E, 0xEC, 0x08, 0x74, + 0x8E, 0xEB, 0xDA, 0xC4, 0x6D, 0xB5, 0xDA, 0x27, + 0xEC, 0x67, 0xAC, 0x7D, 0x63, 0x7A, 0xF4, 0x9B, + 0xB6, 0x43, 0xE5, 0x87, 0xA7, 0x52, 0x34, 0xFD, + 0x15, 0x99, 0xC7, 0x13, 0x3E, 0xC6, 0x4F, 0x38, + 0xA3, 0x8B, 0xD1, 0xB4, 0x5E, 0x0E, 0xC0, 0xA8, + 0x9B, 0x34, 0x47, 0xC3, 0x61, 0x82, 0x0D, 0xBF, + 0x78, 0x2F, 0x90, 0x7B, 0x07, 0x5A, 0x2B, 0xA2, + 0xB6, 0x9D, 0xB0, 0x15, 0x0A, 0x32, 0xA1, 0x70, + 0x4E, 0x8D, 0x98, 0xC4, 0xB7, 0xE0, 0xD0, 0xBE, + 0x1D, 0xB4, 0xA3, 0xC9, 0x6A, 0xA4, 0xE6, 0x4F, + 0x12, 0xC2, 0x35, 0xD7, 0xA6, 0x69, 0x67, 0x18, + 0x2C, 0xB0, 0xCF, 0x39, 0x78, 0xDB, 0xB7, 0x66, + 0x50, 0xFD, 0xB7, 0x3F, 0x77, 0x7F, 0x4E, 0x3C, + 0xF8, 0xBD, 0xC5, 0xD2, 0xCA, 0xCB, 0xCB, 0x10, + 0x94, 0x9F, 0x3E, 0xA0, 0x5B, 0xBA, 0x74, 0xB9, + 0x84, 0xFE, 0xB1, 0x91, 0x93, 0x5D, 0x8B, 0x23, + 0x86, 0x8F, 0xCB, 0xF0, 0x0E, 0x27, 0xA8, 0x6A, + 0x57, 0xF3, 0x7A, 0xEE, 0x94, 0x5F, 0x29, 0x3F, + 0xF8, 0xD3, 0xD1, 0x30, 0xCD, 0x8B, 0x31, 0xFC, + 0x6E, 0xC6, 0x31, 0xB7, 0x76, 0x26, 0x12, 0x7A, + 0xB9, 0xB0, 0x29, 0x4D, 0xAF, 0x10, 0xFC, 0xCC, + 0xD1, 0xD6, 0x9C, 0x19, 0x81, 0x15, 0xC4, 0x19, + 0xDD, 0x5D, 0x56, 0x65, 0x6C, 0xE0, 0x39, 0x25, + 0x5C, 0xE5, 0xE2, 0x6E, 0x5D, 0x48, 0x44, 0xF8, + 0x53, 0x4A, 0x36, 0x48, 0xDB, 0x0D, 0x89, 0xF7, + 0x1B, 0xF5, 0xD0, 0x8E, 0xA2, 0xD8, 0xD7, 0x9E, + 0xCE, 0x35, 0xA0, 0x10, 0xE5, 0xBC, 0x22, 0xF9, + 0xC3, 0x35, 0xEC, 0x35, 0x6C, + + 0x59, 0x77, 0x61, 0xB9, 0xD2, 0xA0, 0x7C, 0xD6, + 0x95, 0x93, 0x17, 0x1B, 0x25, 0x09, 0x47, 0xF1, + 0x1B, 0x73, 0x28, 0xA8, 0xA1, 0xF6, 0x0F, 0x7F, + 0x4B, 0x86, 0xED, 0xC5, 0xBD, 0x15, 0x92, 0x75, + 0x23, 0x3E, 0x38, 0x30, 0xE0, 0x64, 0x80, 0x7B, + 0x10, 0xD5, 0x54, 0x08, 0x6E, 0x3D, 0x78, 0xFE, + 0x40, 0x0C, 0xFD, 0x86, 0xC1, 0xD1, 0xD4, 0xE7, + 0xF0, 0xD2, 0xFF, 0x52, 0xA4, 0x8F, 0xA8, 0x0A, + 0x9C, 0x6E, 0x49, 0x70, 0xC6, 0x7D, 0x7C, 0xDD, + 0xCE, 0x75, 0x09, 0xE3, 0xBF, 0x5A, 0x4A, 0x30, + 0xFD, 0xC0, 0x2B, 0x8F, 0x23, 0x39, 0x6B, 0x41, + 0x17, 0x51, 0x1F, 0x16, 0xAC, 0x33, 0x69, 0x42, + 0xD2, 0xE2, 0x32, 0x91, 0x81, 0xCB, 0x7C, 0x04, + 0xC8, 0xD9, 0x77, 0x1A, 0x59, 0xC7, 0x37, 0x5C, + 0x5B, 0xC3, 0x9F, 0x22, 0xE5, 0xDA, 0xD8, 0x59, + 0x3C, 0x01, 0xE6, 0xCC, 0x46, 0x5C, 0x90, 0xE0, + 0x3F, 0x4D, 0x77, 0xAD, 0x96, 0x2A, 0x5D, 0x37, + 0x0F, 0x59, 0x10, 0xF0, 0x23, 0xF0, 0xA9, 0x75, + 0xED, 0xDE, 0xFB, 0x74, 0x06, 0x2B, 0xBF, 0x20, + 0xED, 0xEA, 0xCD, 0x49, 0x8A, 0x41, 0x09, 0x6C, + 0x0F, 0x0E, 0x79, 0xF7, 0x61, 0x18, 0xBF, 0x77, + 0x09, 0xB9, 0x05, 0xEF, 0x89, 0xEE, 0x79, 0xD9, + 0x20, 0xAF, 0xE3, 0x3F, 0x0E, 0xCE, 0x3E, 0x94, + 0x0C, 0x3B, 0xC1, 0xA0, 0x88, 0xA8, 0x8D, 0xDB, + 0x4D, 0x70, 0xC6, 0x19, 0x2A, 0xCE, 0xE1, 0x9D, + 0x9E, 0x60, 0xBD, 0x93, 0x2D, 0xF1, 0xC6, 0x50, + 0x1B, 0xA7, 0x21, 0x7B, 0x11, 0x86, 0xCD, 0xF9, + 0x17, 0xAE, 0x4B, 0x88, 0x7B, 0xAF, 0xD1, 0xCA, + 0x80, 0xF7, 0xA1, 0x7F, 0x0A, 0x4D, 0xF8, 0x35, + 0x6E, 0xB1, 0x61, 0xEC, 0xAB, 0xD7, 0x60, 0x2D, + 0xB8, 0xB7, 0x59, 0x77, 0x90, 0xED, 0xC5, 0x24, + 0xCA, 0x02, 0xF2, 0x35, 0xD8, 0xB4, + + 0xE5, 0x07, 0x55, 0x53, 0x6C, 0x5E, 0xD2, 0x9F, + 0xE4, 0x23, 0xA5, 0x58, 0x45, 0x18, 0x2B, 0x0B, + 0x17, 0x00, 0xE8, 0xD7, 0x89, 0x06, 0xE6, 0x75, + 0x56, 0x25, 0xC0, 0x92, 0x79, 0xCD, 0xA2, 0xC0, + 0xBD, 0x8E, 0x52, 0x0D, 0xF3, 0xA2, 0xA1, 0x66, + 0x0F, 0x94, 0x23, 0xE7, 0x2D, 0x14, 0xB6, 0x82, + 0xB0, 0x9A, 0xE1, 0xE8, 0xF3, 0x15, 0x92, 0xFB, + 0xDE, 0x8D, 0xA2, 0x1A, 0xB3, 0xB2, 0xF7, 0xC0, + 0xAB, 0xA9, 0x79, 0x70, 0xBE, 0xE8, 0x14, 0x65, + 0x79, 0x10, 0x1C, 0x7C, 0x29, 0x2C, 0x16, 0x33, + 0xBB, 0xEF, 0x91, 0x5A, 0xC0, 0xA5, 0x7D, 0x97, + 0x79, 0x42, 0xB9, 0xA3, 0x7E, 0x9E, 0x87, 0xBB, + 0xDC, 0x65, 0x12, 0xDC, 0xD9, 0x2D, 0x73, 0x8A, + 0x73, 0x38, 0xF1, 0xA5, 0x60, 0x7E, 0xD1, 0xF3, + 0x97, 0xAB, 0xCF, 0xFF, 0x5C, 0x5E, 0xE1, 0xAC, + 0x1A, 0x41, 0xC2, 0x6D, 0xB1, 0x24, 0xAF, 0x19, + 0x55, 0x61, 0x4D, 0xDA, 0xD4, 0xE7, 0x10, 0x78, + 0x14, 0x2B, 0xA4, 0x88, 0x9D, 0x2F, 0x96, 0xF8, + 0x03, 0xF6, 0xB5, 0x13, 0x2B, 0xEA, 0x52, 0x24, + 0xB2, 0xA1, 0x46, 0xEC, 0x57, 0x59, 0x19, 0x17, + 0x83, 0x05, 0x20, 0x44, 0x4C, 0xEC, 0xC7, 0xE0, + 0xB1, 0x84, 0x00, 0x62, 0xE5, 0xE5, 0xAA, 0x0B, + 0xE6, 0x0E, 0x44, 0x5D, 0x55, 0x1D, 0x15, 0xDC, + 0xCE, 0x53, 0x2C, 0xF9, 0x7B, 0xDE, 0x95, 0x05, + 0xAF, 0x88, 0x59, 0xF1, 0xC2, 0x59, 0x3A, 0xFC, + 0x2E, 0x43, 0x37, 0x2F, 0xB8, 0x83, 0xD5, 0x42, + 0xE3, 0xA2, 0xFC, 0xA1, 0x62, 0xC6, 0x99, 0x9C, + 0x4C, 0x7A, 0x72, 0xD6, 0xF5, 0xF8, 0x3E, 0xE1, + 0xD0, 0x21, 0xE1, 0x74, 0x69, 0xB4, 0xE5, 0xB5, + 0x26, 0x2C, 0x37, 0x73, 0x90, 0xD3, 0x1D, 0x5B, + 0x08, 0xF4, 0x64, 0xE4, 0x9F, 0xA1, 0x95, 0xAE, + 0xCA, 0xB5, 0x6A, 0xD4, 0x50, 0x12, 0xDC, + + 0xED, 0xE8, 0x53, 0x43, 0xCA, 0x1B, 0xB7, 0x40, + 0x82, 0x4A, 0x01, 0x55, 0xD6, 0x0F, 0x9F, 0xFE, + 0x2F, 0x1B, 0x94, 0x9F, 0x49, 0xFA, 0x86, 0x45, + 0x3C, 0x63, 0x14, 0x2E, 0xAF, 0x66, 0x78, 0x97, + 0xA3, 0x40, 0x11, 0x32, 0x52, 0x3F, 0x22, 0x26, + 0xCA, 0xCC, 0x61, 0x3F, 0x31, 0x5E, 0x67, 0x5D, + 0x71, 0x7D, 0x5F, 0x69, 0xBF, 0xDC, 0x93, 0x1E, + 0xA8, 0x5F, 0xA2, 0x56, 0x91, 0xEC, 0xCE, 0x61, + 0x67, 0xF3, 0xDC, 0xE8, 0xF4, 0xF7, 0x46, 0xE9, + 0xE3, 0xB8, 0x21, 0x2A, 0x6E, 0xB9, 0xDD, 0x69, + 0x14, 0xFF, 0x1C, 0xC1, 0xF4, 0x27, 0x6B, 0x6E, + 0x2D, 0xC0, 0x75, 0x56, 0xF9, 0x75, 0x21, 0x3D, + 0x39, 0xE0, 0x67, 0x3C, 0xCC, 0x4D, 0x95, 0xDD, + 0xFE, 0x99, 0x1C, 0x77, 0xA6, 0xF2, 0x62, 0x23, + 0x79, 0x49, 0xAB, 0x65, 0x34, 0x2F, 0x71, 0x6F, + 0xCD, 0xA3, 0xBF, 0xF3, 0xAE, 0x08, 0xF8, 0xF3, + 0x18, 0x67, 0x51, 0x33, 0xD7, 0x3B, 0xF1, 0xD0, + 0x37, 0xA9, 0x07, 0x52, 0x77, 0xF0, 0x75, 0x0C, + 0xC6, 0x4A, 0xDC, 0x6B, 0x4C, 0xBF, 0x80, 0xEC, + 0x42, 0x89, 0x19, 0x22, 0xE1, 0x90, 0x2F, 0xC9, + 0x5E, 0x5D, 0xC8, 0xCF, 0xD3, 0x9F, 0x37, 0xAF, + 0x60, 0xAE, 0x34, 0x5F, 0xD8, 0x3F, 0xA9, 0x55, + 0x0E, 0x5C, 0xC9, 0xA8, 0xA2, 0x0E, 0xE3, 0xC2, + 0xAC, 0x9E, 0x87, 0x38, 0xBB, 0x53, 0x86, 0x82, + 0xDF, 0x05, 0xE4, 0x23, 0xF3, 0x77, 0xDA, 0x7A, + 0x1E, 0x9C, 0x88, 0x53, 0xB9, 0x14, 0x56, 0x49, + 0x2F, 0x6A, 0xE8, 0x3A, 0xF1, 0x5C, 0x72, 0xAF, + 0x2A, 0x19, 0x0A, 0xBB, 0x2B, 0xFD, 0x91, 0x59, + 0x43, 0x12, 0x9C, 0xC3, 0x0B, 0x7A, 0x7F, 0x85, + 0x1C, 0x98, 0x34, 0xF6, 0xDE, 0x59, 0x3F, 0x7C, + 0x49, 0x37, 0x75, 0x6B, 0xC1, 0xE1, 0xD7, 0x70, + 0xB9, 0xFB, 0xCA, 0xD8, 0x41, 0x6B, 0x8C, 0x01, + + 0xB9, 0xC3, 0x0F, 0x35, 0xEB, 0x2F, 0x12, 0x4E, + 0xE4, 0x78, 0x2D, 0x24, 0x77, 0xA4, 0x89, 0x1F, + 0x64, 0xAD, 0xE7, 0x2B, 0x49, 0xE0, 0xDC, 0x05, + 0x7D, 0x6C, 0xEC, 0x4E, 0x9E, 0xAE, 0x2E, 0xDF, + 0xE6, 0xF2, 0x3F, 0xC6, 0xD5, 0xAA, 0xFC, 0x23, + 0xAB, 0xCB, 0x96, 0x49, 0xC4, 0xEB, 0xAF, 0x18, + 0x14, 0x27, 0xFB, 0xE7, 0x1C, 0x34, 0xF7, 0x22, + 0x69, 0xA3, 0xD2, 0xFD, 0x9E, 0x9F, 0x29, 0xA9, + 0x50, 0x34, 0x3C, 0x4F, 0xDD, 0xE5, 0xAB, 0xE6, + 0xAC, 0x99, 0x80, 0x50, 0x7A, 0x4F, 0x79, 0xD2, + 0xC2, 0xC6, 0x31, 0x8F, 0xF6, 0xD6, 0xAA, 0x57, + 0x00, 0xA2, 0x4B, 0x61, 0xBA, 0xD6, 0x28, 0xD3, + 0xFA, 0x93, 0xE4, 0x65, 0x53, 0x81, 0xD3, 0x01, + 0xF1, 0xDA, 0x7A, 0x12, 0x85, 0x17, 0x5A, 0x7E, + 0x35, 0x21, 0x6C, 0xF0, 0x18, 0x34, 0xA4, 0xF4, + 0xDD, 0x24, 0xEB, 0x86, 0xDC, 0xD0, 0xF7, 0xAB, + 0x41, 0xB5, 0x59, 0x3F, 0x22, 0x7B, 0xCF, 0xDB, + 0x61, 0x4D, 0x61, 0x58, 0x55, 0xA5, 0x23, 0xCE, + 0x69, 0x2D, 0x5C, 0x24, 0x73, 0xA2, 0x80, 0x3F, + 0x67, 0xB7, 0x66, 0xD8, 0x30, 0x87, 0xFE, 0x74, + 0x23, 0x35, 0xFD, 0xD8, 0xA7, 0xD4, 0xAC, 0x02, + 0x40, 0xF6, 0xCA, 0x9B, 0xB3, 0xB4, 0x73, 0xEA, + 0x67, 0x39, 0xB4, 0x1B, 0xB4, 0x45, 0xFB, 0xB2, + 0xF9, 0xDB, 0x8E, 0x90, 0x29, 0x62, 0x9D, 0x9D, + 0x73, 0x30, 0x6C, 0x51, 0x56, 0xA6, 0x9B, 0x9A, + 0xE9, 0x33, 0xCA, 0xC8, 0xED, 0xE4, 0x58, 0x91, + 0x82, 0x17, 0x10, 0x0F, 0x2D, 0xF7, 0x82, 0xCD, + 0x25, 0x61, 0x45, 0xCD, 0xDD, 0x66, 0xF1, 0x8A, + 0xDC, 0x08, 0xF8, 0x53, 0xF6, 0x07, 0x8E, 0x3A, + 0x6F, 0x0B, 0xF0, 0x9D, 0x8F, 0xFE, 0xDD, 0xCF, + 0x94, 0xDD, 0xB2, 0x1F, 0x1A, 0xFF, 0x08, 0x22, + 0x3D, 0xEE, 0x45, 0x6B, 0xD2, 0x5A, 0xF3, 0x15, + 0x93, + + 0xCC, 0x0E, 0xCA, 0xDF, 0xEF, 0xC6, 0xC6, 0x35, + 0x66, 0x95, 0xFF, 0xCE, 0x3F, 0x13, 0x97, 0xEA, + 0xEC, 0xB9, 0xEB, 0x1F, 0x82, 0x4E, 0x6A, 0xFF, + 0xB0, 0x0A, 0x75, 0xAB, 0xBC, 0x6B, 0xF1, 0xC4, + 0xEE, 0xB8, 0x8C, 0xAE, 0x52, 0xED, 0x7C, 0x9E, + 0xF3, 0xE2, 0x74, 0xAD, 0x95, 0x65, 0x8F, 0x2D, + 0x1A, 0x93, 0xF5, 0x35, 0x5D, 0x5E, 0x7F, 0x5A, + 0x22, 0x9F, 0x48, 0x81, 0x25, 0xDE, 0xB1, 0xC3, + 0x0A, 0xD6, 0x7E, 0x9E, 0x77, 0xF0, 0x1F, 0x77, + 0xE0, 0x85, 0xBB, 0xFA, 0xC5, 0x87, 0xC7, 0x17, + 0x0A, 0x7B, 0x3D, 0x85, 0xD4, 0xA9, 0xCA, 0x98, + 0xCC, 0x08, 0xD5, 0x40, 0x58, 0x6A, 0x0D, 0xFE, + 0xBC, 0x4D, 0xBE, 0x46, 0x9E, 0xC0, 0x84, 0xF4, + 0x63, 0xE7, 0xEA, 0xFD, 0x7B, 0x73, 0x1D, 0xDB, + 0xE2, 0xD7, 0x51, 0x6D, 0xB2, 0x4F, 0x79, 0x37, + 0x9E, 0x37, 0x39, 0xE9, 0xD7, 0xCB, 0x7F, 0x56, + 0x5E, 0x68, 0xCC, 0x73, 0x7B, 0xBA, 0x3D, 0x76, + 0x0A, 0x41, 0x0B, 0x83, 0xC5, 0xAA, 0xCF, 0x7E, + 0xDA, 0x40, 0xB6, 0xCB, 0xC1, 0x76, 0xE5, 0x21, + 0xC3, 0xC8, 0xDB, 0x01, 0x2A, 0x87, 0xFF, 0x9E, + 0xC7, 0x58, 0xD5, 0xC8, 0x22, 0x37, 0x3F, 0x1F, + 0x49, 0xF0, 0x9F, 0x7C, 0x60, 0x91, 0x8C, 0xC5, + 0x51, 0xE3, 0x5F, 0x7A, 0x98, 0x0B, 0x3E, 0xE1, + 0xAA, 0xC8, 0xE4, 0x3C, 0xC5, 0x98, 0xC0, 0xE5, + 0x00, 0x5C, 0xEB, 0x57, 0xAF, 0xF2, 0x67, 0x30, + 0x11, 0xB0, 0xD8, 0x82, 0x8A, 0x5A, 0x97, 0xC8, + 0xFC, 0x09, 0xD7, 0x74, 0x98, 0xC9, 0x8D, 0xB5, + 0x1A, 0x96, 0x79, 0x47, 0xDE, 0x9B, 0x7C, 0xF3, + 0x86, 0x80, 0x87, 0x2C, 0x2F, 0xC6, 0xA6, 0x17, + 0x85, 0xF3, 0xD1, 0xEE, 0x33, 0x68, 0x7A, 0xCC, + 0x54, 0x4A, 0x08, 0x18, 0x8F, 0x20, 0x08, 0x33, + 0x43, 0x25, 0x05, 0x9B, 0x21, 0xB2, 0x49, 0xA5, + 0x33, 0xC0, + + 0x62, 0xD2, 0x23, 0x2D, 0x6D, 0xC4, 0x02, 0x15, + 0x9C, 0xC7, 0x37, 0x08, 0xEA, 0x73, 0x56, 0xB4, + 0x77, 0xEE, 0x14, 0xEC, 0x2B, 0x86, 0x51, 0xA0, + 0x63, 0x54, 0x00, 0xD4, 0xDE, 0x91, 0xE9, 0x94, + 0x3F, 0x32, 0xE6, 0x44, 0x49, 0xF7, 0xBD, 0x4C, + 0xFA, 0x07, 0xFB, 0xCB, 0x86, 0xFC, 0xBA, 0x1B, + 0x66, 0x52, 0xE3, 0x2C, 0x52, 0x5C, 0x07, 0xD7, + 0xFF, 0x08, 0x93, 0x73, 0x30, 0x4E, 0xB8, 0x97, + 0x8E, 0xB5, 0xEB, 0x24, 0xF7, 0x26, 0xB5, 0x21, + 0xEA, 0x1A, 0x62, 0x1B, 0xAC, 0x12, 0x83, 0xB1, + 0xB2, 0xAB, 0x1C, 0x1C, 0x69, 0x7D, 0x5C, 0x9D, + 0x4E, 0x20, 0x13, 0x52, 0x6B, 0x88, 0xB6, 0xBE, + 0x0E, 0x8D, 0x23, 0xD4, 0x14, 0xF5, 0x99, 0xEB, + 0x2E, 0xC8, 0x4D, 0x38, 0x57, 0x47, 0x70, 0x18, + 0x65, 0xB5, 0xB8, 0xB4, 0x29, 0x99, 0xA2, 0x4B, + 0xAA, 0x5A, 0xEC, 0x8B, 0xCA, 0x9F, 0xE9, 0x6D, + 0x8B, 0x0D, 0xEC, 0x0A, 0xCB, 0xD8, 0xF7, 0x0C, + 0x7B, 0x22, 0xB0, 0xE8, 0x3C, 0x4F, 0x0C, 0xEC, + 0x7E, 0x2D, 0x8F, 0x93, 0x2C, 0xC0, 0xA8, 0x40, + 0x48, 0xAA, 0xAF, 0x83, 0x08, 0xBF, 0xBF, 0x5C, + 0xBE, 0xB8, 0xAD, 0xC0, 0xCC, 0xDC, 0x7D, 0x00, + 0x2B, 0x2B, 0xC7, 0x74, 0xB0, 0x77, 0x95, 0x5F, + 0xE9, 0xA1, 0x65, 0x45, 0xB0, 0xBB, 0xEF, 0xD8, + 0xFA, 0x1D, 0x1F, 0x66, 0xEF, 0x3B, 0x68, 0x7B, + 0x67, 0x9F, 0xFF, 0xA4, 0xB5, 0x24, 0x69, 0xAB, + 0x84, 0xE3, 0x75, 0x69, 0x56, 0x72, 0x19, 0x0D, + 0x47, 0xA3, 0xF8, 0xAE, 0xA1, 0x4E, 0xB0, 0x4C, + 0xAA, 0x0F, 0xF9, 0x08, 0xEA, 0x8F, 0x69, 0xFC, + 0x60, 0xF4, 0x91, 0xFF, 0x08, 0xD5, 0xD4, 0xB4, + 0xD2, 0x4B, 0xBD, 0x4E, 0x3F, 0x53, 0x34, 0x06, + 0x4A, 0x26, 0x57, 0x0F, 0xF2, 0xDE, 0x9D, 0x02, + 0xA4, 0xE0, 0x0D, 0xD7, 0x2C, 0x5F, 0x5F, 0x05, + 0x4F, 0x60, 0x38, + + 0x0D, 0xEC, 0xE4, 0x86, 0xF5, 0x6D, 0xC1, 0x52, + 0x88, 0x55, 0xB9, 0x1D, 0x2D, 0xD7, 0x0A, 0xB4, + 0x5C, 0xB2, 0xDE, 0xF6, 0xBD, 0x91, 0x5D, 0x8D, + 0x0D, 0xB0, 0xD8, 0x54, 0x56, 0x62, 0xF1, 0x94, + 0xB5, 0xEE, 0x73, 0x23, 0xF2, 0x25, 0xF5, 0x7E, + 0x6A, 0xBF, 0x8A, 0xAA, 0x1C, 0x66, 0xCB, 0xA0, + 0xD6, 0xB2, 0xFC, 0x94, 0x6E, 0x26, 0x49, 0xF8, + 0xDE, 0xC0, 0xD8, 0x04, 0x93, 0x80, 0x16, 0x52, + 0xA6, 0xDE, 0x3F, 0x87, 0x4E, 0x2F, 0x93, 0xBD, + 0xAA, 0x8D, 0xA8, 0x86, 0x27, 0xAC, 0x6D, 0xA0, + 0x13, 0x81, 0xF6, 0xE7, 0x7D, 0xBC, 0xF1, 0x09, + 0x38, 0x09, 0x93, 0xC5, 0x94, 0x84, 0x0F, 0x4E, + 0x28, 0xCA, 0x29, 0x4D, 0xED, 0xDC, 0x91, 0xDC, + 0xEC, 0x16, 0xF7, 0x86, 0x4C, 0x0B, 0x2C, 0xDA, + 0xCE, 0x2B, 0x1B, 0xB4, 0xFC, 0x6F, 0xA5, 0x7C, + 0xE2, 0xC0, 0xAD, 0xC7, 0xA7, 0xE1, 0x28, 0x03, + 0xD5, 0xB3, 0x30, 0x51, 0xA2, 0x24, 0x2F, 0x9E, + 0x16, 0x48, 0x54, 0x6B, 0x36, 0x60, 0x22, 0x58, + 0x00, 0xFF, 0x40, 0x59, 0x17, 0x36, 0x1C, 0x2C, + 0x58, 0x0F, 0xC0, 0x5D, 0x41, 0x47, 0x7F, 0xE3, + 0xB1, 0x5F, 0x0A, 0x88, 0xBC, 0xBA, 0x83, 0xBF, + 0x3D, 0x0B, 0xDF, 0xFC, 0xD3, 0x55, 0xDF, 0x72, + 0x0D, 0x8A, 0xAF, 0x4D, 0xF4, 0x0C, 0x0F, 0x8C, + 0xE9, 0xEE, 0xA5, 0xA7, 0xFF, 0x87, 0x81, 0x2E, + 0x38, 0x04, 0x76, 0xAE, 0xA2, 0xE6, 0x84, 0x73, + 0xFF, 0xEC, 0xEE, 0xA3, 0x40, 0xA8, 0x67, 0x8C, + 0xD6, 0x68, 0x70, 0xA0, 0x6B, 0x3F, 0x0D, 0x45, + 0xE0, 0xA0, 0x29, 0x38, 0xF8, 0xD5, 0x58, 0x59, + 0xBD, 0x9B, 0x2B, 0xD3, 0x8B, 0x45, 0x3D, 0xDB, + 0x79, 0x0F, 0x25, 0xE7, 0xF9, 0xC7, 0x30, 0xC8, + 0x92, 0xC1, 0x99, 0xBE, 0xEC, 0xAF, 0x68, 0xDF, + 0x9F, 0xEC, 0x4C, 0x8C, 0xD8, 0x7A, 0xEF, 0x07, + 0x6E, 0x14, 0xF4, 0x41, + + 0x93, 0x45, 0x33, 0x61, 0xF0, 0xF2, 0x27, 0x76, + 0x4C, 0x53, 0x60, 0xAE, 0x83, 0xB0, 0x90, 0x70, + 0xDB, 0xAE, 0x2F, 0x1A, 0xCF, 0xF2, 0xC6, 0x50, + 0x36, 0x1B, 0x2E, 0x75, 0xF2, 0x4F, 0x1B, 0x9B, + 0x81, 0x2F, 0x49, 0x96, 0xED, 0x05, 0x69, 0x07, + 0x0C, 0x6B, 0x5A, 0x35, 0x11, 0x6B, 0x29, 0xC6, + 0x07, 0xF5, 0x68, 0xAF, 0x26, 0xE1, 0xEB, 0x71, + 0x53, 0x4A, 0xC0, 0x5A, 0x1B, 0xDD, 0x49, 0x28, + 0x90, 0xE3, 0xDB, 0x1F, 0x86, 0xB1, 0xAB, 0x4A, + 0x64, 0x39, 0x8F, 0x83, 0xA7, 0x37, 0x1B, 0xC9, + 0xB6, 0xEA, 0x5E, 0xA8, 0x25, 0x87, 0x78, 0x52, + 0xA0, 0xE5, 0xA3, 0x26, 0x74, 0x66, 0xCC, 0x0A, + 0x92, 0x7D, 0x9B, 0x4B, 0xAD, 0x79, 0x7F, 0x87, + 0x9E, 0x0A, 0xF9, 0xDE, 0xB0, 0x31, 0x2B, 0xDC, + 0xC8, 0x29, 0xD0, 0xAC, 0x3C, 0xD8, 0x60, 0x19, + 0xA5, 0xF6, 0x50, 0xC1, 0xBA, 0x0F, 0xE4, 0x4A, + 0x07, 0xE8, 0xA5, 0xB7, 0x6C, 0x88, 0xF1, 0xBE, + 0xAF, 0x2F, 0x40, 0x9B, 0xB3, 0x98, 0x28, 0x43, + 0x25, 0xBB, 0x23, 0x1C, 0xF3, 0x98, 0x19, 0x9E, + 0x9E, 0x3D, 0xC0, 0xFF, 0x05, 0xA8, 0x35, 0xCD, + 0x47, 0xB7, 0xB3, 0xEF, 0xC9, 0x38, 0x36, 0xE7, + 0xF9, 0x6F, 0x5B, 0x6E, 0x79, 0xCB, 0x08, 0x49, + 0x6F, 0x4C, 0x17, 0xC2, 0x91, 0x7E, 0x8B, 0x86, + 0x57, 0x30, 0x8F, 0x92, 0x94, 0x34, 0x10, 0x1B, + 0xE5, 0x8F, 0xCC, 0xAF, 0xD0, 0x87, 0xDA, 0x9A, + 0xF2, 0x09, 0x99, 0x6C, 0x8E, 0x50, 0x91, 0x7D, + 0x7C, 0x4D, 0x3E, 0x24, 0xFF, 0xD4, 0xC1, 0xAA, + 0xA7, 0x80, 0x89, 0xB5, 0x8E, 0x50, 0x7D, 0x49, + 0xD4, 0xA0, 0xA1, 0xFD, 0x34, 0xCA, 0xB8, 0xCB, + 0x64, 0x76, 0x07, 0x91, 0x39, 0x7D, 0xAE, 0xE2, + 0xD9, 0x5A, 0xCB, 0xB7, 0xAD, 0x9E, 0x8C, 0xE8, + 0x0A, 0xE8, 0xE9, 0xCC, 0x49, 0x97, 0xC3, 0xE9, + 0x96, 0x1A, 0xCE, 0xFF, 0x16, + + 0x3D, 0x76, 0xF1, 0x1D, 0x88, 0x56, 0x8E, 0x23, + 0x96, 0xE0, 0x55, 0xE9, 0xF3, 0x36, 0xCE, 0xEA, + 0x3C, 0x72, 0x34, 0x12, 0xA4, 0x19, 0x29, 0x74, + 0x26, 0xCA, 0xF9, 0xDB, 0xA4, 0x2F, 0x2E, 0xBD, + 0xD1, 0x64, 0x97, 0xE2, 0x7E, 0x30, 0x59, 0xDB, + 0xBB, 0x7D, 0x42, 0xD2, 0xC1, 0x84, 0x18, 0x1F, + 0x67, 0x61, 0x5D, 0x40, 0xE5, 0xD8, 0x83, 0x36, + 0x98, 0xAB, 0x2F, 0xE8, 0x24, 0x13, 0xC7, 0x61, + 0x0A, 0x71, 0x59, 0x9E, 0x51, 0xE1, 0xBB, 0xCE, + 0xEA, 0xE7, 0xFC, 0x7E, 0x92, 0xE0, 0x1F, 0xA5, + 0xE3, 0x89, 0x80, 0xBC, 0xA8, 0xCB, 0xDB, 0xFD, + 0x49, 0x8D, 0x7F, 0x01, 0x15, 0xA5, 0x35, 0x89, + 0x46, 0xB5, 0xBC, 0x8E, 0x96, 0x80, 0x11, 0xCA, + 0x6C, 0x56, 0x94, 0x1C, 0x0C, 0xEC, 0x56, 0x78, + 0xC9, 0x7E, 0x4A, 0x44, 0xE7, 0xDF, 0x03, 0xA9, + 0x32, 0xF5, 0xA2, 0x4C, 0x80, 0xBB, 0x31, 0x9E, + 0x00, 0x5D, 0xCD, 0xB2, 0x33, 0x41, 0xCA, 0xA4, + 0x8A, 0xB3, 0x52, 0x59, 0xD9, 0x19, 0xD4, 0x98, + 0x78, 0xFE, 0xB1, 0x02, 0x6D, 0x52, 0x6F, 0x23, + 0xC8, 0xF7, 0xA8, 0xC2, 0xB4, 0x90, 0x69, 0x68, + 0x5A, 0xA5, 0xDB, 0x26, 0x93, 0xF0, 0x78, 0x25, + 0xC3, 0x94, 0x1F, 0x0A, 0x0B, 0x0F, 0xA8, 0xC2, + 0xCB, 0x7B, 0x0D, 0xC9, 0x53, 0x1D, 0x5A, 0xB4, + 0x30, 0xF2, 0xDD, 0x15, 0x8D, 0xDE, 0x49, 0x82, + 0x94, 0x9B, 0x6E, 0x2D, 0x2D, 0xD7, 0x21, 0x38, + 0x98, 0x61, 0xEB, 0xF3, 0xDE, 0xB1, 0xCE, 0x54, + 0xB9, 0x7F, 0x9C, 0x4B, 0xF0, 0x8B, 0xD1, 0xBF, + 0x6A, 0xA4, 0x57, 0x28, 0x86, 0xE2, 0xB2, 0x74, + 0x31, 0x51, 0x9E, 0x30, 0xF5, 0x98, 0x3E, 0x9D, + 0x98, 0x21, 0x7C, 0xAD, 0x68, 0x44, 0x91, 0x5C, + 0x11, 0x5A, 0x42, 0x0C, 0x84, 0x69, 0x62, 0xD0, + 0x98, 0x3D, 0x89, 0x4D, 0xBC, 0x65, 0x38, 0x03, + 0x44, 0x76, 0xFF, 0x20, 0x3D, 0x28, + + 0xD4, 0xAE, 0xDA, 0xA8, 0x24, 0x19, 0x06, 0xFB, + 0xE9, 0xAD, 0x9F, 0x3C, 0x1D, 0xD6, 0x92, 0x5E, + 0x83, 0x58, 0x54, 0xBB, 0xCB, 0x7D, 0x8E, 0x24, + 0x0F, 0x0A, 0x43, 0xF5, 0x6D, 0x89, 0x99, 0x4A, + 0xB7, 0xDF, 0x8A, 0x84, 0x00, 0x1A, 0xF5, 0x6E, + 0x1C, 0xC2, 0xFA, 0x20, 0xE7, 0xC2, 0x41, 0x60, + 0x7C, 0xCE, 0x90, 0x8E, 0x16, 0x64, 0x94, 0xDA, + 0xE2, 0x3A, 0x8D, 0xDF, 0x21, 0x61, 0x17, 0x3C, + 0x72, 0xE3, 0x66, 0x35, 0x0B, 0xF6, 0x30, 0x5C, + 0x82, 0x5C, 0x48, 0xA1, 0xE8, 0x21, 0x9C, 0x92, + 0xA1, 0x0F, 0xBF, 0x89, 0xC8, 0x7A, 0xEE, 0x18, + 0x87, 0x5B, 0x51, 0xF3, 0xF7, 0x0A, 0xA3, 0xB4, + 0x2E, 0xDA, 0x4E, 0xAA, 0xAD, 0x6E, 0x70, 0x04, + 0xF8, 0x47, 0x1E, 0x5F, 0x43, 0x69, 0x0B, 0x26, + 0x4C, 0xBC, 0xDB, 0x46, 0x77, 0x87, 0xA1, 0x9A, + 0x04, 0xEF, 0x74, 0x99, 0xEA, 0x29, 0x71, 0x55, + 0x9C, 0x2E, 0x47, 0xE3, 0x4A, 0x15, 0xA4, 0xF6, + 0xED, 0x5D, 0xBE, 0x4F, 0x94, 0x3F, 0xFD, 0xD3, + 0x6E, 0x0E, 0xD9, 0x26, 0x68, 0x9F, 0xC2, 0x8C, + 0xA5, 0xA8, 0xF0, 0xFB, 0x53, 0x5F, 0x18, 0xCA, + 0xDB, 0xBD, 0x98, 0x96, 0x28, 0xFC, 0xE9, 0x2E, + 0x81, 0xE1, 0x0E, 0x3A, 0x77, 0x18, 0x25, 0x9D, + 0xB7, 0x67, 0x8C, 0x35, 0x68, 0x4A, 0x88, 0x18, + 0x45, 0x6C, 0xE5, 0xCC, 0x22, 0xD2, 0x77, 0x9A, + 0x68, 0x0E, 0xC5, 0xA6, 0x1D, 0xAD, 0xDB, 0xB5, + 0xFF, 0xEA, 0x7C, 0x7D, 0x19, 0x1B, 0x9D, 0xB9, + 0xD0, 0xA7, 0xAB, 0x4D, 0x4A, 0x7D, 0x1A, 0xA5, + 0x09, 0x54, 0x84, 0x5C, 0x14, 0x4E, 0xA6, 0x14, + 0x6B, 0x22, 0x46, 0x7E, 0x5E, 0xB9, 0x33, 0x97, + 0xB5, 0x63, 0x5C, 0xCE, 0xF4, 0xD0, 0xD8, 0x8C, + 0xCC, 0xE7, 0x67, 0x01, 0x1B, 0x56, 0x68, 0x54, + 0x56, 0x4C, 0x19, 0x1D, 0x05, 0xF8, 0x55, 0xD6, + 0x45, 0xC0, 0xAD, 0xD6, 0xEE, 0xD9, 0x03, + + 0x7F, 0x07, 0xFA, 0x8C, 0x77, 0xF1, 0xDA, 0x4E, + 0x4B, 0x65, 0x55, 0xA4, 0x12, 0xE8, 0x7F, 0x04, + 0x0A, 0xEC, 0x57, 0xFF, 0x30, 0xD1, 0xE2, 0x08, + 0x71, 0xEC, 0x5F, 0x72, 0x88, 0x37, 0xC1, 0x33, + 0x79, 0xC8, 0x49, 0x47, 0x11, 0xC9, 0x8C, 0xD3, + 0x3B, 0x4F, 0x2C, 0x5A, 0xDD, 0x8C, 0x00, 0x8B, + 0xF6, 0xC4, 0x07, 0x7E, 0x2C, 0xFA, 0x3A, 0xBF, + 0x84, 0xBA, 0x13, 0x31, 0x20, 0x69, 0xAF, 0xBD, + 0x61, 0xB0, 0xFF, 0x28, 0x40, 0x1B, 0x5F, 0x1D, + 0x87, 0x99, 0x23, 0x95, 0x77, 0x88, 0x3F, 0xBB, + 0xFA, 0x55, 0xB5, 0xB2, 0xF7, 0xF3, 0xA6, 0x0B, + 0x57, 0x71, 0xF5, 0x7A, 0xE7, 0x23, 0x61, 0x54, + 0x13, 0xCB, 0x71, 0x97, 0xDE, 0xFF, 0x0E, 0x23, + 0x5C, 0xD4, 0xCC, 0xB1, 0xB8, 0x2F, 0xA1, 0xA5, + 0x15, 0xEB, 0xB4, 0x47, 0xA7, 0x7C, 0xEA, 0xD1, + 0x23, 0xE7, 0xFE, 0x9D, 0x56, 0xC5, 0x73, 0x9D, + 0xE8, 0x90, 0x4E, 0x00, 0xE5, 0x7F, 0x6E, 0x36, + 0x6D, 0xEA, 0x8C, 0xBA, 0xC6, 0x9C, 0xA0, 0x27, + 0x13, 0x90, 0x35, 0xE7, 0xD5, 0x27, 0x56, 0x50, + 0x70, 0x64, 0x03, 0xD2, 0x43, 0xDC, 0x1F, 0x60, + 0x4B, 0x5D, 0x48, 0xF1, 0x82, 0xE6, 0x9A, 0xDE, + 0xC7, 0xDB, 0x2F, 0x0D, 0xD9, 0xF4, 0xAD, 0xD0, + 0xB9, 0xDC, 0x01, 0x09, 0x74, 0x4F, 0x29, 0x71, + 0x61, 0x6D, 0xE2, 0xAC, 0xE3, 0x60, 0x9C, 0x4D, + 0x9A, 0x26, 0xC9, 0x3D, 0x11, 0x04, 0x83, 0x9F, + 0x39, 0x39, 0x0E, 0x44, 0xB2, 0xD3, 0x7A, 0x0D, + 0xD7, 0x0B, 0xDB, 0x14, 0x4A, 0x46, 0xA7, 0xB5, + 0x34, 0x2C, 0x22, 0x98, 0xBA, 0x6A, 0xBA, 0x4A, + 0xDA, 0x30, 0x8C, 0xE7, 0xDF, 0xD8, 0x73, 0x83, + 0x8C, 0xB6, 0x7A, 0x73, 0x74, 0xE5, 0x04, 0x32, + 0x66, 0x24, 0xC9, 0x45, 0xDA, 0x86, 0xE2, 0xCD, + 0xF9, 0xDB, 0x2F, 0x1D, 0xBA, 0x2C, 0xC0, 0x91, + 0x06, 0xF7, 0x44, 0xE8, 0x30, 0x34, 0x94, 0x91, + + 0xD5, 0x3B, 0xA6, 0x5A, 0x56, 0xB4, 0xDD, 0xBC, + 0xD8, 0xE7, 0x85, 0x2B, 0x3E, 0xFC, 0xBC, 0xD1, + 0xB9, 0x68, 0x09, 0xC3, 0xA5, 0xC1, 0x1D, 0x5C, + 0x4C, 0x11, 0xAC, 0x4A, 0xA3, 0x96, 0xAB, 0xD2, + 0xFD, 0x33, 0xD7, 0x69, 0xD1, 0x95, 0x50, 0xE6, + 0x4D, 0x3A, 0xE9, 0x9D, 0x67, 0x68, 0x16, 0x01, + 0xE7, 0x8A, 0xE3, 0x90, 0xC8, 0x4D, 0xD4, 0xDF, + 0xB0, 0xA1, 0x90, 0x14, 0xC2, 0x32, 0x9A, 0xC2, + 0x86, 0xA2, 0x53, 0x72, 0x1A, 0x9A, 0xFE, 0x8E, + 0xB1, 0x4C, 0xCD, 0x9F, 0xFF, 0x3F, 0xA6, 0x6A, + 0x8E, 0xEB, 0x5E, 0x7C, 0x9D, 0xF3, 0x4B, 0x3B, + 0x09, 0xC7, 0x68, 0x71, 0xD5, 0xBA, 0x32, 0x2D, + 0xBA, 0x15, 0xE3, 0x63, 0xD7, 0x5A, 0x87, 0x5E, + 0x9E, 0xAF, 0xB0, 0x9C, 0xF9, 0x35, 0xA3, 0x67, + 0x1B, 0xD5, 0xC7, 0x9C, 0xB5, 0x63, 0x0E, 0x16, + 0x9B, 0x2D, 0x25, 0x1E, 0x22, 0x96, 0x0A, 0x96, + 0xBE, 0xEF, 0x65, 0xAA, 0xFE, 0x28, 0xB9, 0x55, + 0xD0, 0xDC, 0xB2, 0x5C, 0x21, 0x9B, 0x31, 0xF7, + 0xDE, 0xD9, 0xA6, 0x86, 0x3D, 0xD8, 0x5F, 0xB4, + 0xC5, 0x7A, 0x4D, 0xAB, 0xE5, 0xBC, 0x10, 0xB7, + 0x26, 0x15, 0x08, 0x64, 0x58, 0xB0, 0x4F, 0x77, + 0xCE, 0xDC, 0xBD, 0x1D, 0xD5, 0x21, 0x9A, 0x9A, + 0x93, 0x72, 0x2D, 0x4D, 0x82, 0x36, 0x29, 0xCA, + 0xAB, 0x80, 0xD2, 0x02, 0x1D, 0x6C, 0x49, 0x30, + 0x56, 0x20, 0x8D, 0xFA, 0xA1, 0x58, 0x3F, 0xA6, + 0xCB, 0xF0, 0x41, 0xFB, 0xDD, 0xD0, 0x6D, 0x80, + 0x75, 0xE1, 0x36, 0xAC, 0xF8, 0xAE, 0xDB, 0x37, + 0xB0, 0xDE, 0x4B, 0x89, 0xFB, 0xC1, 0x1B, 0x23, + 0x05, 0xBC, 0x02, 0x87, 0x42, 0x9A, 0x77, 0x23, + 0x99, 0xB4, 0x56, 0x7E, 0x7C, 0xD7, 0x61, 0x18, + 0xD2, 0x4F, 0x19, 0xE6, 0x05, 0x4D, 0x72, 0xC8, + 0xB7, 0xA9, 0x9F, 0xF9, 0x9F, 0xE9, 0x54, 0x34, + 0x54, 0x45, 0x7C, 0xA4, 0xCC, 0x3B, 0xFF, 0x2B, + 0x58, + + 0xB7, 0x93, 0x32, 0xE4, 0xCA, 0xD7, 0xB5, 0x4F, + 0x11, 0xB0, 0x5B, 0x38, 0xB8, 0xC0, 0x90, 0xA8, + 0x0E, 0x4B, 0x34, 0x6C, 0xCC, 0x81, 0x82, 0xC5, + 0x41, 0x91, 0x0D, 0x1A, 0x47, 0x0E, 0xE6, 0x39, + 0x7F, 0xDD, 0xF9, 0xBE, 0xF1, 0xB0, 0xDC, 0x76, + 0xB8, 0x2C, 0x2B, 0xAD, 0xA9, 0xE5, 0xCA, 0x70, + 0x8D, 0xF1, 0xCA, 0x60, 0x5C, 0x85, 0x2E, 0x73, + 0xE7, 0xF8, 0x0A, 0x33, 0xB3, 0x32, 0xC0, 0xCD, + 0x64, 0x4C, 0x9C, 0x65, 0x55, 0x2A, 0x60, 0x42, + 0xF3, 0xBA, 0xF5, 0xC2, 0x28, 0xFC, 0xFE, 0x32, + 0x96, 0xBF, 0x6F, 0x7E, 0x09, 0x82, 0xA5, 0xD0, + 0xE4, 0x11, 0x66, 0x67, 0x66, 0xDB, 0x8A, 0x3D, + 0x08, 0x40, 0xC4, 0x40, 0x66, 0xF8, 0x1F, 0x86, + 0x39, 0xDB, 0xB6, 0x12, 0x11, 0x4E, 0xE4, 0x52, + 0xA5, 0x18, 0x4A, 0x3F, 0x04, 0x01, 0x3D, 0x8D, + 0x15, 0xEE, 0x28, 0xBD, 0xED, 0x32, 0x11, 0xB8, + 0xE9, 0x5E, 0x2A, 0xB7, 0xD3, 0x3C, 0xDD, 0xC6, + 0x42, 0xE0, 0x15, 0x20, 0x0B, 0x13, 0x7E, 0x21, + 0xFE, 0x7B, 0x5F, 0x46, 0x70, 0xF6, 0x64, 0x73, + 0x71, 0x1A, 0x29, 0xD9, 0x07, 0xE0, 0xA9, 0x65, + 0x5B, 0x90, 0x17, 0x5E, 0xE6, 0xFC, 0x29, 0xA2, + 0x5B, 0xB0, 0x00, 0x54, 0x6C, 0x15, 0xA1, 0x44, + 0x66, 0x0F, 0x87, 0xCF, 0x66, 0x04, 0x6F, 0x3F, + 0x04, 0xE5, 0xFA, 0x2D, 0x0C, 0x70, 0x43, 0xF0, + 0x08, 0x01, 0x62, 0x21, 0x81, 0x65, 0x9F, 0x61, + 0xC6, 0x5A, 0x80, 0x64, 0x9D, 0x8E, 0xD7, 0x52, + 0xCF, 0xDC, 0x04, 0x50, 0x0A, 0x98, 0x5A, 0xF2, + 0x9F, 0x6A, 0xAF, 0xDA, 0x96, 0x35, 0x34, 0xF9, + 0xAB, 0x1E, 0x17, 0xC3, 0x18, 0x90, 0xFB, 0x07, + 0x5E, 0xA0, 0x02, 0x4C, 0x0C, 0xE1, 0xE9, 0x06, + 0x7F, 0x1F, 0xC1, 0xA2, 0x77, 0x63, 0xF0, 0x96, + 0xEE, 0x87, 0xC8, 0x28, 0x79, 0x17, 0x33, 0x2D, + 0x16, 0x43, 0x9A, 0x79, 0x32, 0xA9, 0x3D, 0x1F, + 0x35, 0x3D, + + 0xD1, 0x6C, 0xFF, 0x29, 0x4F, 0xC3, 0x29, 0xC5, + 0xC9, 0x5B, 0xDC, 0x81, 0xD1, 0xB3, 0x55, 0xA8, + 0x5E, 0xE2, 0xC9, 0xE3, 0xFC, 0x16, 0x30, 0x02, + 0xAB, 0x43, 0x26, 0x7E, 0xD1, 0xCD, 0x2B, 0x06, + 0xE8, 0x4E, 0xDA, 0xA3, 0xD3, 0xD3, 0x87, 0x05, + 0xB3, 0x05, 0x8C, 0xCF, 0xAE, 0xEF, 0x15, 0x33, + 0xCE, 0x72, 0x64, 0x52, 0x88, 0x78, 0x20, 0x1B, + 0xF1, 0x7E, 0xC9, 0x46, 0x13, 0xCF, 0xD9, 0xF2, + 0x4B, 0x1C, 0xA6, 0x96, 0xAF, 0x02, 0xAD, 0x6F, + 0xDB, 0xAD, 0x6F, 0x04, 0x55, 0xA2, 0x2C, 0x9C, + 0x56, 0xB6, 0x07, 0x8A, 0x87, 0x3F, 0xD6, 0x06, + 0x90, 0x4C, 0x6D, 0x5E, 0xE5, 0x16, 0x21, 0x46, + 0x00, 0x88, 0x77, 0x58, 0x0E, 0x9E, 0x89, 0xFE, + 0x3F, 0x20, 0x6A, 0x17, 0x64, 0x9C, 0x63, 0x71, + 0x3C, 0xDB, 0x36, 0xE3, 0x8A, 0xA6, 0x7A, 0xD5, + 0x1A, 0x27, 0x75, 0x4C, 0xD9, 0xF3, 0xF0, 0x02, + 0x2B, 0x3D, 0xE5, 0xF1, 0xDE, 0x98, 0x98, 0x62, + 0x18, 0x17, 0xAE, 0xD2, 0x80, 0xA7, 0xA3, 0x82, + 0xBF, 0x30, 0xC8, 0xC9, 0x68, 0x6A, 0x2F, 0x40, + 0x8A, 0x2F, 0x8A, 0x39, 0xB4, 0x60, 0x6F, 0xE1, + 0x0F, 0x19, 0xB1, 0x4C, 0x82, 0x9A, 0x11, 0xB4, + 0x98, 0x74, 0xE7, 0xAF, 0xD4, 0x21, 0x5E, 0xD8, + 0x30, 0xA1, 0x53, 0xF3, 0xD2, 0xDE, 0x02, 0x9F, + 0xA6, 0xA8, 0x4D, 0x03, 0x2A, 0x45, 0xE3, 0x49, + 0x98, 0xE2, 0xF7, 0x71, 0x9F, 0xBC, 0x93, 0xDE, + 0xF6, 0x7E, 0x12, 0xC1, 0x37, 0xB0, 0x93, 0x4E, + 0x89, 0x16, 0x39, 0x2B, 0xF6, 0x28, 0x30, 0x75, + 0xC0, 0x43, 0xA9, 0x3D, 0x1F, 0xD3, 0xFD, 0x56, + 0xF7, 0x65, 0xFC, 0x08, 0x3C, 0x9B, 0x1C, 0x33, + 0x57, 0x0B, 0xAB, 0x4B, 0xFE, 0xD4, 0x50, 0xFC, + 0x9D, 0x42, 0xF7, 0x8D, 0xB4, 0x50, 0xF5, 0xA9, + 0x54, 0x00, 0x74, 0x93, 0x59, 0x6B, 0x6F, 0x95, + 0x31, 0xEE, 0xF5, 0xC4, 0x4A, 0x9C, 0x33, 0x27, + 0x6E, 0x1D, 0x4A, + + 0xB5, 0xEE, 0x2C, 0xC7, 0x88, 0xD2, 0x63, 0x3E, + 0xF6, 0x8E, 0xC1, 0xA3, 0xD2, 0x39, 0x8E, 0xF6, + 0xD3, 0x44, 0x7C, 0xA6, 0x27, 0x6E, 0x67, 0x66, + 0x0F, 0x68, 0xF9, 0xE2, 0xE4, 0xA2, 0x9B, 0x59, + 0x6D, 0x26, 0xE2, 0x4B, 0xF1, 0x82, 0x5C, 0x67, + 0xE9, 0x55, 0xEE, 0xB6, 0xA0, 0xE3, 0x32, 0xEE, + 0x2A, 0x33, 0x2F, 0x6A, 0x2B, 0xBC, 0xE8, 0xB9, + 0x45, 0xCD, 0x0F, 0xF0, 0x84, 0xB6, 0x5D, 0xC5, + 0xAC, 0xD7, 0x45, 0x4B, 0x5A, 0xE4, 0xD5, 0x07, + 0x6D, 0x30, 0x0A, 0x9E, 0x73, 0x0E, 0x7D, 0x5F, + 0x8A, 0x2C, 0x7A, 0xE4, 0xD0, 0x1B, 0xE7, 0x02, + 0x93, 0x2E, 0xCE, 0x3C, 0xA1, 0xB1, 0x48, 0x5A, + 0xD6, 0xC0, 0x38, 0x0F, 0x13, 0xC7, 0x8B, 0x0F, + 0x4D, 0x90, 0xFC, 0x22, 0xC5, 0x74, 0xC6, 0xE0, + 0xFE, 0x1B, 0x2D, 0xEA, 0x3E, 0xDB, 0xC1, 0xCA, + 0x7F, 0x0D, 0xA0, 0xBB, 0x62, 0x0B, 0x51, 0x46, + 0xD4, 0xAB, 0x21, 0xE9, 0x6A, 0x13, 0x55, 0xEC, + 0x00, 0x16, 0xF5, 0xA3, 0xE8, 0x59, 0x90, 0xFB, + 0x56, 0xA5, 0x4B, 0x7F, 0x3F, 0xAA, 0x2E, 0x27, + 0x6F, 0xEC, 0x7B, 0xD1, 0xFA, 0x79, 0x36, 0x95, + 0x17, 0x84, 0xE0, 0xF1, 0xFB, 0xCA, 0xE2, 0x71, + 0x18, 0xAF, 0x49, 0x4D, 0x02, 0x51, 0xAB, 0x0C, + 0x56, 0x0A, 0xAF, 0x96, 0x0A, 0xD3, 0x57, 0x93, + 0xA1, 0x8E, 0xD1, 0x32, 0xD4, 0x1D, 0x62, 0xE1, + 0x1F, 0x4D, 0x00, 0xD4, 0x0B, 0x6F, 0xAE, 0x69, + 0x67, 0xAA, 0x2D, 0x75, 0x17, 0x0E, 0xE8, 0x72, + 0x91, 0x11, 0x05, 0xBB, 0x94, 0xBE, 0x0B, 0x2C, + 0x05, 0xF3, 0x09, 0x98, 0xAA, 0xA3, 0x77, 0xB9, + 0xAF, 0x75, 0x29, 0x57, 0xA0, 0x7F, 0x70, 0x53, + 0x4E, 0xEB, 0xD5, 0x15, 0x50, 0x60, 0x1D, 0x7B, + 0x3F, 0x3F, 0x65, 0x4E, 0xE0, 0x4A, 0x47, 0xEF, + 0xBA, 0x68, 0x01, 0xEB, 0xA5, 0x09, 0x4C, 0xE4, + 0xF6, 0xAA, 0xE0, 0x00, 0x43, 0x5B, 0x2E, 0x09, + 0xFE, 0xD0, 0x4E, 0xE8, + + 0x06, 0x49, 0x5B, 0x9A, 0xCF, 0x47, 0x2B, 0x04, + 0xBE, 0x1B, 0x98, 0x6A, 0xB5, 0x21, 0x6D, 0x0E, + 0x18, 0x9B, 0x87, 0xA4, 0x97, 0x13, 0xF5, 0x7E, + 0x94, 0x92, 0xB3, 0x69, 0xA1, 0xD5, 0x25, 0x46, + 0x26, 0x8E, 0x6B, 0x47, 0x0A, 0x77, 0x48, 0x51, + 0xB9, 0xAF, 0x64, 0x37, 0x70, 0x64, 0xBD, 0x98, + 0x16, 0x1A, 0x46, 0xAA, 0xFA, 0x0C, 0xB2, 0x3C, + 0x6F, 0x23, 0xA5, 0x6C, 0x81, 0x0B, 0x49, 0x7C, + 0x75, 0xE6, 0x0B, 0x47, 0x49, 0xDB, 0x70, 0xC5, + 0x26, 0x75, 0x20, 0x3E, 0xE2, 0x28, 0xD9, 0x1A, + 0xE2, 0x96, 0x9D, 0xAE, 0x29, 0x53, 0x9A, 0xE2, + 0xFE, 0xC9, 0xF5, 0x3B, 0x02, 0x40, 0x13, 0xF1, + 0x10, 0x27, 0x1A, 0x67, 0x97, 0x9C, 0xF6, 0xC5, + 0xBA, 0xE6, 0x3D, 0xA4, 0xA8, 0x67, 0xCF, 0xB6, + 0x35, 0xC7, 0x6B, 0xC6, 0x9C, 0x85, 0x25, 0x14, + 0x20, 0xC6, 0x48, 0x23, 0x26, 0x29, 0xE9, 0xBE, + 0x79, 0x89, 0xFA, 0x26, 0x2E, 0xB9, 0xC3, 0x54, + 0x28, 0x8B, 0xE2, 0xEF, 0xBC, 0xB2, 0x19, 0xC3, + 0x40, 0x85, 0x8A, 0x59, 0x06, 0x96, 0xDF, 0x39, + 0x03, 0x6E, 0xA0, 0xF8, 0x20, 0xD9, 0x3B, 0x72, + 0x80, 0xCC, 0x15, 0xBF, 0xB6, 0x2B, 0x15, 0x8A, + 0xC2, 0x71, 0x60, 0xEE, 0x51, 0x80, 0xCB, 0x61, + 0x7A, 0xDB, 0x17, 0x18, 0x80, 0xE5, 0x2F, 0xB9, + 0xB9, 0x36, 0xE3, 0xED, 0x7B, 0x7C, 0x23, 0xF7, + 0xBB, 0xBE, 0x61, 0xF1, 0xAF, 0x38, 0x5E, 0xAC, + 0x6E, 0x8F, 0x5A, 0x46, 0xB9, 0xD6, 0x4D, 0x51, + 0x5A, 0x7F, 0x3C, 0x34, 0xE5, 0x5E, 0x28, 0xB4, + 0x00, 0xD5, 0x76, 0xF6, 0x04, 0x94, 0x00, 0x36, + 0x23, 0xD1, 0x87, 0xA7, 0x0D, 0x55, 0xF4, 0x8A, + 0x89, 0x3A, 0x32, 0x9D, 0x08, 0xEB, 0xB6, 0x6F, + 0xED, 0xF0, 0xF8, 0x8F, 0x25, 0x3B, 0xAC, 0x6E, + 0xF5, 0x2B, 0x37, 0x4E, 0x4C, 0x28, 0x19, 0x69, + 0x66, 0x04, 0x40, 0x09, 0xE4, 0x1F, 0x82, 0x6F, + 0x5E, 0x40, 0x4C, 0x16, 0x4B, + + 0xBF, 0x12, 0xC4, 0xD2, 0xDD, 0xF1, 0x90, 0x50, + 0x38, 0x11, 0x8E, 0x5D, 0x20, 0x49, 0x64, 0xEB, + 0x08, 0xE0, 0x5F, 0xFF, 0x81, 0xC1, 0x23, 0x7F, + 0xA0, 0xBB, 0xB4, 0x28, 0x61, 0xA2, 0x99, 0xEB, + 0x44, 0x5A, 0x26, 0xF3, 0xF1, 0x76, 0x41, 0xF7, + 0x92, 0xFD, 0x6A, 0xB1, 0x72, 0x92, 0xBF, 0x62, + 0x92, 0x57, 0x46, 0x45, 0x0B, 0x5E, 0xA3, 0x33, + 0x64, 0x79, 0xE9, 0x3B, 0x50, 0x54, 0x62, 0xC1, + 0xB8, 0x5E, 0x45, 0x5A, 0x5F, 0xB4, 0xBB, 0x5F, + 0x6A, 0x01, 0x8D, 0x1B, 0x79, 0x76, 0x34, 0x08, + 0xF9, 0x42, 0xE4, 0xD6, 0x42, 0xDD, 0x16, 0x80, + 0xD4, 0x7D, 0x7D, 0xC4, 0x09, 0xAC, 0x96, 0xF8, + 0xB5, 0x8A, 0xF7, 0xBF, 0x92, 0xDB, 0x5D, 0xB7, + 0x15, 0x91, 0x6F, 0x96, 0xB5, 0x4C, 0x3E, 0x27, + 0xD8, 0xE2, 0xE1, 0x6F, 0x9D, 0x9B, 0xBB, 0x99, + 0x5D, 0xB5, 0x33, 0x55, 0xB3, 0x12, 0xFF, 0x64, + 0xBF, 0xB7, 0x58, 0xA0, 0x40, 0x9B, 0x6E, 0x35, + 0x26, 0x88, 0x13, 0x0B, 0x82, 0xA7, 0x2F, 0x10, + 0x70, 0x2B, 0xAF, 0x24, 0x1F, 0xAA, 0x62, 0xD2, + 0xBA, 0x0F, 0x3A, 0x29, 0x88, 0x48, 0xCB, 0xC9, + 0x60, 0x20, 0x73, 0x07, 0x46, 0x4D, 0x62, 0x2B, + 0x52, 0x2D, 0x39, 0xCB, 0x79, 0x63, 0x29, 0x54, + 0xA7, 0x4E, 0x3A, 0xD7, 0xD4, 0x51, 0x2D, 0xDD, + 0xBF, 0x6C, 0x16, 0x4E, 0xA7, 0x49, 0xEA, 0xE2, + 0x78, 0xBA, 0x84, 0x89, 0x7D, 0x5F, 0xA8, 0xFD, + 0x89, 0x2F, 0xA0, 0xE0, 0x1E, 0xE5, 0x51, 0x8D, + 0xE9, 0xE1, 0x73, 0x79, 0xE5, 0x28, 0x62, 0xFB, + 0x15, 0xD3, 0x44, 0xAF, 0xED, 0xCE, 0x1F, 0xB6, + 0x2D, 0x26, 0x5D, 0xDA, 0xED, 0x01, 0x30, 0x4D, + 0x72, 0x4F, 0x23, 0xF0, 0x5A, 0x1E, 0xC9, 0xAF, + 0xCF, 0x75, 0xA3, 0x15, 0xAE, 0x29, 0xF6, 0xD7, + 0xE4, 0xA9, 0x95, 0xF4, 0x39, 0x3E, 0xF6, 0x99, + 0xB5, 0xB3, 0x60, 0xDE, 0x7C, 0x26, 0xDD, 0xF0, + 0xD3, 0x04, 0x0A, 0xFC, 0xE9, 0xAE, + + 0xB0, 0x1B, 0x1C, 0xC2, 0xC4, 0x72, 0x71, 0xBF, + 0x61, 0x44, 0xD2, 0x9C, 0x4B, 0x14, 0x90, 0x14, + 0xC5, 0x89, 0x3D, 0xC7, 0x32, 0x07, 0xA7, 0xF5, + 0x64, 0xC1, 0xDD, 0x7D, 0x42, 0x2C, 0x3D, 0xDE, + 0x61, 0xDD, 0x78, 0x94, 0xB6, 0xB5, 0xF8, 0x47, + 0xEE, 0x5C, 0x2A, 0xA3, 0x32, 0xB5, 0x63, 0x3E, + 0x8C, 0xE6, 0x2F, 0x56, 0x1A, 0x24, 0xAA, 0xBD, + 0xA6, 0xAF, 0x30, 0x90, 0x7D, 0xF2, 0x73, 0x86, + 0x4D, 0x93, 0x74, 0xF1, 0xD8, 0x9B, 0x70, 0xDC, + 0xA5, 0xCC, 0xA8, 0xF9, 0x27, 0xBA, 0xA0, 0x3D, + 0x4D, 0x11, 0x19, 0xA5, 0x23, 0xE0, 0xC1, 0xC2, + 0xF7, 0x18, 0xDA, 0xCA, 0x23, 0x7E, 0x91, 0x56, + 0x72, 0xF7, 0xCE, 0xB4, 0xBF, 0x31, 0x2A, 0x61, + 0x9B, 0x7D, 0x82, 0xDF, 0xF2, 0xC7, 0x04, 0x74, + 0xD4, 0x2F, 0x14, 0x02, 0x4C, 0xC9, 0x12, 0x6D, + 0xF9, 0x67, 0x9C, 0xC5, 0x97, 0x3B, 0x9C, 0x54, + 0xA4, 0x45, 0xDA, 0xA5, 0xE8, 0xDB, 0xF7, 0x95, + 0x62, 0x0E, 0xE3, 0x25, 0x95, 0xC6, 0x68, 0xA1, + 0xBD, 0xC7, 0x78, 0xBD, 0x7E, 0xF8, 0x98, 0xDE, + 0xDA, 0x30, 0x75, 0xB6, 0xD0, 0xE1, 0x67, 0x42, + 0x6D, 0xE0, 0x26, 0x34, 0x7B, 0x47, 0x81, 0x03, + 0x96, 0x3C, 0x55, 0x8A, 0x6E, 0x73, 0x51, 0x88, + 0x1B, 0x05, 0x52, 0xDC, 0x8A, 0xA4, 0x07, 0x50, + 0x3C, 0x98, 0xC3, 0xFC, 0x58, 0xFA, 0xC8, 0x94, + 0x7D, 0x75, 0xE3, 0x4B, 0x6C, 0xCC, 0xB7, 0x9D, + 0x6F, 0xEA, 0x79, 0x4F, 0x5C, 0x77, 0xD3, 0x1B, + 0xC5, 0xB7, 0x97, 0xD7, 0xD4, 0xE9, 0x72, 0xD5, + 0x7E, 0xA3, 0xB5, 0xE9, 0xAC, 0xA0, 0x5D, 0xF8, + 0x38, 0xAC, 0x50, 0xA2, 0x47, 0x51, 0x96, 0x15, + 0x73, 0x74, 0x1B, 0x4E, 0xF2, 0x80, 0x59, 0xC6, + 0x58, 0x40, 0x3F, 0xB3, 0xE5, 0x4C, 0x62, 0x1F, + 0x65, 0xD9, 0x16, 0xBF, 0xD5, 0x45, 0x4A, 0x39, + 0xCA, 0x5A, 0x8F, 0xD2, 0x77, 0x5D, 0x96, 0x25, + 0x2E, 0xD4, 0xC3, 0x01, 0x25, 0xFB, 0x78 +}; diff --git a/deps/picoquic/picotls/deps/cifra/src/testpoly1305.c b/deps/picoquic/picotls/deps/cifra/src/testpoly1305.c new file mode 100644 index 0000000..3ecf1ca --- /dev/null +++ b/deps/picoquic/picotls/deps/cifra/src/testpoly1305.c @@ -0,0 +1,102 @@ +/* + * cifra - embedded cryptography library + * Written in 2014 by Joseph Birr-Pixton + * + * To the extent possible under law, the author(s) have dedicated all + * copyright and related and neighboring rights to this software to the + * public domain worldwide. This software is distributed without any + * warranty. + * + * You should have received a copy of the CC0 Public Domain Dedication + * along with this software. If not, see + * . + */ + + +#include "poly1305.h" + +#include "testutil.h" +#include "handy.h" +#include "cutest.h" + +static void check(const char *rstr, const char *sstr, + const char *msgstr, const char *tagstr) +{ + uint8_t r[16], s[16], tag[16]; + uint8_t msg[132], out[16]; + + unhex(r, sizeof r, rstr); + unhex(s, sizeof s, sstr); + size_t nmsg = unhex(msg, sizeof msg, msgstr); + unhex(tag, sizeof tag, tagstr); + + cf_poly1305 ctx; + cf_poly1305_init(&ctx, r, s); + cf_poly1305_update(&ctx, msg, nmsg); + cf_poly1305_finish(&ctx, out); + + TEST_CHECK(memcmp(out, tag, 16) == 0); +} + +static void test_poly1305(void) +{ + check("eea6a7251c1e72916d11c2cb214d3c25", + "2539121d8e234e652d651fa4c8cff880", + "8e993b9f48681273c29650ba32fc76ce48332ea7164d96a4476fb8c531a1186ac0dfc17c98dce87b4da7f011ec48c97271d2c20f9b928fe2270d6fb863d51738b48eeee314a7cc8ab932164548e526ae90224368517acfeabd6bb3732bc0e9da99832b61ca01b6de56244a9e88d5f9b37973f622a43d14a6599b1f654cb45a74e355a5", + "f3ffc7703f9400e52a7dfb4b3d3305d9"); + + check("851fc40c3467ac0be05cc20404f3f700", + "580b3b0f9447bb1e69d095b5928b6dbc", + "f3f6", + "f4c633c3044fc145f84f335cb81953de"); + + check("a0f3080000f46400d0c7e9076c834403", + "dd3fab2251f11ac759f0887129cc2ee7", + "", + "dd3fab2251f11ac759f0887129cc2ee7"); + + check("48443d0bb0d21109c89a100b5ce2c208", + "83149c69b561dd88298a1798b10716ef", + "663cea190ffb83d89593f3f476b6bc24d7e679107ea26adb8caf6652d0656136", + "0ee1c16bb73f0f4fd19881753c01cdbe"); + + check("12976a08c4426d0ce8a82407c4f48207", + "80f8c20aa71202d1e29179cbcb555a57", + "ab0812724a7f1e342742cbed374d94d136c6b8795d45b3819830f2c04491faf0990c62e48b8018b2c3e4a0fa3134cb67fa83e158c994d961c4cb21095c1bf9", + "5154ad0d2cb26e01274fc51148491f1b"); + + /* extras from RFC7539 */ + check("02000000000000000000000000000000", + "00000000000000000000000000000000", + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", + "03000000000000000000000000000000"); + check("02000000000000000000000000000000", + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", + "02000000000000000000000000000000", + "03000000000000000000000000000000"); + check("01000000000000000000000000000000", + "00000000000000000000000000000000", + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF11000000000000000000000000000000", + "05000000000000000000000000000000"); + check("01000000000000000000000000000000", + "00000000000000000000000000000000", + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBFEFEFEFEFEFEFEFEFEFEFEFEFEFEFE01010101010101010101010101010101", + "00000000000000000000000000000000"); + check("02000000000000000000000000000000", + "00000000000000000000000000000000", + "FDFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", + "FAFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"); + check("01000000000000000400000000000000", + "00000000000000000000000000000000", + "E33594D7505E43B900000000000000003394D7505E4379CD01000000000000000000000000000000000000000000000001000000000000000000000000000000", + "14000000000000005500000000000000"); + check("01000000000000000400000000000000", + "00000000000000000000000000000000", + "E33594D7505E43B900000000000000003394D7505E4379CD010000000000000000000000000000000000000000000000", + "13000000000000000000000000000000"); +} + +TEST_LIST = { + { "poly1305", test_poly1305 }, + { 0 } +}; diff --git a/deps/picoquic/picotls/deps/cifra/src/testsalsa20.c b/deps/picoquic/picotls/deps/cifra/src/testsalsa20.c new file mode 100644 index 0000000..9d50b93 --- /dev/null +++ b/deps/picoquic/picotls/deps/cifra/src/testsalsa20.c @@ -0,0 +1,210 @@ +/* + * cifra - embedded cryptography library + * Written in 2014 by Joseph Birr-Pixton + * + * To the extent possible under law, the author(s) have dedicated all + * copyright and related and neighboring rights to this software to the + * public domain worldwide. This software is distributed without any + * warranty. + * + * You should have received a copy of the CC0 Public Domain Dedication + * along with this software. If not, see + * . + */ + +#include "salsa20.h" + +#include "testutil.h" +#include "handy.h" +#include "cutest.h" + +static void test_salsa20_core(void) +{ + uint8_t k0[16], k1[16], nonce[16], sigma[16], out[64], expect[64]; + + /* From section 8. */ + memset(k0, 0, sizeof k0); + memset(k1, 0, sizeof k1); + memset(nonce, 0, sizeof nonce); + memset(sigma, 0, sizeof sigma); + + cf_salsa20_core(k0, k1, nonce, sigma, out); + + unhex(expect, 64, "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"); + TEST_CHECK(memcmp(expect, out, 64) == 0); + + /* + d39f0d73 + 4c3752b70375de25bfbbea8831edb330 + 016ab2db + afc7a6305610b3cf1ff0203f0f535da1 + 74933071 + ee37cc244fc9eb4f03519c2fcb1af4f3 + 58766836 + */ + unhex(k0, 16, "4c3752b70375de25bfbbea8831edb330"); + unhex(k1, 16, "ee37cc244fc9eb4f03519c2fcb1af4f3"); + unhex(nonce, 16, "afc7a6305610b3cf1ff0203f0f535da1"); + unhex(sigma, 16, "d39f0d73016ab2db7493307158766836"); + + cf_salsa20_core(k0, k1, nonce, sigma, out); + + unhex(expect, 64, "6d2ab2a89cf0f8eea8c4becb1a6eaa9a1d1d961a961eebf9bea3fb30459033397628989db4391b5e6b2aec231b6f7272dbece8876f9b6e1218e85f9eb31330ca"); + TEST_CHECK(memcmp(expect, out, 64) == 0); + + /* + 58766836 + 4fc9eb4f03519c2fcb1af4f3bfbbea88 + d39f0d73 + 4c3752b70375de255610b3cf31edb330 + 016ab2db + afc7a630ee37cc241ff0203f0f535da1 + 74933071 + */ + unhex(k0, 16, "4fc9eb4f03519c2fcb1af4f3bfbbea88"); + unhex(k1, 16, "afc7a630ee37cc241ff0203f0f535da1"); + unhex(nonce, 16, "4c3752b70375de255610b3cf31edb330"); + unhex(sigma, 16, "58766836d39f0d73016ab2db74933071"); + + cf_salsa20_core(k0, k1, nonce, sigma, out); + + unhex(expect, 64, "b31330cadbece8876f9b6e1218e85f9e1a6eaa9a6d2ab2a89cf0f8eea8c4becb459033391d1d961a961eebf9bea3fb301b6f72727628989db4391b5e6b2aec23"); + TEST_CHECK(memcmp(expect, out, 64) == 0); + + /* From section 9. */ + for (size_t i = 0; i < 16; i++) + { + k0[i] = 1 + i; + k1[i] = 201 + i; + nonce[i] = 101 + i; + } + + cf_salsa20_core(k0, k1, nonce, (const uint8_t *) "expand 32-byte k", out); + + unhex(expect, 64, "45254427290f6bc1ff8b7a06aae9d9625990b66a1533c841ef31de22d772287e68c507e1c5991f02664e4cb054f5f6b8b1a0858206489577c0c384ecea67f64a"); + TEST_CHECK(memcmp(expect, out, 64) == 0); + + cf_salsa20_core(k0, k0, nonce, (const uint8_t *) "expand 16-byte k", out); + + unhex(expect, 64, "27ad2ef81ec852113043feef25120df7f1c83d900a3732b9062ff6fd8f56bbe186556ef6a1a32bebe75eab3391d6701d0ee80510978cb78dab097ab568b6b1c1"); + TEST_CHECK(memcmp(expect, out, 64) == 0); +} + +static void test_salsa20(void) +{ + cf_salsa20_ctx ctx; + uint8_t key[32], nonce[8], cipher[64], expect[64]; + + unhex(key, 32, "0102030405060708090a0b0c0d0e0f10c9cacbcccdcecfd0d1d2d3d4d5d6d7d8"); + memset(nonce, 0, 8); + + cf_salsa20_init(&ctx, key, sizeof key, nonce); + unhex(ctx.nonce, 16, "65666768696a6b6c6d6e6f7071727374"); + memset(cipher, 0, 64); + cf_salsa20_cipher(&ctx, cipher, cipher, 64); + + unhex(expect, 64, "45254427290f6bc1ff8b7a06aae9d9625990b66a1533c841ef31de22d772287e68c507e1c5991f02664e4cb054f5f6b8b1a0858206489577c0c384ecea67f64a"); + TEST_CHECK(memcmp(expect, cipher, 64) == 0); + + cf_salsa20_init(&ctx, key, 16, nonce); + unhex(ctx.nonce, 16, "65666768696a6b6c6d6e6f7071727374"); + memset(cipher, 0, 64); + cf_salsa20_cipher(&ctx, cipher, cipher, 64); + + unhex(expect, 64, "27ad2ef81ec852113043feef25120df7f1c83d900a3732b9062ff6fd8f56bbe186556ef6a1a32bebe75eab3391d6701d0ee80510978cb78dab097ab568b6b1c1"); + TEST_CHECK(memcmp(expect, cipher, 64) == 0); +} + +static void test_chacha20_core(void) +{ + uint8_t k0[16], k1[16], nonce[16], out[64], expect[64]; + + /* From draft-agl-tls-chacha20poly1305-04 section 7. */ + + memset(k0, 0, sizeof k0); + memset(k1, 0, sizeof k1); + memset(nonce, 0, sizeof nonce); + + cf_chacha20_core(k0, k1, nonce, (const uint8_t *) "expand 32-byte k", out); + + unhex(expect, 60, "76b8e0ada0f13d90405d6ae55386bd28bdd219b8a08ded1aa836efcc8b770dc7da41597c5157488d7724e03fb8d84a376a43b8f41518a11cc387b669"); + TEST_CHECK(memcmp(expect, out, 60) == 0); + + k1[15] = 0x01; + cf_chacha20_core(k0, k1, nonce, (const uint8_t *) "expand 32-byte k", out); + + unhex(expect, 60, "4540f05a9f1fb296d7736e7b208e3c96eb4fe1834688d2604f450952ed432d41bbe2a0b6ea7566d2a5d1e7e20d42af2c53d792b1c43fea817e9ad275"); + TEST_CHECK(memcmp(expect, out, 60) == 0); + + memset(k1, 0, sizeof k1); + nonce[15] = 0x01; + + cf_chacha20_core(k0, k1, nonce, (const uint8_t *) "expand 32-byte k", out); + + unhex(expect, 60, "de9cba7bf3d69ef5e786dc63973f653a0b49e015adbff7134fcb7df137821031e85a050278a7084527214f73efc7fa5b5277062eb7a0433e445f41e3"); + TEST_CHECK(memcmp(expect, out, 60) == 0); + + memset(nonce, 0, sizeof nonce); + nonce[8] = 0x01; + + cf_chacha20_core(k0, k1, nonce, (const uint8_t *) "expand 32-byte k", out); + + unhex(expect, 64, "ef3fdfd6c61578fbf5cf35bd3dd33b8009631634d21e42ac33960bd138e50d32111e4caf237ee53ca8ad6426194a88545ddc497a0b466e7d6bbdb0041b2f586b"); + TEST_CHECK(memcmp(expect, out, 64) == 0); + + unhex(k0, 16, "000102030405060708090a0b0c0d0e0f"); + unhex(k1, 16, "101112131415161718191a1b1c1d1e1f"); + unhex(nonce, 16, "00000000000000000001020304050607"); + + cf_chacha20_core(k0, k1, nonce, (const uint8_t *) "expand 32-byte k", out); + + unhex(expect, 64, "f798a189f195e66982105ffb640bb7757f579da31602fc93ec01ac56f85ac3c134a4547b733b46413042c9440049176905d3be59ea1c53f15916155c2be8241a"); + TEST_CHECK(memcmp(expect, out, 64) == 0); + + nonce[0]++; + cf_chacha20_core(k0, k1, nonce, (const uint8_t *) "expand 32-byte k", out); + + unhex(expect, 64, "38008b9a26bc35941e2444177c8ade6689de95264986d95889fb60e84629c9bd9a5acb1cc118be563eb9b3a4a472f82e09a7e778492b562ef7130e88dfe031c7"); + TEST_CHECK(memcmp(expect, out, 64) == 0); + + nonce[0]++; + cf_chacha20_core(k0, k1, nonce, (const uint8_t *) "expand 32-byte k", out); + + unhex(expect, 64, "9db9d4f7c7a899151b9a475032b63fc385245fe054e3dd5a97a5f576fe064025d3ce042c566ab2c507b138db853e3d6959660996546cc9c4a6eafdc777c040d7"); + TEST_CHECK(memcmp(expect, out, 64) == 0); + + nonce[0]++; + cf_chacha20_core(k0, k1, nonce, (const uint8_t *) "expand 32-byte k", out); + + unhex(expect, 64, "0eaf46f76dad3979e5c5360c3317166a1c894c94a371876a94df7628fe4eaaf2ccb27d5aaae0ad7ad0f9d4b6ad3b54098746d4524d38407a6deb3ab78fab78c9"); + TEST_CHECK(memcmp(expect, out, 64) == 0); +} + +static void test_chacha20(void) +{ + uint8_t key[32], nonce[8], block[256], expect[256]; + + unhex(key, 32, "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f"); + unhex(nonce, 8, "0001020304050607"); + unhex(expect, 256, "f798a189f195e66982105ffb640bb7757f579da31602fc93ec01ac56f85ac3c134a4547b733b46413042c9440049176905d3be59ea1c53f15916155c2be8241a38008b9a26bc35941e2444177c8ade6689de95264986d95889fb60e84629c9bd9a5acb1cc118be563eb9b3a4a472f82e09a7e778492b562ef7130e88dfe031c79db9d4f7c7a899151b9a475032b63fc385245fe054e3dd5a97a5f576fe064025d3ce042c566ab2c507b138db853e3d6959660996546cc9c4a6eafdc777c040d70eaf46f76dad3979e5c5360c3317166a1c894c94a371876a94df7628fe4eaaf2ccb27d5aaae0ad7ad0f9d4b6ad3b54098746d4524d38407a6deb3ab78fab78c9"); + memset(block, 0, 256); + + cf_chacha20_ctx ctx; + cf_chacha20_init(&ctx, key, sizeof key, nonce); + cf_chacha20_cipher(&ctx, block, block, sizeof block); + + TEST_CHECK(memcmp(expect, block, sizeof expect) == 0); + + /* Check 128-bit mode works. */ + cf_chacha20_init(&ctx, key, 16, nonce); + cf_chacha20_cipher(&ctx, block, block, sizeof block); +} + +TEST_LIST = { + { "salsa20-core", test_salsa20_core }, + { "chacha20-core", test_chacha20_core }, + { "salsa20", test_salsa20 }, + { "chacha20", test_chacha20 }, + { 0 } +}; + diff --git a/deps/picoquic/picotls/deps/cifra/src/testsha.h b/deps/picoquic/picotls/deps/cifra/src/testsha.h new file mode 100644 index 0000000..70075f0 --- /dev/null +++ b/deps/picoquic/picotls/deps/cifra/src/testsha.h @@ -0,0 +1,213 @@ +/* + * cifra - embedded cryptography library + * Written in 2014 by Joseph Birr-Pixton + * + * To the extent possible under law, the author(s) have dedicated all + * copyright and related and neighboring rights to this software to the + * public domain worldwide. This software is distributed without any + * warranty. + * + * You should have received a copy of the CC0 Public Domain Dedication + * along with this software. If not, see + * . + */ + +#ifndef TESTSHA_H +#define TESTSHA_H + +#include "hmac.h" + +/* Common functions for testing hash functions. + * You shouldn't use this file. */ + +static void vector(const cf_chash *hash, + const void *vmsg, size_t nmsg, + const char *expect, size_t nexpect) +{ + uint8_t digest[CF_MAXHASH]; + const uint8_t *msg = vmsg; + size_t orig_nmsg = nmsg; + + cf_chash_ctx ctx; + hash->init(&ctx); + + /* Input in carefully chosen chunk sizes to exercise blockwise code. */ + if (nmsg) + { + hash->update(&ctx, msg, 1); + nmsg--; + msg++; + } + + hash->update(&ctx, msg, nmsg); + hash->digest(&ctx, digest); + TEST_CHECK(nexpect == hash->hashsz); + TEST_CHECK(memcmp(digest, expect, nexpect) == 0); + + /* Now try with other arrangements. */ + msg = vmsg; + nmsg = orig_nmsg; + + hash->init(&ctx); + if (nmsg >= hash->blocksz) + { + hash->update(&ctx, msg, hash->blocksz - 1); + nmsg -= hash->blocksz - 1; + msg += hash->blocksz - 1; + } + + hash->update(&ctx, msg, nmsg); + hash->digest(&ctx, digest); + TEST_CHECK(memcmp(digest, expect, nexpect) == 0); +} + +/* These are shared between RFC2202 and RFC4231. */ +static inline void hmac_test(const cf_chash *hash, + const void *hi_there, + const void *jefe, + const void *aa_dd, + const void *counter_key) +{ + uint8_t sig[CF_MAXHASH]; + uint8_t key[25], message[50]; + + /* Key: 0x0b * 20 + * Message: "Hi There" + */ + memset(key, 0x0b, 20); + memcpy(message, "Hi There", 8); + cf_hmac(key, 20, message, 8, sig, hash); + + TEST_CHECK(memcmp(sig, hi_there, hash->hashsz) == 0); + + /* Key: "Jefe" + * Message: "what do ya want for nothing?" + */ + memcpy(key, "Jefe", 4); + memcpy(message, "what do ya want for nothing?", 28); + cf_hmac(key, 4, message, 28, sig, hash); + TEST_CHECK(memcmp(sig, jefe, hash->hashsz) == 0); + + /* Key: 0xaa * 20 + * Message: 0xdd * 50 + */ + memset(key, 0xaa, 20); + memset(message, 0xdd, 50); + cf_hmac(key, 20, message, 50, sig, hash); + TEST_CHECK(memcmp(sig, aa_dd, hash->hashsz) == 0); + + /* Key: 0x01..0x19 + * Message: 0xcd * 50 + */ + for (uint8_t i = 1; i < 26; i++) + key[i - 1] = i; + memset(message, 0xcd, 50); + cf_hmac(key, 25, message, 50, sig, hash); + TEST_CHECK(memcmp(sig, counter_key, hash->hashsz) == 0); +} + +/* These are specific to RFC4231. */ +static inline void hmac_test_sha2(const cf_chash *hash, + const char *long_key, + const char *long_message) +{ + uint8_t sig[CF_MAXHASH]; + uint8_t key[131], message[152]; + + /* Key: 0xaa * 131 + * Message: "Test Using Larger Than Block-Size Key - Hash Key First" + */ + memset(key, 0xaa, 131); + memcpy(message, "Test Using Larger Than Block-Size Key - Hash Key First", 54); + cf_hmac(key, 131, message, 54, sig, hash); + TEST_CHECK(memcmp(sig, long_key, hash->hashsz) == 0); + + /* Key: 0xaa * 131 + * Message: "This is a test using a larger than block-size key and a larger than block-size data. The key needs to be hashed before being used by the HMAC algorithm." + */ + memset(key, 0xaa, 131); + memcpy(message, "This is a test using a larger than block-size key and a larger than block-size data. The key needs to be hashed before being used by the HMAC algorithm.", 152); + cf_hmac(key, 131, message, 152, sig, hash); + TEST_CHECK(memcmp(sig, long_message, hash->hashsz) == 0); +} + +/* This is as hmac_test_sha2, except the sizes are specific to + * a 512-bit block. This is from RFC2202. */ +static inline void hmac_test_sha1(const cf_chash *hash, + const char *long_key, + const char *long_message) +{ + uint8_t sig[CF_MAXHASH]; + uint8_t key[80], message[73]; + + /* Key: 0xaa * 80 + * Message: "Test Using Larger Than Block-Size Key - Hash Key First" + */ + memset(key, 0xaa, 80); + memcpy(message, "Test Using Larger Than Block-Size Key - Hash Key First", 54); + cf_hmac(key, 80, message, 54, sig, hash); + TEST_CHECK(memcmp(sig, long_key, hash->hashsz) == 0); + + /* Key: 0xaa * 80 + * Message: "Test Using Larger Than Block-Size Key and Larger Than One Block-Size Data" + */ + memset(key, 0xaa, 80); + memcpy(message, "Test Using Larger Than Block-Size Key and Larger Than One Block-Size Data", 73); + cf_hmac(key, 80, message, 73, sig, hash); + TEST_CHECK(memcmp(sig, long_message, hash->hashsz) == 0); +} + +typedef void (*final_fn)(void *ctx, uint8_t *out); + +/* Check incremental interface works, and final function likewise. */ +static void vector_abc_final(const cf_chash *hash, const void *vfinal_fn, + const void *expect, size_t nexpect) +{ + uint8_t digest[CF_MAXHASH]; + + final_fn final = vfinal_fn; + cf_chash_ctx ctx; + hash->init(&ctx); + hash->update(&ctx, "a", 1); + hash->digest(&ctx, digest); + hash->update(&ctx, "b", 1); + hash->digest(&ctx, digest); + hash->update(&ctx, "c", 1); + final(&ctx, digest); + + TEST_CHECK(hash->hashsz == nexpect); + TEST_CHECK(memcmp(expect, digest, nexpect) == 0); +} + +/* Check length-checking vectors work (generated by programs in ../extra_vecs) */ +static inline void vector_length(const cf_chash *h, + size_t max, + const void *expect, size_t nexpect) +{ + cf_chash_ctx outer, inner; + uint8_t digest[CF_MAXHASH]; + + h->init(&outer); + + for (size_t n = 0; n < max; n++) + { + h->init(&inner); + + for (size_t i = 0; i < n; i++) + { + uint8_t byte = (uint8_t) n & 0xff; + h->update(&inner, &byte, 1); + } + + h->digest(&inner, digest); + + h->update(&outer, digest, h->hashsz); + } + + h->digest(&outer, digest); + + TEST_CHECK(h->hashsz == nexpect); + TEST_CHECK(memcmp(expect, digest, nexpect) == 0); +} + +#endif diff --git a/deps/picoquic/picotls/deps/cifra/src/testsha1.c b/deps/picoquic/picotls/deps/cifra/src/testsha1.c new file mode 100644 index 0000000..c7adc8a --- /dev/null +++ b/deps/picoquic/picotls/deps/cifra/src/testsha1.c @@ -0,0 +1,55 @@ +/* + * cifra - embedded cryptography library + * Written in 2014 by Joseph Birr-Pixton + * + * To the extent possible under law, the author(s) have dedicated all + * copyright and related and neighboring rights to this software to the + * public domain worldwide. This software is distributed without any + * warranty. + * + * You should have received a copy of the CC0 Public Domain Dedication + * along with this software. If not, see + * . + */ + +#include "sha1.h" +#include "hmac.h" +#include "handy.h" +#include "cutest.h" +#include "testutil.h" + +#include "testsha.h" + +static void test_sha1(void) +{ + const cf_chash *h = &cf_sha1; + vector(h, "", 0, "\xda\x39\xa3\xee\x5e\x6b\x4b\x0d\x32\x55\xbf\xef\x95\x60\x18\x90\xaf\xd8\x07\x09", 20); + vector(h, "abc", 3, "\xa9\x99\x3e\x36\x47\x06\x81\x6a\xba\x3e\x25\x71\x78\x50\xc2\x6c\x9c\xd0\xd8\x9d", 20); + vector(h, "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", 56, + "\x84\x98\x3e\x44\x1c\x3b\xd2\x6e\xba\xae\x4a\xa1\xf9\x51\x29\xe5\xe5\x46\x70\xf1", 20); + vector(h, "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu", 112, + "\xa4\x9b\x24\x46\xa0\x2c\x64\x5b\xf4\x19\xf9\x95\xb6\x70\x91\x25\x3a\x04\xa2\x59", 20); + + vector_abc_final(h, cf_sha1_digest_final, "\xa9\x99\x3e\x36\x47\x06\x81\x6a\xba\x3e\x25\x71\x78\x50\xc2\x6c\x9c\xd0\xd8\x9d", 20); + + vector_length(h, 1024, "\x15\x53\x65\xcf\x77\xee\xd4\x8f\x46\xe2\x55\xc7\xdd\xdf\xfd\x0a\xf6\x99\x88\xbe", 20); +} + +static void test_hmac_sha1(void) +{ + hmac_test(&cf_sha1, + "\xb6\x17\x31\x86\x55\x05\x72\x64\xe2\x8b\xc0\xb6\xfb\x37\x8c\x8e\xf1\x46\xbe\x00", + "\xef\xfc\xdf\x6a\xe5\xeb\x2f\xa2\xd2\x74\x16\xd5\xf1\x84\xdf\x9c\x25\x9a\x7c\x79", + "\x12\x5d\x73\x42\xb9\xac\x11\xcd\x91\xa3\x9a\xf4\x8a\xa1\x7b\x4f\x63\xf1\x75\xd3", + "\x4c\x90\x07\xf4\x02\x62\x50\xc6\xbc\x84\x14\xf9\xbf\x50\xc8\x6c\x2d\x72\x35\xda"); + hmac_test_sha1(&cf_sha1, + "\xaa\x4a\xe5\xe1\x52\x72\xd0\x0e\x95\x70\x56\x37\xce\x8a\x3b\x55\xed\x40\x21\x12", + "\xe8\xe9\x9d\x0f\x45\x23\x7d\x78\x6d\x6b\xba\xa7\x96\x5c\x78\x08\xbb\xff\x1a\x91"); +} + +TEST_LIST = { + { "sha1", test_sha1}, + { "hmac-sha1", test_hmac_sha1}, + { 0 } +}; + diff --git a/deps/picoquic/picotls/deps/cifra/src/testsha2.c b/deps/picoquic/picotls/deps/cifra/src/testsha2.c new file mode 100644 index 0000000..fc0ab6c --- /dev/null +++ b/deps/picoquic/picotls/deps/cifra/src/testsha2.c @@ -0,0 +1,224 @@ +/* + * cifra - embedded cryptography library + * Written in 2014 by Joseph Birr-Pixton + * + * To the extent possible under law, the author(s) have dedicated all + * copyright and related and neighboring rights to this software to the + * public domain worldwide. This software is distributed without any + * warranty. + * + * You should have received a copy of the CC0 Public Domain Dedication + * along with this software. If not, see + * . + */ + +#include "sha2.h" +#include "hmac.h" +#include "pbkdf2.h" +#include "handy.h" +#include "cutest.h" +#include "testutil.h" + +#include "testsha.h" + +#undef REALLY_SLOW_TEST + +static void test_sha224(void) +{ + const cf_chash *h = &cf_sha224; + vector(h, "", 0, "\xd1\x4a\x02\x8c\x2a\x3a\x2b\xc9\x47\x61\x02\xbb\x28\x82\x34\xc4\x15\xa2\xb0\x1f\x82\x8e\xa6\x2a\xc5\xb3\xe4\x2f", 28); + vector(h, "abc", 3, "\x23\x09\x7d\x22\x34\x05\xd8\x22\x86\x42\xa4\x77\xbd\xa2\x55\xb3\x2a\xad\xbc\xe4\xbd\xa0\xb3\xf7\xe3\x6c\x9d\xa7", 28); + vector(h, "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", 56, + "\x75\x38\x8b\x16\x51\x27\x76\xcc\x5d\xba\x5d\xa1\xfd\x89\x01\x50\xb0\xc6\x45\x5c\xb4\xf5\x8b\x19\x52\x52\x25\x25", 28); + vector(h, "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu", 112, + "\xc9\x7c\xa9\xa5\x59\x85\x0c\xe9\x7a\x04\xa9\x6d\xef\x6d\x99\xa9\xe0\xe0\xe2\xab\x14\xe6\xb8\xdf\x26\x5f\xc0\xb3", 28); + + /* Check that incremental interface produces correct results. */ + vector_abc_final(h, cf_sha224_digest_final, "\x23\x09\x7d\x22\x34\x05\xd8\x22\x86\x42\xa4\x77\xbd\xa2\x55\xb3\x2a\xad\xbc\xe4\xbd\xa0\xb3\xf7\xe3\x6c\x9d\xa7", 28); + + vector_length(h, 1024, "\x08\x2c\x80\x5b\x6f\x85\xde\x0e\xdf\xa8\x51\xa0\x1f\xe6\x4f\x64\x85\x16\x48\xae\xfc\xc1\xd4\x52\x4e\xf8\x36\xe7", 28); +} + +static void test_hmac_sha224(void) +{ + hmac_test(&cf_sha224, + "\x89\x6f\xb1\x12\x8a\xbb\xdf\x19\x68\x32\x10\x7c\xd4\x9d\xf3\x3f\x47\xb4\xb1\x16\x99\x12\xba\x4f\x53\x68\x4b\x22", + "\xa3\x0e\x01\x09\x8b\xc6\xdb\xbf\x45\x69\x0f\x3a\x7e\x9e\x6d\x0f\x8b\xbe\xa2\xa3\x9e\x61\x48\x00\x8f\xd0\x5e\x44", + "\x7f\xb3\xcb\x35\x88\xc6\xc1\xf6\xff\xa9\x69\x4d\x7d\x6a\xd2\x64\x93\x65\xb0\xc1\xf6\x5d\x69\xd1\xec\x83\x33\xea", + "\x6c\x11\x50\x68\x74\x01\x3c\xac\x6a\x2a\xbc\x1b\xb3\x82\x62\x7c\xec\x6a\x90\xd8\x6e\xfc\x01\x2d\xe7\xaf\xec\x5a"); + hmac_test_sha2(&cf_sha224, + "\x95\xe9\xa0\xdb\x96\x20\x95\xad\xae\xbe\x9b\x2d\x6f\x0d\xbc\xe2\xd4\x99\xf1\x12\xf2\xd2\xb7\x27\x3f\xa6\x87\x0e", + "\x3a\x85\x41\x66\xac\x5d\x9f\x02\x3f\x54\xd5\x17\xd0\xb3\x9d\xbd\x94\x67\x70\xdb\x9c\x2b\x95\xc9\xf6\xf5\x65\xd1"); +} + +static void test_sha256(void) +{ + const cf_chash *h = &cf_sha256; + vector(h, "", 0, "\xe3\xb0\xc4\x42\x98\xfc\x1c\x14\x9a\xfb\xf4\xc8\x99\x6f\xb9\x24\x27\xae\x41\xe4\x64\x9b\x93\x4c\xa4\x95\x99\x1b\x78\x52\xb8\x55", 32); + vector(h, "abc", 3, "\xba\x78\x16\xbf\x8f\x01\xcf\xea\x41\x41\x40\xde\x5d\xae\x22\x23\xb0\x03\x61\xa3\x96\x17\x7a\x9c\xb4\x10\xff\x61\xf2\x00\x15\xad", 32); + vector(h, "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", 56, + "\x24\x8d\x6a\x61\xd2\x06\x38\xb8\xe5\xc0\x26\x93\x0c\x3e\x60\x39\xa3\x3c\xe4\x59\x64\xff\x21\x67\xf6\xec\xed\xd4\x19\xdb\x06\xc1", 32); + vector(h, "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu", 112, + "\xcf\x5b\x16\xa7\x78\xaf\x83\x80\x03\x6c\xe5\x9e\x7b\x04\x92\x37\x0b\x24\x9b\x11\xe8\xf0\x7a\x51\xaf\xac\x45\x03\x7a\xfe\xe9\xd1", 32); + + vector_abc_final(h, cf_sha256_digest_final, "\xba\x78\x16\xbf\x8f\x01\xcf\xea\x41\x41\x40\xde\x5d\xae\x22\x23\xb0\x03\x61\xa3\x96\x17\x7a\x9c\xb4\x10\xff\x61\xf2\x00\x15\xad", 32); + + vector_length(h, 1024, "\x55\x7b\xfd\xd5\xef\xda\xfd\x63\x06\x5e\xb7\x98\x87\xde\x86\xdb\x54\xc3\xfe\xdf\x7b\xcc\xcb\x97\x08\xfa\x87\xf0\x11\x87\x61\xdc", 32); +} + +static void test_hmac_sha256(void) +{ + hmac_test(&cf_sha256, + "\xb0\x34\x4c\x61\xd8\xdb\x38\x53\x5c\xa8\xaf\xce\xaf\x0b\xf1\x2b\x88\x1d\xc2\x00\xc9\x83\x3d\xa7\x26\xe9\x37\x6c\x2e\x32\xcf\xf7", + "\x5b\xdc\xc1\x46\xbf\x60\x75\x4e\x6a\x04\x24\x26\x08\x95\x75\xc7\x5a\x00\x3f\x08\x9d\x27\x39\x83\x9d\xec\x58\xb9\x64\xec\x38\x43", + "\x77\x3e\xa9\x1e\x36\x80\x0e\x46\x85\x4d\xb8\xeb\xd0\x91\x81\xa7\x29\x59\x09\x8b\x3e\xf8\xc1\x22\xd9\x63\x55\x14\xce\xd5\x65\xfe", + "\x82\x55\x8a\x38\x9a\x44\x3c\x0e\xa4\xcc\x81\x98\x99\xf2\x08\x3a\x85\xf0\xfa\xa3\xe5\x78\xf8\x07\x7a\x2e\x3f\xf4\x67\x29\x66\x5b"); + hmac_test_sha2(&cf_sha256, + "\x60\xe4\x31\x59\x1e\xe0\xb6\x7f\x0d\x8a\x26\xaa\xcb\xf5\xb7\x7f\x8e\x0b\xc6\x21\x37\x28\xc5\x14\x05\x46\x04\x0f\x0e\xe3\x7f\x54", + "\x9b\x09\xff\xa7\x1b\x94\x2f\xcb\x27\x63\x5f\xbc\xd5\xb0\xe9\x44\xbf\xdc\x63\x64\x4f\x07\x13\x93\x8a\x7f\x51\x53\x5c\x3a\x35\xe2"); +} + +static void test_sha384(void) +{ + const cf_chash *h = &cf_sha384; + vector(h, "", 0, "\x38\xb0\x60\xa7\x51\xac\x96\x38\x4c\xd9\x32\x7e\xb1\xb1\xe3\x6a\x21\xfd\xb7\x11\x14\xbe\x07\x43\x4c\x0c\xc7\xbf\x63\xf6\xe1\xda\x27\x4e\xde\xbf\xe7\x6f\x65\xfb\xd5\x1a\xd2\xf1\x48\x98\xb9\x5b", 48); + vector(h, "abc", 3, "\xcb\x00\x75\x3f\x45\xa3\x5e\x8b\xb5\xa0\x3d\x69\x9a\xc6\x50\x07\x27\x2c\x32\xab\x0e\xde\xd1\x63\x1a\x8b\x60\x5a\x43\xff\x5b\xed\x80\x86\x07\x2b\xa1\xe7\xcc\x23\x58\xba\xec\xa1\x34\xc8\x25\xa7", 48); + vector(h, "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", 56, + "\x33\x91\xfd\xdd\xfc\x8d\xc7\x39\x37\x07\xa6\x5b\x1b\x47\x09\x39\x7c\xf8\xb1\xd1\x62\xaf\x05\xab\xfe\x8f\x45\x0d\xe5\xf3\x6b\xc6\xb0\x45\x5a\x85\x20\xbc\x4e\x6f\x5f\xe9\x5b\x1f\xe3\xc8\x45\x2b", 48); + vector(h, "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu", 112, + "\x09\x33\x0c\x33\xf7\x11\x47\xe8\x3d\x19\x2f\xc7\x82\xcd\x1b\x47\x53\x11\x1b\x17\x3b\x3b\x05\xd2\x2f\xa0\x80\x86\xe3\xb0\xf7\x12\xfc\xc7\xc7\x1a\x55\x7e\x2d\xb9\x66\xc3\xe9\xfa\x91\x74\x60\x39", 48); + + vector_abc_final(h, cf_sha384_digest_final, "\xcb\x00\x75\x3f\x45\xa3\x5e\x8b\xb5\xa0\x3d\x69\x9a\xc6\x50\x07\x27\x2c\x32\xab\x0e\xde\xd1\x63\x1a\x8b\x60\x5a\x43\xff\x5b\xed\x80\x86\x07\x2b\xa1\xe7\xcc\x23\x58\xba\xec\xa1\x34\xc8\x25\xa7", 48); + + vector_length(h, 1024, "\xae\xe7\xf1\x43\xef\x69\x7b\xa2\xe1\xfd\x39\x9f\xd7\xe6\x28\x9c\x19\xaf\x3c\xc1\xbd\xda\xbd\x3f\x07\xae\xc2\xea\x40\x55\x37\x27\x5f\x41\x49\x50\x3f\xcc\xaf\xa0\xc5\x95\xf7\x62\x9b\xff\x50\x32", 48); +} + +static void test_hmac_sha384(void) +{ + hmac_test(&cf_sha384, + "\xaf\xd0\x39\x44\xd8\x48\x95\x62\x6b\x08\x25\xf4\xab\x46\x90\x7f\x15\xf9\xda\xdb\xe4\x10\x1e\xc6\x82\xaa\x03\x4c\x7c\xeb\xc5\x9c\xfa\xea\x9e\xa9\x07\x6e\xde\x7f\x4a\xf1\x52\xe8\xb2\xfa\x9c\xb6", + "\xaf\x45\xd2\xe3\x76\x48\x40\x31\x61\x7f\x78\xd2\xb5\x8a\x6b\x1b\x9c\x7e\xf4\x64\xf5\xa0\x1b\x47\xe4\x2e\xc3\x73\x63\x22\x44\x5e\x8e\x22\x40\xca\x5e\x69\xe2\xc7\x8b\x32\x39\xec\xfa\xb2\x16\x49", + "\x88\x06\x26\x08\xd3\xe6\xad\x8a\x0a\xa2\xac\xe0\x14\xc8\xa8\x6f\x0a\xa6\x35\xd9\x47\xac\x9f\xeb\xe8\x3e\xf4\xe5\x59\x66\x14\x4b\x2a\x5a\xb3\x9d\xc1\x38\x14\xb9\x4e\x3a\xb6\xe1\x01\xa3\x4f\x27", + "\x3e\x8a\x69\xb7\x78\x3c\x25\x85\x19\x33\xab\x62\x90\xaf\x6c\xa7\x7a\x99\x81\x48\x08\x50\x00\x9c\xc5\x57\x7c\x6e\x1f\x57\x3b\x4e\x68\x01\xdd\x23\xc4\xa7\xd6\x79\xcc\xf8\xa3\x86\xc6\x74\xcf\xfb"); + hmac_test_sha2(&cf_sha384, + "\x4e\xce\x08\x44\x85\x81\x3e\x90\x88\xd2\xc6\x3a\x04\x1b\xc5\xb4\x4f\x9e\xf1\x01\x2a\x2b\x58\x8f\x3c\xd1\x1f\x05\x03\x3a\xc4\xc6\x0c\x2e\xf6\xab\x40\x30\xfe\x82\x96\x24\x8d\xf1\x63\xf4\x49\x52", + "\x66\x17\x17\x8e\x94\x1f\x02\x0d\x35\x1e\x2f\x25\x4e\x8f\xd3\x2c\x60\x24\x20\xfe\xb0\xb8\xfb\x9a\xdc\xce\xbb\x82\x46\x1e\x99\xc5\xa6\x78\xcc\x31\xe7\x99\x17\x6d\x38\x60\xe6\x11\x0c\x46\x52\x3e"); +} + +static void test_sha512(void) +{ + const cf_chash *h = &cf_sha512; + vector(h, "", 0, "\xcf\x83\xe1\x35\x7e\xef\xb8\xbd\xf1\x54\x28\x50\xd6\x6d\x80\x07\xd6\x20\xe4\x05\x0b\x57\x15\xdc\x83\xf4\xa9\x21\xd3\x6c\xe9\xce\x47\xd0\xd1\x3c\x5d\x85\xf2\xb0\xff\x83\x18\xd2\x87\x7e\xec\x2f\x63\xb9\x31\xbd\x47\x41\x7a\x81\xa5\x38\x32\x7a\xf9\x27\xda\x3e", 64); + vector(h, "abc", 3, "\xdd\xaf\x35\xa1\x93\x61\x7a\xba\xcc\x41\x73\x49\xae\x20\x41\x31\x12\xe6\xfa\x4e\x89\xa9\x7e\xa2\x0a\x9e\xee\xe6\x4b\x55\xd3\x9a\x21\x92\x99\x2a\x27\x4f\xc1\xa8\x36\xba\x3c\x23\xa3\xfe\xeb\xbd\x45\x4d\x44\x23\x64\x3c\xe8\x0e\x2a\x9a\xc9\x4f\xa5\x4c\xa4\x9f", 64); + vector(h, "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", 56, + "\x20\x4a\x8f\xc6\xdd\xa8\x2f\x0a\x0c\xed\x7b\xeb\x8e\x08\xa4\x16\x57\xc1\x6e\xf4\x68\xb2\x28\xa8\x27\x9b\xe3\x31\xa7\x03\xc3\x35\x96\xfd\x15\xc1\x3b\x1b\x07\xf9\xaa\x1d\x3b\xea\x57\x78\x9c\xa0\x31\xad\x85\xc7\xa7\x1d\xd7\x03\x54\xec\x63\x12\x38\xca\x34\x45", 64); + vector(h, "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu", 112, + "\x8e\x95\x9b\x75\xda\xe3\x13\xda\x8c\xf4\xf7\x28\x14\xfc\x14\x3f\x8f\x77\x79\xc6\xeb\x9f\x7f\xa1\x72\x99\xae\xad\xb6\x88\x90\x18\x50\x1d\x28\x9e\x49\x00\xf7\xe4\x33\x1b\x99\xde\xc4\xb5\x43\x3a\xc7\xd3\x29\xee\xb6\xdd\x26\x54\x5e\x96\xe5\x5b\x87\x4b\xe9\x09", 64); + + vector_abc_final(h, cf_sha512_digest_final, "\xdd\xaf\x35\xa1\x93\x61\x7a\xba\xcc\x41\x73\x49\xae\x20\x41\x31\x12\xe6\xfa\x4e\x89\xa9\x7e\xa2\x0a\x9e\xee\xe6\x4b\x55\xd3\x9a\x21\x92\x99\x2a\x27\x4f\xc1\xa8\x36\xba\x3c\x23\xa3\xfe\xeb\xbd\x45\x4d\x44\x23\x64\x3c\xe8\x0e\x2a\x9a\xc9\x4f\xa5\x4c\xa4\x9f", 64); + + vector_length(h, 1024, "\x61\x20\x81\x2e\xd5\x0c\xc3\x11\x67\x04\x3f\x1f\x06\x9d\xcd\x4a\xd8\x83\x23\xd9\x96\x53\xd9\x67\x38\x2c\xc3\x44\x25\x69\x53\x1c\xd0\x3d\xe4\x79\x0a\x71\xde\x88\x45\x44\x66\x80\xb8\xc5\x90\xb3\x07\xc8\xae\x52\x57\x67\xf9\x28\xf8\xda\x9e\x9e\x80\xc9\x35\x5e", 64); +} + +static void test_hmac_sha512(void) +{ + hmac_test(&cf_sha512, + "\x87\xaa\x7c\xde\xa5\xef\x61\x9d\x4f\xf0\xb4\x24\x1a\x1d\x6c\xb0\x23\x79\xf4\xe2\xce\x4e\xc2\x78\x7a\xd0\xb3\x05\x45\xe1\x7c\xde\xda\xa8\x33\xb7\xd6\xb8\xa7\x02\x03\x8b\x27\x4e\xae\xa3\xf4\xe4\xbe\x9d\x91\x4e\xeb\x61\xf1\x70\x2e\x69\x6c\x20\x3a\x12\x68\x54", + "\x16\x4b\x7a\x7b\xfc\xf8\x19\xe2\xe3\x95\xfb\xe7\x3b\x56\xe0\xa3\x87\xbd\x64\x22\x2e\x83\x1f\xd6\x10\x27\x0c\xd7\xea\x25\x05\x54\x97\x58\xbf\x75\xc0\x5a\x99\x4a\x6d\x03\x4f\x65\xf8\xf0\xe6\xfd\xca\xea\xb1\xa3\x4d\x4a\x6b\x4b\x63\x6e\x07\x0a\x38\xbc\xe7\x37", + "\xfa\x73\xb0\x08\x9d\x56\xa2\x84\xef\xb0\xf0\x75\x6c\x89\x0b\xe9\xb1\xb5\xdb\xdd\x8e\xe8\x1a\x36\x55\xf8\x3e\x33\xb2\x27\x9d\x39\xbf\x3e\x84\x82\x79\xa7\x22\xc8\x06\xb4\x85\xa4\x7e\x67\xc8\x07\xb9\x46\xa3\x37\xbe\xe8\x94\x26\x74\x27\x88\x59\xe1\x32\x92\xfb", + "\xb0\xba\x46\x56\x37\x45\x8c\x69\x90\xe5\xa8\xc5\xf6\x1d\x4a\xf7\xe5\x76\xd9\x7f\xf9\x4b\x87\x2d\xe7\x6f\x80\x50\x36\x1e\xe3\xdb\xa9\x1c\xa5\xc1\x1a\xa2\x5e\xb4\xd6\x79\x27\x5c\xc5\x78\x80\x63\xa5\xf1\x97\x41\x12\x0c\x4f\x2d\xe2\xad\xeb\xeb\x10\xa2\x98\xdd"); + hmac_test_sha2(&cf_sha512, + "\x80\xb2\x42\x63\xc7\xc1\xa3\xeb\xb7\x14\x93\xc1\xdd\x7b\xe8\xb4\x9b\x46\xd1\xf4\x1b\x4a\xee\xc1\x12\x1b\x01\x37\x83\xf8\xf3\x52\x6b\x56\xd0\x37\xe0\x5f\x25\x98\xbd\x0f\xd2\x21\x5d\x6a\x1e\x52\x95\xe6\x4f\x73\xf6\x3f\x0a\xec\x8b\x91\x5a\x98\x5d\x78\x65\x98", + "\xe3\x7b\x6a\x77\x5d\xc8\x7d\xba\xa4\xdf\xa9\xf9\x6e\x5e\x3f\xfd\xde\xbd\x71\xf8\x86\x72\x89\x86\x5d\xf5\xa3\x2d\x20\xcd\xc9\x44\xb6\x02\x2c\xac\x3c\x49\x82\xb1\x0d\x5e\xeb\x55\xc3\xe4\xde\x15\x13\x46\x76\xfb\x6d\xe0\x44\x60\x65\xc9\x74\x40\xfa\x8c\x6a\x58"); +} + +#ifdef REALLY_SLOW_TEST +static void test_sha256_long(void) +{ + uint8_t digest[32]; + cf_sha256_context ctx; + cf_sha256_init(&ctx); + + for (size_t i = 0; i < 0x1000000; i++) + cf_sha256_update(&ctx, "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmno", 64); + cf_sha256_digest_final(&ctx, digest); + + uint8_t expect[32]; + unhex(expect, sizeof expect, "\x50\xe7\x2a\x0e\x26\x44\x2f\xe2\x55\x2d\xc3\x93\x8a\xc5\x86\x58\x22\x8c\x0c\xbf\xb1\xd2\xca\x87\x2a\xe4\x35\x26\x6f\xcd\x05\x5e", 32); + TEST_CHECK(memcmp(expect, digest, sizeof digest) == 0); +} +#endif + +static void check_pkbdf2_sha256(const void *pw, size_t npw, + const void *salt, size_t nsalt, + uint32_t iters, + const void *expect, size_t nexpect) +{ + uint8_t output[64]; + + cf_pbkdf2_hmac((const void *) pw, npw, + (const void *) salt, nsalt, + iters, + output, nexpect, + &cf_sha256); + + TEST_CHECK(memcmp(expect, output, nexpect) == 0); +} + +static void test_pbkdf2_sha256(void) +{ + check_pkbdf2_sha256("password", 8, + "salt", 4, + 1, + "\x12\x0f\xb6\xcf\xfc\xf8\xb3\x2c\x43\xe7\x22\x52\x56\xc4\xf8\x37\xa8\x65\x48\xc9\x2c\xcc\x35\x48\x08\x05\x98\x7c\xb7\x0b\xe1\x7b", 32); + + check_pkbdf2_sha256("password", 8, + "salt", 4, + 2, + "\xae\x4d\x0c\x95\xaf\x6b\x46\xd3\x2d\x0a\xdf\xf9\x28\xf0\x6d\xd0\x2a\x30\x3f\x8e\xf3\xc2\x51\xdf\xd6\xe2\xd8\x5a\x95\x47\x4c\x43", 32); + + check_pkbdf2_sha256("password", 8, + "salt", 4, + 4096, + "\xc5\xe4\x78\xd5\x92\x88\xc8\x41\xaa\x53\x0d\xb6\x84\x5c\x4c\x8d\x96\x28\x93\xa0\x01\xce\x4e\x11\xa4\x96\x38\x73\xaa\x98\x13\x4a", 32); + + check_pkbdf2_sha256("passwordPASSWORDpassword", 24, + "saltSALTsaltSALTsaltSALTsaltSALTsalt", 36, + 4096, + "\x34\x8c\x89\xdb\xcb\xd3\x2b\x2f\x32\xd8\x14\xb8\x11\x6e\x84\xcf\x2b\x17\x34\x7e\xbc\x18\x00\x18\x1c\x4e\x2a\x1f\xb8\xdd\x53\xe1\xc6\x35\x51\x8c\x7d\xac\x47\xe9", 40); + + check_pkbdf2_sha256("", 0, + "salt", 4, + 1024, + "\x9e\x83\xf2\x79\xc0\x40\xf2\xa1\x1a\xa4\xa0\x2b\x24\xc4\x18\xf2\xd3\xcb\x39\x56\x0c\x96\x27\xfa\x4f\x47\xe3\xbc\xc2\x89\x7c\x3d", 32); + + check_pkbdf2_sha256("password", 8, + "", 0, + 1024, + "\xea\x58\x08\x41\x1e\xb0\xc7\xe8\x30\xde\xab\x55\x09\x6c\xee\x58\x27\x61\xe2\x2a\x9b\xc0\x34\xe3\xec\xe9\x25\x22\x5b\x07\xbf\x46", 32); + + check_pkbdf2_sha256("\x70\x61\x73\x73\x00\x77\x6f\x72\x64", 9, + "\x73\x61\x00\x6c\x74", 5, + 4096, + "\x89\xb6\x9d\x05\x16\xf8\x29\x89\x3c\x69\x62\x26\x65\x0a\x86\x87", 16); +} + +TEST_LIST = { + { "sha224", test_sha224}, + { "sha256", test_sha256 }, + { "sha384", test_sha384 }, + { "sha512", test_sha512 }, + + { "hmac-sha224", test_hmac_sha224 }, + { "hmac-sha256", test_hmac_sha256 }, + { "hmac-sha384", test_hmac_sha384 }, + { "hmac-sha512", test_hmac_sha512 }, + + { "pbkdf2-sha256", test_pbkdf2_sha256 }, + +#ifdef REALLY_SLOW_TEST + { "sha256-long", test_sha256_long }, +#endif + { 0 } +}; + diff --git a/deps/picoquic/picotls/deps/cifra/src/testsha3.c b/deps/picoquic/picotls/deps/cifra/src/testsha3.c new file mode 100644 index 0000000..92ab9cf --- /dev/null +++ b/deps/picoquic/picotls/deps/cifra/src/testsha3.c @@ -0,0 +1,104 @@ +/* + * cifra - embedded cryptography library + * Written in 2014 by Joseph Birr-Pixton + * + * To the extent possible under law, the author(s) have dedicated all + * copyright and related and neighboring rights to this software to the + * public domain worldwide. This software is distributed without any + * warranty. + * + * You should have received a copy of the CC0 Public Domain Dedication + * along with this software. If not, see + * . + */ + +#include "sha3.h" +#include "handy.h" +#include "cutest.h" +#include "testutil.h" +#include "testsha.h" + +static void test_sha3_224(void) +{ + const cf_chash *H = &cf_sha3_224; + vector(H, "", 0, + "\x6b\x4e\x03\x42\x36\x67\xdb\xb7\x3b\x6e\x15\x45\x4f\x0e\xb1\xab\xd4\x59\x7f\x9a\x1b\x07\x8e\x3f\x5b\x5a\x6b\xc7", 28); + vector(H, "abc", 3, + "\xe6\x42\x82\x4c\x3f\x8c\xf2\x4a\xd0\x92\x34\xee\x7d\x3c\x76\x6f\xc9\xa3\xa5\x16\x8d\x0c\x94\xad\x73\xb4\x6f\xdf", 28); + vector(H, "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", 56, + "\x8a\x24\x10\x8b\x15\x4a\xda\x21\xc9\xfd\x55\x74\x49\x44\x79\xba\x5c\x7e\x7a\xb7\x6e\xf2\x64\xea\xd0\xfc\xce\x33", 28); + vector(H, "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu", 112, + "\x54\x3e\x68\x68\xe1\x66\x6c\x1a\x64\x36\x30\xdf\x77\x36\x7a\xe5\xa6\x2a\x85\x07\x0a\x51\xc1\x4c\xbf\x66\x5c\xbc", 28); + + /* Artificial exercise for len(msg) = rate_bytes - 1 codepath */ + vector(H, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", 143, + "\x73\xb1\xb2\x2b\x54\xf5\x15\xf6\x26\xa6\xab\xdd\xe6\xaf\x25\xcd\x48\x01\xdc\x6e\x9d\xc7\xfa\x3f\x77\xe1\xc1\x22", 28); + + vector_abc_final(H, cf_sha3_224_digest_final, + "\xe6\x42\x82\x4c\x3f\x8c\xf2\x4a\xd0\x92\x34\xee\x7d\x3c\x76\x6f\xc9\xa3\xa5\x16\x8d\x0c\x94\xad\x73\xb4\x6f\xdf", 28); + + vector_length(H, 1024, "\xf2\x54\xf3\x67\x6d\xc6\xc0\x0f\x2f\xee\x50\x59\x07\x62\x8b\x0d\x5b\x9e\xdf\xf8\xe8\xc2\x9e\x9b\xa7\xd6\x05\xdd", 28); +} + +static void test_sha3_256(void) +{ + const cf_chash *H = &cf_sha3_256; + vector(H, "", 0, + "\xa7\xff\xc6\xf8\xbf\x1e\xd7\x66\x51\xc1\x47\x56\xa0\x61\xd6\x62\xf5\x80\xff\x4d\xe4\x3b\x49\xfa\x82\xd8\x0a\x4b\x80\xf8\x43\x4a", 32); + vector(H, "abc", 3, + "\x3a\x98\x5d\xa7\x4f\xe2\x25\xb2\x04\x5c\x17\x2d\x6b\xd3\x90\xbd\x85\x5f\x08\x6e\x3e\x9d\x52\x5b\x46\xbf\xe2\x45\x11\x43\x15\x32", 32); + vector(H, "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", 56, + "\x41\xc0\xdb\xa2\xa9\xd6\x24\x08\x49\x10\x03\x76\xa8\x23\x5e\x2c\x82\xe1\xb9\x99\x8a\x99\x9e\x21\xdb\x32\xdd\x97\x49\x6d\x33\x76", 32); + vector(H, "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu", 112, + "\x91\x6f\x60\x61\xfe\x87\x97\x41\xca\x64\x69\xb4\x39\x71\xdf\xdb\x28\xb1\xa3\x2d\xc3\x6c\xb3\x25\x4e\x81\x2b\xe2\x7a\xad\x1d\x18", 32); + + vector_abc_final(H, cf_sha3_256_digest_final, + "\x3a\x98\x5d\xa7\x4f\xe2\x25\xb2\x04\x5c\x17\x2d\x6b\xd3\x90\xbd\x85\x5f\x08\x6e\x3e\x9d\x52\x5b\x46\xbf\xe2\x45\x11\x43\x15\x32", 32); + + vector_length(H, 1024, "\xf7\xed\xf7\x2b\x34\x8c\xb4\xab\x5e\xe7\x4f\x6c\xae\xaf\x11\xad\xe2\x2f\x04\x65\x84\x8e\x5c\xaa\x14\x38\x7f\xd4\xeb\xdb\x9d\x70", 32); +} + +static void test_sha3_384(void) +{ + const cf_chash *H = &cf_sha3_384; + vector(H, "", 0, + "\x0c\x63\xa7\x5b\x84\x5e\x4f\x7d\x01\x10\x7d\x85\x2e\x4c\x24\x85\xc5\x1a\x50\xaa\xaa\x94\xfc\x61\x99\x5e\x71\xbb\xee\x98\x3a\x2a\xc3\x71\x38\x31\x26\x4a\xdb\x47\xfb\x6b\xd1\xe0\x58\xd5\xf0\x04", 48); + vector(H, "abc", 3, + "\xec\x01\x49\x82\x88\x51\x6f\xc9\x26\x45\x9f\x58\xe2\xc6\xad\x8d\xf9\xb4\x73\xcb\x0f\xc0\x8c\x25\x96\xda\x7c\xf0\xe4\x9b\xe4\xb2\x98\xd8\x8c\xea\x92\x7a\xc7\xf5\x39\xf1\xed\xf2\x28\x37\x6d\x25", 48); + vector(H, "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", 56, + "\x99\x1c\x66\x57\x55\xeb\x3a\x4b\x6b\xbd\xfb\x75\xc7\x8a\x49\x2e\x8c\x56\xa2\x2c\x5c\x4d\x7e\x42\x9b\xfd\xbc\x32\xb9\xd4\xad\x5a\xa0\x4a\x1f\x07\x6e\x62\xfe\xa1\x9e\xef\x51\xac\xd0\x65\x7c\x22", 48); + vector(H, "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu", 112, + "\x79\x40\x7d\x3b\x59\x16\xb5\x9c\x3e\x30\xb0\x98\x22\x97\x47\x91\xc3\x13\xfb\x9e\xcc\x84\x9e\x40\x6f\x23\x59\x2d\x04\xf6\x25\xdc\x8c\x70\x9b\x98\xb4\x3b\x38\x52\xb3\x37\x21\x61\x79\xaa\x7f\xc7", 48); + + vector_abc_final(H, cf_sha3_384_digest_final, + "\xec\x01\x49\x82\x88\x51\x6f\xc9\x26\x45\x9f\x58\xe2\xc6\xad\x8d\xf9\xb4\x73\xcb\x0f\xc0\x8c\x25\x96\xda\x7c\xf0\xe4\x9b\xe4\xb2\x98\xd8\x8c\xea\x92\x7a\xc7\xf5\x39\xf1\xed\xf2\x28\x37\x6d\x25", 48); + + vector_length(H, 1024, "\xc2\x16\x48\x6a\x00\x32\xb1\xe1\x98\xf8\x72\x52\x01\x87\xba\xd4\xcf\x39\x13\x9c\x54\x21\x6d\x78\x86\x93\x88\xf9\x75\x03\xc1\x11\xcc\x7f\x5a\xc3\x21\x00\x3f\xc8\xa1\xf7\xfa\x10\x75\x60\xdb\xb1", 48); +} + +static void test_sha3_512(void) +{ + const cf_chash *H = &cf_sha3_512; + vector(H, "", 0, + "\xa6\x9f\x73\xcc\xa2\x3a\x9a\xc5\xc8\xb5\x67\xdc\x18\x5a\x75\x6e\x97\xc9\x82\x16\x4f\xe2\x58\x59\xe0\xd1\xdc\xc1\x47\x5c\x80\xa6\x15\xb2\x12\x3a\xf1\xf5\xf9\x4c\x11\xe3\xe9\x40\x2c\x3a\xc5\x58\xf5\x00\x19\x9d\x95\xb6\xd3\xe3\x01\x75\x85\x86\x28\x1d\xcd\x26", 64); + vector(H, "abc", 3, + "\xb7\x51\x85\x0b\x1a\x57\x16\x8a\x56\x93\xcd\x92\x4b\x6b\x09\x6e\x08\xf6\x21\x82\x74\x44\xf7\x0d\x88\x4f\x5d\x02\x40\xd2\x71\x2e\x10\xe1\x16\xe9\x19\x2a\xf3\xc9\x1a\x7e\xc5\x76\x47\xe3\x93\x40\x57\x34\x0b\x4c\xf4\x08\xd5\xa5\x65\x92\xf8\x27\x4e\xec\x53\xf0", 64); + vector(H, "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", 56, + "\x04\xa3\x71\xe8\x4e\xcf\xb5\xb8\xb7\x7c\xb4\x86\x10\xfc\xa8\x18\x2d\xd4\x57\xce\x6f\x32\x6a\x0f\xd3\xd7\xec\x2f\x1e\x91\x63\x6d\xee\x69\x1f\xbe\x0c\x98\x53\x02\xba\x1b\x0d\x8d\xc7\x8c\x08\x63\x46\xb5\x33\xb4\x9c\x03\x0d\x99\xa2\x7d\xaf\x11\x39\xd6\xe7\x5e", 64); + vector(H, "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu", 112, + "\xaf\xeb\xb2\xef\x54\x2e\x65\x79\xc5\x0c\xad\x06\xd2\xe5\x78\xf9\xf8\xdd\x68\x81\xd7\xdc\x82\x4d\x26\x36\x0f\xee\xbf\x18\xa4\xfa\x73\xe3\x26\x11\x22\x94\x8e\xfc\xfd\x49\x2e\x74\xe8\x2e\x21\x89\xed\x0f\xb4\x40\xd1\x87\xf3\x82\x27\x0c\xb4\x55\xf2\x1d\xd1\x85", 64); + + vector_abc_final(H, cf_sha3_512_digest_final, + "\xb7\x51\x85\x0b\x1a\x57\x16\x8a\x56\x93\xcd\x92\x4b\x6b\x09\x6e\x08\xf6\x21\x82\x74\x44\xf7\x0d\x88\x4f\x5d\x02\x40\xd2\x71\x2e\x10\xe1\x16\xe9\x19\x2a\xf3\xc9\x1a\x7e\xc5\x76\x47\xe3\x93\x40\x57\x34\x0b\x4c\xf4\x08\xd5\xa5\x65\x92\xf8\x27\x4e\xec\x53\xf0", 64); + + vector_length(H, 1024, "\x3a\x98\x11\x17\xbc\x2f\xa3\x3b\x00\x51\x71\xf8\x80\x86\x33\x7f\x4f\x6c\xe9\xd1\x5c\xb7\x38\xc0\x9b\xe2\x8a\xb6\xd5\x38\xba\xbf\x7b\xc5\x4e\xbf\x3d\xdb\x53\x4a\x9c\x3c\x10\x85\xe7\x18\x3d\x46\xa5\x8c\xbc\xb0\x15\xb0\xdf\x50\x7a\xad\x0e\xdf\xf3\x54\x8e\xfd", 64); +} + +TEST_LIST = { + { "sha3-224", test_sha3_224 }, + { "sha3-256", test_sha3_256 }, + { "sha3-384", test_sha3_384 }, + { "sha3-512", test_sha3_512 }, + { 0 } +}; + diff --git a/deps/picoquic/picotls/deps/cifra/src/testutil.h b/deps/picoquic/picotls/deps/cifra/src/testutil.h new file mode 100644 index 0000000..b9a52b3 --- /dev/null +++ b/deps/picoquic/picotls/deps/cifra/src/testutil.h @@ -0,0 +1,61 @@ +/* + * cifra - embedded cryptography library + * Written in 2014 by Joseph Birr-Pixton + * + * To the extent possible under law, the author(s) have dedicated all + * copyright and related and neighboring rights to this software to the + * public domain worldwide. This software is distributed without any + * warranty. + * + * You should have received a copy of the CC0 Public Domain Dedication + * along with this software. If not, see + * . + */ + +#ifndef TESTUTIL_H +#define TESTUTIL_H + +#include +#include +#include + +static inline uint8_t unhex_chr(char a) +{ + if (a >= '0' && a <= '9') + return a - '0'; + else if (a >= 'a' && a <= 'f') + return a - 'a' + 10; + else if (a >= 'A' && a <= 'F') + return a - 'A' + 10; + return 0; +} + +static inline size_t unhex(uint8_t *buf, size_t len, const char *str) +{ + size_t used = 0; + + assert(strlen(str) % 2 == 0); + assert(strlen(str) / 2 <= len); + + while (*str) + { + assert(len); + *buf = unhex_chr(str[0]) << 4 | unhex_chr(str[1]); + buf++; + used++; + str += 2; + len--; + } + + return used; +} + +static inline void dump(const char *label, const uint8_t *buf, size_t len) +{ + printf("%s: ", label); + for (size_t i = 0; i < len; i++) + printf("%02x", buf[i]); + printf("\n"); +} + +#endif diff --git a/deps/picoquic/picotls/deps/micro-ecc/.gitignore b/deps/picoquic/picotls/deps/micro-ecc/.gitignore new file mode 100644 index 0000000..cd812eb --- /dev/null +++ b/deps/picoquic/picotls/deps/micro-ecc/.gitignore @@ -0,0 +1,8 @@ +__build__/ +__pycache__ +*.pyc +*.pyo +*.pyd +*.pyz +*.egg-info/ +.DS_Store \ No newline at end of file diff --git a/deps/picoquic/picotls/deps/micro-ecc/LICENSE.txt b/deps/picoquic/picotls/deps/micro-ecc/LICENSE.txt new file mode 100644 index 0000000..ab099ae --- /dev/null +++ b/deps/picoquic/picotls/deps/micro-ecc/LICENSE.txt @@ -0,0 +1,21 @@ +Copyright (c) 2014, Kenneth MacKay +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/deps/picoquic/picotls/deps/micro-ecc/README.md b/deps/picoquic/picotls/deps/micro-ecc/README.md new file mode 100644 index 0000000..01926e3 --- /dev/null +++ b/deps/picoquic/picotls/deps/micro-ecc/README.md @@ -0,0 +1,41 @@ +micro-ecc +========== + +A small and fast ECDH and ECDSA implementation for 8-bit, 32-bit, and 64-bit processors. + +The static version of micro-ecc (ie, where the curve was selected at compile-time) can be found in the "static" branch. + +Features +-------- + + * Resistant to known side-channel attacks. + * Written in C, with optional GCC inline assembly for AVR, ARM and Thumb platforms. + * Supports 8, 32, and 64-bit architectures. + * Small code size. + * No dynamic memory allocation. + * Support for 5 standard curves: secp160r1, secp192r1, secp224r1, secp256r1, and secp256k1. + * BSD 2-clause license. + +Usage Notes +----------- +### Point Representation ### +Compressed points are represented in the standard format as defined in http://www.secg.org/collateral/sec1_final.pdf; uncompressed points are represented in standard format, but without the `0x04` prefix. All functions except `uECC_compress()` only accept uncompressed points; use `uECC_compress()` and `uECC_decompress()` to convert between compressed and uncompressed point representations. + +Private keys are represented in the standard format. + +### Using the Code ### + +I recommend just copying (or symlink) the uECC files into your project. Then just `#include "uECC.h"` to use the micro-ecc functions. + +For use with Arduino, you can just create a symlink to the `uECC` directory in your Arduino `libraries` directory. You can then use uECC just like any other Arduino library (uECC should show up in the **Sketch**=>**Import Library** submenu). + +See uECC.h for documentation for each function. + +### Compilation Notes ### + + * Should compile with any C/C++ compiler that supports stdint.h (this includes Visual Studio 2013). + * If you want to change the defaults for any of the uECC compile-time options (such as `uECC_OPTIMIZATION_LEVEL`), you must change them in your Makefile or similar so that uECC.c is compiled with the desired values (ie, compile uECC.c with `-DuECC_OPTIMIZATION_LEVEL=3` or whatever). + * When compiling for a Thumb-1 platform, you must use the `-fomit-frame-pointer` GCC option (this is enabled by default when compiling with `-O1` or higher). + * When compiling for an ARM/Thumb-2 platform with `uECC_OPTIMIZATION_LEVEL` >= 3, you must use the `-fomit-frame-pointer` GCC option (this is enabled by default when compiling with `-O1` or higher). + * When compiling for AVR, you must have optimizations enabled (compile with `-O1` or higher). + * When building for Windows, you will need to link in the `advapi32.lib` system library. diff --git a/deps/picoquic/picotls/deps/micro-ecc/asm_arm.inc b/deps/picoquic/picotls/deps/micro-ecc/asm_arm.inc new file mode 100644 index 0000000..688fdc7 --- /dev/null +++ b/deps/picoquic/picotls/deps/micro-ecc/asm_arm.inc @@ -0,0 +1,820 @@ +/* Copyright 2015, Kenneth MacKay. Licensed under the BSD 2-clause license. */ + +#ifndef _UECC_ASM_ARM_H_ +#define _UECC_ASM_ARM_H_ + +#if (uECC_SUPPORTS_secp256r1 || uECC_SUPPORTS_secp256k1) + #define uECC_MIN_WORDS 8 +#endif +#if uECC_SUPPORTS_secp224r1 + #undef uECC_MIN_WORDS + #define uECC_MIN_WORDS 7 +#endif +#if uECC_SUPPORTS_secp192r1 + #undef uECC_MIN_WORDS + #define uECC_MIN_WORDS 6 +#endif +#if uECC_SUPPORTS_secp160r1 + #undef uECC_MIN_WORDS + #define uECC_MIN_WORDS 5 +#endif + +#if (uECC_PLATFORM == uECC_arm_thumb) + #define REG_RW "+l" + #define REG_WRITE "=l" +#else + #define REG_RW "+r" + #define REG_WRITE "=r" +#endif + +#if (uECC_PLATFORM == uECC_arm_thumb || uECC_PLATFORM == uECC_arm_thumb2) + #define REG_RW_LO "+l" + #define REG_WRITE_LO "=l" +#else + #define REG_RW_LO "+r" + #define REG_WRITE_LO "=r" +#endif + +#if (uECC_PLATFORM == uECC_arm_thumb2) + #define RESUME_SYNTAX +#else + #define RESUME_SYNTAX ".syntax divided \n\t" +#endif + +#if (uECC_OPTIMIZATION_LEVEL >= 2) + +uECC_VLI_API uECC_word_t uECC_vli_add(uECC_word_t *result, + const uECC_word_t *left, + const uECC_word_t *right, + wordcount_t num_words) { +#if (uECC_MAX_WORDS != uECC_MIN_WORDS) + #if (uECC_PLATFORM == uECC_arm_thumb) || (uECC_PLATFORM == uECC_arm_thumb2) + uint32_t jump = (uECC_MAX_WORDS - num_words) * 4 * 2 + 1; + #else /* ARM */ + uint32_t jump = (uECC_MAX_WORDS - num_words) * 4 * 4; + #endif +#endif + uint32_t carry; + uint32_t left_word; + uint32_t right_word; + + __asm__ volatile ( + ".syntax unified \n\t" + "movs %[carry], #0 \n\t" + #if (uECC_MAX_WORDS != uECC_MIN_WORDS) + "adr %[left], 1f \n\t" + ".align 4 \n\t" + "adds %[jump], %[left] \n\t" + #endif + + "ldmia %[lptr]!, {%[left]} \n\t" + "ldmia %[rptr]!, {%[right]} \n\t" + "adds %[left], %[right] \n\t" + "stmia %[dptr]!, {%[left]} \n\t" + + #if (uECC_MAX_WORDS != uECC_MIN_WORDS) + "bx %[jump] \n\t" + #endif + "1: \n\t" + REPEAT(DEC(uECC_MAX_WORDS), + "ldmia %[lptr]!, {%[left]} \n\t" + "ldmia %[rptr]!, {%[right]} \n\t" + "adcs %[left], %[right] \n\t" + "stmia %[dptr]!, {%[left]} \n\t") + + "adcs %[carry], %[carry] \n\t" + RESUME_SYNTAX + : [dptr] REG_RW_LO (result), [lptr] REG_RW_LO (left), [rptr] REG_RW_LO (right), + #if (uECC_MAX_WORDS != uECC_MIN_WORDS) + [jump] REG_RW_LO (jump), + #endif + [carry] REG_WRITE_LO (carry), [left] REG_WRITE_LO (left_word), + [right] REG_WRITE_LO (right_word) + : + : "cc", "memory" + ); + return carry; +} +#define asm_add 1 + +uECC_VLI_API uECC_word_t uECC_vli_sub(uECC_word_t *result, + const uECC_word_t *left, + const uECC_word_t *right, + wordcount_t num_words) { +#if (uECC_MAX_WORDS != uECC_MIN_WORDS) + #if (uECC_PLATFORM == uECC_arm_thumb) || (uECC_PLATFORM == uECC_arm_thumb2) + uint32_t jump = (uECC_MAX_WORDS - num_words) * 4 * 2 + 1; + #else /* ARM */ + uint32_t jump = (uECC_MAX_WORDS - num_words) * 4 * 4; + #endif +#endif + uint32_t carry; + uint32_t left_word; + uint32_t right_word; + + __asm__ volatile ( + ".syntax unified \n\t" + "movs %[carry], #0 \n\t" + #if (uECC_MAX_WORDS != uECC_MIN_WORDS) + "adr %[left], 1f \n\t" + ".align 4 \n\t" + "adds %[jump], %[left] \n\t" + #endif + + "ldmia %[lptr]!, {%[left]} \n\t" + "ldmia %[rptr]!, {%[right]} \n\t" + "subs %[left], %[right] \n\t" + "stmia %[dptr]!, {%[left]} \n\t" + + #if (uECC_MAX_WORDS != uECC_MIN_WORDS) + "bx %[jump] \n\t" + #endif + "1: \n\t" + REPEAT(DEC(uECC_MAX_WORDS), + "ldmia %[lptr]!, {%[left]} \n\t" + "ldmia %[rptr]!, {%[right]} \n\t" + "sbcs %[left], %[right] \n\t" + "stmia %[dptr]!, {%[left]} \n\t") + + "adcs %[carry], %[carry] \n\t" + RESUME_SYNTAX + : [dptr] REG_RW_LO (result), [lptr] REG_RW_LO (left), [rptr] REG_RW_LO (right), + #if (uECC_MAX_WORDS != uECC_MIN_WORDS) + [jump] REG_RW_LO (jump), + #endif + [carry] REG_WRITE_LO (carry), [left] REG_WRITE_LO (left_word), + [right] REG_WRITE_LO (right_word) + : + : "cc", "memory" + ); + return !carry; /* Note that on ARM, carry flag set means "no borrow" when subtracting + (for some reason...) */ +} +#define asm_sub 1 + +#endif /* (uECC_OPTIMIZATION_LEVEL >= 2) */ + +#if (uECC_OPTIMIZATION_LEVEL >= 3) + +#if (uECC_PLATFORM != uECC_arm_thumb) + +#if uECC_ARM_USE_UMAAL + #include "asm_arm_mult_square_umaal.inc" +#else + #include "asm_arm_mult_square.inc" +#endif + +#if (uECC_OPTIMIZATION_LEVEL == 3) + +uECC_VLI_API void uECC_vli_mult(uint32_t *result, + const uint32_t *left, + const uint32_t *right, + wordcount_t num_words) { + register uint32_t *r0 __asm__("r0") = result; + register const uint32_t *r1 __asm__("r1") = left; + register const uint32_t *r2 __asm__("r2") = right; + register uint32_t r3 __asm__("r3") = num_words; + + __asm__ volatile ( + ".syntax unified \n\t" +#if (uECC_MIN_WORDS == 5) + FAST_MULT_ASM_5 + #if (uECC_MAX_WORDS > 5) + FAST_MULT_ASM_5_TO_6 + #endif + #if (uECC_MAX_WORDS > 6) + FAST_MULT_ASM_6_TO_7 + #endif + #if (uECC_MAX_WORDS > 7) + FAST_MULT_ASM_7_TO_8 + #endif +#elif (uECC_MIN_WORDS == 6) + FAST_MULT_ASM_6 + #if (uECC_MAX_WORDS > 6) + FAST_MULT_ASM_6_TO_7 + #endif + #if (uECC_MAX_WORDS > 7) + FAST_MULT_ASM_7_TO_8 + #endif +#elif (uECC_MIN_WORDS == 7) + FAST_MULT_ASM_7 + #if (uECC_MAX_WORDS > 7) + FAST_MULT_ASM_7_TO_8 + #endif +#elif (uECC_MIN_WORDS == 8) + FAST_MULT_ASM_8 +#endif + "1: \n\t" + RESUME_SYNTAX + : "+r" (r0), "+r" (r1), "+r" (r2) + : "r" (r3) + : "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12", "r14", "cc", "memory" + ); +} +#define asm_mult 1 + +#if uECC_SQUARE_FUNC +uECC_VLI_API void uECC_vli_square(uECC_word_t *result, + const uECC_word_t *left, + wordcount_t num_words) { + register uint32_t *r0 __asm__("r0") = result; + register const uint32_t *r1 __asm__("r1") = left; + register uint32_t r2 __asm__("r2") = num_words; + + __asm__ volatile ( + ".syntax unified \n\t" +#if (uECC_MIN_WORDS == 5) + FAST_SQUARE_ASM_5 + #if (uECC_MAX_WORDS > 5) + FAST_SQUARE_ASM_5_TO_6 + #endif + #if (uECC_MAX_WORDS > 6) + FAST_SQUARE_ASM_6_TO_7 + #endif + #if (uECC_MAX_WORDS > 7) + FAST_SQUARE_ASM_7_TO_8 + #endif +#elif (uECC_MIN_WORDS == 6) + FAST_SQUARE_ASM_6 + #if (uECC_MAX_WORDS > 6) + FAST_SQUARE_ASM_6_TO_7 + #endif + #if (uECC_MAX_WORDS > 7) + FAST_SQUARE_ASM_7_TO_8 + #endif +#elif (uECC_MIN_WORDS == 7) + FAST_SQUARE_ASM_7 + #if (uECC_MAX_WORDS > 7) + FAST_SQUARE_ASM_7_TO_8 + #endif +#elif (uECC_MIN_WORDS == 8) + FAST_SQUARE_ASM_8 +#endif + + "1: \n\t" + RESUME_SYNTAX + : "+r" (r0), "+r" (r1) + : "r" (r2) + : "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12", "r14", "cc", "memory" + ); +} +#define asm_square 1 +#endif /* uECC_SQUARE_FUNC */ + +#else /* (uECC_OPTIMIZATION_LEVEL > 3) */ + +uECC_VLI_API void uECC_vli_mult(uint32_t *result, + const uint32_t *left, + const uint32_t *right, + wordcount_t num_words) { + register uint32_t *r0 __asm__("r0") = result; + register const uint32_t *r1 __asm__("r1") = left; + register const uint32_t *r2 __asm__("r2") = right; + register uint32_t r3 __asm__("r3") = num_words; + +#if uECC_SUPPORTS_secp160r1 + if (num_words == 5) { + __asm__ volatile ( + ".syntax unified \n\t" + FAST_MULT_ASM_5 + RESUME_SYNTAX + : "+r" (r0), "+r" (r1), "+r" (r2) + : "r" (r3) + : "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12", "r14", "cc", "memory" + ); + return; + } +#endif +#if uECC_SUPPORTS_secp192r1 + if (num_words == 6) { + __asm__ volatile ( + ".syntax unified \n\t" + FAST_MULT_ASM_6 + RESUME_SYNTAX + : "+r" (r0), "+r" (r1), "+r" (r2) + : "r" (r3) + : "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12", "r14", "cc", "memory" + ); + return; + } +#endif +#if uECC_SUPPORTS_secp224r1 + if (num_words == 7) { + __asm__ volatile ( + ".syntax unified \n\t" + FAST_MULT_ASM_7 + RESUME_SYNTAX + : "+r" (r0), "+r" (r1), "+r" (r2) + : "r" (r3) + : "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12", "r14", "cc", "memory" + ); + return; + } +#endif +#if (uECC_SUPPORTS_secp256r1 || uECC_SUPPORTS_secp256k1) + if (num_words == 8) { + __asm__ volatile ( + ".syntax unified \n\t" + FAST_MULT_ASM_8 + RESUME_SYNTAX + : "+r" (r0), "+r" (r1), "+r" (r2) + : "r" (r3) + : "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12", "r14", "cc", "memory" + ); + return; + } +#endif +} +#define asm_mult 1 + +#if uECC_SQUARE_FUNC +uECC_VLI_API void uECC_vli_square(uECC_word_t *result, + const uECC_word_t *left, + wordcount_t num_words) { + register uint32_t *r0 __asm__("r0") = result; + register const uint32_t *r1 __asm__("r1") = left; + register uint32_t r2 __asm__("r2") = num_words; + +#if uECC_SUPPORTS_secp160r1 + if (num_words == 5) { + __asm__ volatile ( + ".syntax unified \n\t" + FAST_SQUARE_ASM_5 + RESUME_SYNTAX + : "+r" (r0), "+r" (r1) + : "r" (r2) + : "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12", "r14", "cc", "memory" + ); + return; + } +#endif +#if uECC_SUPPORTS_secp192r1 + if (num_words == 6) { + __asm__ volatile ( + ".syntax unified \n\t" + FAST_SQUARE_ASM_6 + RESUME_SYNTAX + : "+r" (r0), "+r" (r1) + : "r" (r2) + : "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12", "r14", "cc", "memory" + ); + return; + } +#endif +#if uECC_SUPPORTS_secp224r1 + if (num_words == 7) { + __asm__ volatile ( + ".syntax unified \n\t" + FAST_SQUARE_ASM_7 + RESUME_SYNTAX + : "+r" (r0), "+r" (r1) + : "r" (r2) + : "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12", "r14", "cc", "memory" + ); + return; + } +#endif +#if (uECC_SUPPORTS_secp256r1 || uECC_SUPPORTS_secp256k1) + if (num_words == 8) { + __asm__ volatile ( + ".syntax unified \n\t" + FAST_SQUARE_ASM_8 + RESUME_SYNTAX + : "+r" (r0), "+r" (r1) + : "r" (r2) + : "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12", "r14", "cc", "memory" + ); + return; + } +#endif +} +#define asm_square 1 +#endif /* uECC_SQUARE_FUNC */ + +#endif /* (uECC_OPTIMIZATION_LEVEL > 3) */ + +#endif /* uECC_PLATFORM != uECC_arm_thumb */ + +#endif /* (uECC_OPTIMIZATION_LEVEL >= 3) */ + +/* ---- "Small" implementations ---- */ + +#if !asm_add +uECC_VLI_API uECC_word_t uECC_vli_add(uECC_word_t *result, + const uECC_word_t *left, + const uECC_word_t *right, + wordcount_t num_words) { + uint32_t carry = 0; + uint32_t left_word; + uint32_t right_word; + + __asm__ volatile ( + ".syntax unified \n\t" + "1: \n\t" + "ldmia %[lptr]!, {%[left]} \n\t" /* Load left word. */ + "ldmia %[rptr]!, {%[right]} \n\t" /* Load right word. */ + "lsrs %[carry], #1 \n\t" /* Set up carry flag (carry = 0 after this). */ + "adcs %[left], %[left], %[right] \n\t" /* Add with carry. */ + "adcs %[carry], %[carry], %[carry] \n\t" /* Store carry bit. */ + "stmia %[dptr]!, {%[left]} \n\t" /* Store result word. */ + "subs %[ctr], #1 \n\t" /* Decrement counter. */ + "bne 1b \n\t" /* Loop until counter == 0. */ + RESUME_SYNTAX + : [dptr] REG_RW (result), [lptr] REG_RW (left), [rptr] REG_RW (right), + [ctr] REG_RW (num_words), [carry] REG_RW (carry), + [left] REG_WRITE (left_word), [right] REG_WRITE (right_word) + : + : "cc", "memory" + ); + return carry; +} +#define asm_add 1 +#endif + +#if !asm_sub +uECC_VLI_API uECC_word_t uECC_vli_sub(uECC_word_t *result, + const uECC_word_t *left, + const uECC_word_t *right, + wordcount_t num_words) { + uint32_t carry = 1; /* carry = 1 initially (means don't borrow) */ + uint32_t left_word; + uint32_t right_word; + + __asm__ volatile ( + ".syntax unified \n\t" + "1: \n\t" + "ldmia %[lptr]!, {%[left]} \n\t" /* Load left word. */ + "ldmia %[rptr]!, {%[right]} \n\t" /* Load right word. */ + "lsrs %[carry], #1 \n\t" /* Set up carry flag (carry = 0 after this). */ + "sbcs %[left], %[left], %[right] \n\t" /* Subtract with borrow. */ + "adcs %[carry], %[carry], %[carry] \n\t" /* Store carry bit. */ + "stmia %[dptr]!, {%[left]} \n\t" /* Store result word. */ + "subs %[ctr], #1 \n\t" /* Decrement counter. */ + "bne 1b \n\t" /* Loop until counter == 0. */ + RESUME_SYNTAX + : [dptr] REG_RW (result), [lptr] REG_RW (left), [rptr] REG_RW (right), + [ctr] REG_RW (num_words), [carry] REG_RW (carry), + [left] REG_WRITE (left_word), [right] REG_WRITE (right_word) + : + : "cc", "memory" + ); + return !carry; +} +#define asm_sub 1 +#endif + +#if !asm_mult +uECC_VLI_API void uECC_vli_mult(uECC_word_t *result, + const uECC_word_t *left, + const uECC_word_t *right, + wordcount_t num_words) { +#if (uECC_PLATFORM != uECC_arm_thumb) + uint32_t c0 = 0; + uint32_t c1 = 0; + uint32_t c2 = 0; + uint32_t k = 0; + uint32_t i; + uint32_t t0, t1; + + __asm__ volatile ( + ".syntax unified \n\t" + + "1: \n\t" /* outer loop (k < num_words) */ + "movs %[i], #0 \n\t" /* i = 0 */ + "b 3f \n\t" + + "2: \n\t" /* outer loop (k >= num_words) */ + "movs %[i], %[k] \n\t" /* i = k */ + "subs %[i], %[last_word] \n\t" /* i = k - (num_words - 1) (times 4) */ + + "3: \n\t" /* inner loop */ + "subs %[t0], %[k], %[i] \n\t" /* t0 = k-i */ + + "ldr %[t1], [%[right], %[t0]] \n\t" /* t1 = right[k - i] */ + "ldr %[t0], [%[left], %[i]] \n\t" /* t0 = left[i] */ + + "umull %[t0], %[t1], %[t0], %[t1] \n\t" /* (t0, t1) = left[i] * right[k - i] */ + + "adds %[c0], %[c0], %[t0] \n\t" /* add low word to c0 */ + "adcs %[c1], %[c1], %[t1] \n\t" /* add high word to c1, including carry */ + "adcs %[c2], %[c2], #0 \n\t" /* add carry to c2 */ + + "adds %[i], #4 \n\t" /* i += 4 */ + "cmp %[i], %[last_word] \n\t" /* i > (num_words - 1) (times 4)? */ + "bgt 4f \n\t" /* if so, exit the loop */ + "cmp %[i], %[k] \n\t" /* i <= k? */ + "ble 3b \n\t" /* if so, continue looping */ + + "4: \n\t" /* end inner loop */ + + "str %[c0], [%[result], %[k]] \n\t" /* result[k] = c0 */ + "mov %[c0], %[c1] \n\t" /* c0 = c1 */ + "mov %[c1], %[c2] \n\t" /* c1 = c2 */ + "movs %[c2], #0 \n\t" /* c2 = 0 */ + "adds %[k], #4 \n\t" /* k += 4 */ + "cmp %[k], %[last_word] \n\t" /* k <= (num_words - 1) (times 4) ? */ + "ble 1b \n\t" /* if so, loop back, start with i = 0 */ + "cmp %[k], %[last_word], lsl #1 \n\t" /* k <= (num_words * 2 - 2) (times 4) ? */ + "ble 2b \n\t" /* if so, loop back, start with i = (k + 1) - num_words */ + /* end outer loop */ + + "str %[c0], [%[result], %[k]] \n\t" /* result[num_words * 2 - 1] = c0 */ + RESUME_SYNTAX + : [c0] "+r" (c0), [c1] "+r" (c1), [c2] "+r" (c2), + [k] "+r" (k), [i] "=&r" (i), [t0] "=&r" (t0), [t1] "=&r" (t1) + : [result] "r" (result), [left] "r" (left), [right] "r" (right), + [last_word] "r" ((num_words - 1) * 4) + : "cc", "memory" + ); + +#else /* Thumb-1 */ + uint32_t r4, r5, r6, r7; + + __asm__ volatile ( + ".syntax unified \n\t" + "subs %[r3], #1 \n\t" /* r3 = num_words - 1 */ + "lsls %[r3], #2 \n\t" /* r3 = (num_words - 1) * 4 */ + "mov r8, %[r3] \n\t" /* r8 = (num_words - 1) * 4 */ + "lsls %[r3], #1 \n\t" /* r3 = (num_words - 1) * 8 */ + "mov r9, %[r3] \n\t" /* r9 = (num_words - 1) * 8 */ + "movs %[r3], #0 \n\t" /* c0 = 0 */ + "movs %[r4], #0 \n\t" /* c1 = 0 */ + "movs %[r5], #0 \n\t" /* c2 = 0 */ + "movs %[r6], #0 \n\t" /* k = 0 */ + + "push {%[r0]} \n\t" /* keep result on the stack */ + + "1: \n\t" /* outer loop (k < num_words) */ + "movs %[r7], #0 \n\t" /* r7 = i = 0 */ + "b 3f \n\t" + + "2: \n\t" /* outer loop (k >= num_words) */ + "movs %[r7], %[r6] \n\t" /* r7 = k */ + "mov %[r0], r8 \n\t" /* r0 = (num_words - 1) * 4 */ + "subs %[r7], %[r0] \n\t" /* r7 = i = k - (num_words - 1) (times 4) */ + + "3: \n\t" /* inner loop */ + "mov r10, %[r3] \n\t" + "mov r11, %[r4] \n\t" + "mov r12, %[r5] \n\t" + "mov r14, %[r6] \n\t" + "subs %[r0], %[r6], %[r7] \n\t" /* r0 = k - i */ + + "ldr %[r4], [%[r2], %[r0]] \n\t" /* r4 = right[k - i] */ + "ldr %[r0], [%[r1], %[r7]] \n\t" /* r0 = left[i] */ + + "lsrs %[r3], %[r0], #16 \n\t" /* r3 = a1 */ + "uxth %[r0], %[r0] \n\t" /* r0 = a0 */ + + "lsrs %[r5], %[r4], #16 \n\t" /* r5 = b1 */ + "uxth %[r4], %[r4] \n\t" /* r4 = b0 */ + + "movs %[r6], %[r3] \n\t" /* r6 = a1 */ + "muls %[r6], %[r5], %[r6] \n\t" /* r6 = a1 * b1 */ + "muls %[r3], %[r4], %[r3] \n\t" /* r3 = b0 * a1 */ + "muls %[r5], %[r0], %[r5] \n\t" /* r5 = a0 * b1 */ + "muls %[r0], %[r4], %[r0] \n\t" /* r0 = a0 * b0 */ + + /* Add middle terms */ + "lsls %[r4], %[r3], #16 \n\t" + "lsrs %[r3], %[r3], #16 \n\t" + "adds %[r0], %[r4] \n\t" + "adcs %[r6], %[r3] \n\t" + + "lsls %[r4], %[r5], #16 \n\t" + "lsrs %[r5], %[r5], #16 \n\t" + "adds %[r0], %[r4] \n\t" + "adcs %[r6], %[r5] \n\t" + + "mov %[r3], r10\n\t" + "mov %[r4], r11\n\t" + "mov %[r5], r12\n\t" + "adds %[r3], %[r0] \n\t" /* add low word to c0 */ + "adcs %[r4], %[r6] \n\t" /* add high word to c1, including carry */ + "movs %[r0], #0 \n\t" /* r0 = 0 (does not affect carry bit) */ + "adcs %[r5], %[r0] \n\t" /* add carry to c2 */ + + "mov %[r6], r14\n\t" /* r6 = k */ + + "adds %[r7], #4 \n\t" /* i += 4 */ + "cmp %[r7], r8 \n\t" /* i > (num_words - 1) (times 4)? */ + "bgt 4f \n\t" /* if so, exit the loop */ + "cmp %[r7], %[r6] \n\t" /* i <= k? */ + "ble 3b \n\t" /* if so, continue looping */ + + "4: \n\t" /* end inner loop */ + + "ldr %[r0], [sp, #0] \n\t" /* r0 = result */ + + "str %[r3], [%[r0], %[r6]] \n\t" /* result[k] = c0 */ + "mov %[r3], %[r4] \n\t" /* c0 = c1 */ + "mov %[r4], %[r5] \n\t" /* c1 = c2 */ + "movs %[r5], #0 \n\t" /* c2 = 0 */ + "adds %[r6], #4 \n\t" /* k += 4 */ + "cmp %[r6], r8 \n\t" /* k <= (num_words - 1) (times 4) ? */ + "ble 1b \n\t" /* if so, loop back, start with i = 0 */ + "cmp %[r6], r9 \n\t" /* k <= (num_words * 2 - 2) (times 4) ? */ + "ble 2b \n\t" /* if so, loop back, with i = (k + 1) - num_words */ + /* end outer loop */ + + "str %[r3], [%[r0], %[r6]] \n\t" /* result[num_words * 2 - 1] = c0 */ + "pop {%[r0]} \n\t" /* pop result off the stack */ + + ".syntax divided \n\t" + : [r3] "+l" (num_words), [r4] "=&l" (r4), + [r5] "=&l" (r5), [r6] "=&l" (r6), [r7] "=&l" (r7) + : [r0] "l" (result), [r1] "l" (left), [r2] "l" (right) + : "r8", "r9", "r10", "r11", "r12", "r14", "cc", "memory" + ); +#endif +} +#define asm_mult 1 +#endif + +#if uECC_SQUARE_FUNC +#if !asm_square +uECC_VLI_API void uECC_vli_square(uECC_word_t *result, + const uECC_word_t *left, + wordcount_t num_words) { +#if (uECC_PLATFORM != uECC_arm_thumb) + uint32_t c0 = 0; + uint32_t c1 = 0; + uint32_t c2 = 0; + uint32_t k = 0; + uint32_t i, tt; + uint32_t t0, t1; + + __asm__ volatile ( + ".syntax unified \n\t" + + "1: \n\t" /* outer loop (k < num_words) */ + "movs %[i], #0 \n\t" /* i = 0 */ + "b 3f \n\t" + + "2: \n\t" /* outer loop (k >= num_words) */ + "movs %[i], %[k] \n\t" /* i = k */ + "subs %[i], %[last_word] \n\t" /* i = k - (num_words - 1) (times 4) */ + + "3: \n\t" /* inner loop */ + "subs %[tt], %[k], %[i] \n\t" /* tt = k-i */ + + "ldr %[t1], [%[left], %[tt]] \n\t" /* t1 = left[k - i] */ + "ldr %[t0], [%[left], %[i]] \n\t" /* t0 = left[i] */ + + "umull %[t0], %[t1], %[t0], %[t1] \n\t" /* (t0, t1) = left[i] * right[k - i] */ + + "cmp %[i], %[tt] \n\t" /* (i < k - i) ? */ + "bge 4f \n\t" /* if i >= k - i, skip */ + "adds %[c0], %[c0], %[t0] \n\t" /* add low word to c0 */ + "adcs %[c1], %[c1], %[t1] \n\t" /* add high word to c1, including carry */ + "adcs %[c2], %[c2], #0 \n\t" /* add carry to c2 */ + + "4: \n\t" + "adds %[c0], %[c0], %[t0] \n\t" /* add low word to c0 */ + "adcs %[c1], %[c1], %[t1] \n\t" /* add high word to c1, including carry */ + "adcs %[c2], %[c2], #0 \n\t" /* add carry to c2 */ + + "adds %[i], #4 \n\t" /* i += 4 */ + "cmp %[i], %[k] \n\t" /* i >= k? */ + "bge 5f \n\t" /* if so, exit the loop */ + "subs %[tt], %[k], %[i] \n\t" /* tt = k - i */ + "cmp %[i], %[tt] \n\t" /* i <= k - i? */ + "ble 3b \n\t" /* if so, continue looping */ + + "5: \n\t" /* end inner loop */ + + "str %[c0], [%[result], %[k]] \n\t" /* result[k] = c0 */ + "mov %[c0], %[c1] \n\t" /* c0 = c1 */ + "mov %[c1], %[c2] \n\t" /* c1 = c2 */ + "movs %[c2], #0 \n\t" /* c2 = 0 */ + "adds %[k], #4 \n\t" /* k += 4 */ + "cmp %[k], %[last_word] \n\t" /* k <= (num_words - 1) (times 4) ? */ + "ble 1b \n\t" /* if so, loop back, start with i = 0 */ + "cmp %[k], %[last_word], lsl #1 \n\t" /* k <= (num_words * 2 - 2) (times 4) ? */ + "ble 2b \n\t" /* if so, loop back, start with i = (k + 1) - num_words */ + /* end outer loop */ + + "str %[c0], [%[result], %[k]] \n\t" /* result[num_words * 2 - 1] = c0 */ + RESUME_SYNTAX + : [c0] "+r" (c0), [c1] "+r" (c1), [c2] "+r" (c2), + [k] "+r" (k), [i] "=&r" (i), [tt] "=&r" (tt), [t0] "=&r" (t0), [t1] "=&r" (t1) + : [result] "r" (result), [left] "r" (left), [last_word] "r" ((num_words - 1) * 4) + : "cc", "memory" + ); + +#else + uint32_t r3, r4, r5, r6, r7; + + __asm__ volatile ( + ".syntax unified \n\t" + "subs %[r2], #1 \n\t" /* r2 = num_words - 1 */ + "lsls %[r2], #2 \n\t" /* r2 = (num_words - 1) * 4 */ + "mov r8, %[r2] \n\t" /* r8 = (num_words - 1) * 4 */ + "lsls %[r2], #1 \n\t" /* r2 = (num_words - 1) * 8 */ + "mov r9, %[r2] \n\t" /* r9 = (num_words - 1) * 8 */ + "movs %[r2], #0 \n\t" /* c0 = 0 */ + "movs %[r3], #0 \n\t" /* c1 = 0 */ + "movs %[r4], #0 \n\t" /* c2 = 0 */ + "movs %[r5], #0 \n\t" /* k = 0 */ + + "push {%[r0]} \n\t" /* keep result on the stack */ + + "1: \n\t" /* outer loop (k < num_words) */ + "movs %[r6], #0 \n\t" /* r6 = i = 0 */ + "b 3f \n\t" + + "2: \n\t" /* outer loop (k >= num_words) */ + "movs %[r6], %[r5] \n\t" /* r6 = k */ + "mov %[r0], r8 \n\t" /* r0 = (num_words - 1) * 4 */ + "subs %[r6], %[r0] \n\t" /* r6 = i = k - (num_words - 1) (times 4) */ + + "3: \n\t" /* inner loop */ + "mov r10, %[r2] \n\t" + "mov r11, %[r3] \n\t" + "mov r12, %[r4] \n\t" + "mov r14, %[r5] \n\t" + "subs %[r7], %[r5], %[r6] \n\t" /* r7 = k - i */ + + "ldr %[r3], [%[r1], %[r7]] \n\t" /* r3 = left[k - i] */ + "ldr %[r0], [%[r1], %[r6]] \n\t" /* r0 = left[i] */ + + "lsrs %[r2], %[r0], #16 \n\t" /* r2 = a1 */ + "uxth %[r0], %[r0] \n\t" /* r0 = a0 */ + + "lsrs %[r4], %[r3], #16 \n\t" /* r4 = b1 */ + "uxth %[r3], %[r3] \n\t" /* r3 = b0 */ + + "movs %[r5], %[r2] \n\t" /* r5 = a1 */ + "muls %[r5], %[r4], %[r5] \n\t" /* r5 = a1 * b1 */ + "muls %[r2], %[r3], %[r2] \n\t" /* r2 = b0 * a1 */ + "muls %[r4], %[r0], %[r4] \n\t" /* r4 = a0 * b1 */ + "muls %[r0], %[r3], %[r0] \n\t" /* r0 = a0 * b0 */ + + /* Add middle terms */ + "lsls %[r3], %[r2], #16 \n\t" + "lsrs %[r2], %[r2], #16 \n\t" + "adds %[r0], %[r3] \n\t" + "adcs %[r5], %[r2] \n\t" + + "lsls %[r3], %[r4], #16 \n\t" + "lsrs %[r4], %[r4], #16 \n\t" + "adds %[r0], %[r3] \n\t" + "adcs %[r5], %[r4] \n\t" + + /* Add to acc, doubling if necessary */ + "mov %[r2], r10\n\t" + "mov %[r3], r11\n\t" + "mov %[r4], r12\n\t" + + "cmp %[r6], %[r7] \n\t" /* (i < k - i) ? */ + "bge 4f \n\t" /* if i >= k - i, skip */ + "movs %[r7], #0 \n\t" /* r7 = 0 */ + "adds %[r2], %[r0] \n\t" /* add low word to c0 */ + "adcs %[r3], %[r5] \n\t" /* add high word to c1, including carry */ + "adcs %[r4], %[r7] \n\t" /* add carry to c2 */ + "4: \n\t" + "movs %[r7], #0 \n\t" /* r7 = 0 */ + "adds %[r2], %[r0] \n\t" /* add low word to c0 */ + "adcs %[r3], %[r5] \n\t" /* add high word to c1, including carry */ + "adcs %[r4], %[r7] \n\t" /* add carry to c2 */ + + "mov %[r5], r14\n\t" /* r5 = k */ + + "adds %[r6], #4 \n\t" /* i += 4 */ + "cmp %[r6], %[r5] \n\t" /* i >= k? */ + "bge 5f \n\t" /* if so, exit the loop */ + "subs %[r7], %[r5], %[r6] \n\t" /* r7 = k - i */ + "cmp %[r6], %[r7] \n\t" /* i <= k - i? */ + "ble 3b \n\t" /* if so, continue looping */ + + "5: \n\t" /* end inner loop */ + + "ldr %[r0], [sp, #0] \n\t" /* r0 = result */ + + "str %[r2], [%[r0], %[r5]] \n\t" /* result[k] = c0 */ + "mov %[r2], %[r3] \n\t" /* c0 = c1 */ + "mov %[r3], %[r4] \n\t" /* c1 = c2 */ + "movs %[r4], #0 \n\t" /* c2 = 0 */ + "adds %[r5], #4 \n\t" /* k += 4 */ + "cmp %[r5], r8 \n\t" /* k <= (num_words - 1) (times 4) ? */ + "ble 1b \n\t" /* if so, loop back, start with i = 0 */ + "cmp %[r5], r9 \n\t" /* k <= (num_words * 2 - 2) (times 4) ? */ + "ble 2b \n\t" /* if so, loop back, with i = (k + 1) - num_words */ + /* end outer loop */ + + "str %[r2], [%[r0], %[r5]] \n\t" /* result[num_words * 2 - 1] = c0 */ + "pop {%[r0]} \n\t" /* pop result off the stack */ + + ".syntax divided \n\t" + : [r2] "+l" (num_words), [r3] "=&l" (r3), [r4] "=&l" (r4), + [r5] "=&l" (r5), [r6] "=&l" (r6), [r7] "=&l" (r7) + : [r0] "l" (result), [r1] "l" (left) + : "r8", "r9", "r10", "r11", "r12", "r14", "cc", "memory" + ); +#endif +} +#define asm_square 1 +#endif +#endif /* uECC_SQUARE_FUNC */ + +#endif /* _UECC_ASM_ARM_H_ */ diff --git a/deps/picoquic/picotls/deps/micro-ecc/asm_arm_mult_square.inc b/deps/picoquic/picotls/deps/micro-ecc/asm_arm_mult_square.inc new file mode 100644 index 0000000..8907fc1 --- /dev/null +++ b/deps/picoquic/picotls/deps/micro-ecc/asm_arm_mult_square.inc @@ -0,0 +1,2311 @@ +/* Copyright 2015, Kenneth MacKay. Licensed under the BSD 2-clause license. */ + +#ifndef _UECC_ASM_ARM_MULT_SQUARE_H_ +#define _UECC_ASM_ARM_MULT_SQUARE_H_ + +#define FAST_MULT_ASM_5 \ + "push {r3} \n\t" \ + "add r0, 12 \n\t" \ + "add r2, 12 \n\t" \ + "ldmia r1!, {r3,r4} \n\t" \ + "ldmia r2!, {r6,r7} \n\t" \ + \ + "umull r11, r12, r3, r6 \n\t" \ + "stmia r0!, {r11} \n\t" \ + \ + "mov r10, #0 \n\t" \ + "umull r11, r9, r3, r7 \n\t" \ + "adds r12, r12, r11 \n\t" \ + "adc r9, r9, #0 \n\t" \ + "umull r11, r14, r4, r6 \n\t" \ + "adds r12, r12, r11 \n\t" \ + "adcs r9, r9, r14 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "stmia r0!, {r12} \n\t" \ + \ + "umull r12, r14, r4, r7 \n\t" \ + "adds r9, r9, r12 \n\t" \ + "adc r10, r10, r14 \n\t" \ + "stmia r0!, {r9, r10} \n\t" \ + \ + "sub r0, 28 \n\t" \ + "sub r2, 20 \n\t" \ + "ldmia r2!, {r6,r7,r8} \n\t" \ + "ldmia r1!, {r5} \n\t" \ + \ + "umull r11, r12, r3, r6 \n\t" \ + "stmia r0!, {r11} \n\t" \ + \ + "mov r10, #0 \n\t" \ + "umull r11, r9, r3, r7 \n\t" \ + "adds r12, r12, r11 \n\t" \ + "adc r9, r9, #0 \n\t" \ + "umull r11, r14, r4, r6 \n\t" \ + "adds r12, r12, r11 \n\t" \ + "adcs r9, r9, r14 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "stmia r0!, {r12} \n\t" \ + \ + "mov r11, #0 \n\t" \ + "umull r12, r14, r3, r8 \n\t" \ + "adds r9, r9, r12 \n\t" \ + "adcs r10, r10, r14 \n\t" \ + "adc r11, r11, #0 \n\t" \ + "umull r12, r14, r4, r7 \n\t" \ + "adds r9, r9, r12 \n\t" \ + "adcs r10, r10, r14 \n\t" \ + "adc r11, r11, #0 \n\t" \ + "umull r12, r14, r5, r6 \n\t" \ + "adds r9, r9, r12 \n\t" \ + "adcs r10, r10, r14 \n\t" \ + "adc r11, r11, #0 \n\t" \ + "stmia r0!, {r9} \n\t" \ + \ + "ldmia r1!, {r3} \n\t" \ + "mov r12, #0 \n\t" \ + "umull r14, r9, r4, r8 \n\t" \ + "adds r10, r10, r14 \n\t" \ + "adcs r11, r11, r9 \n\t" \ + "adc r12, r12, #0 \n\t" \ + "umull r14, r9, r5, r7 \n\t" \ + "adds r10, r10, r14 \n\t" \ + "adcs r11, r11, r9 \n\t" \ + "adc r12, r12, #0 \n\t" \ + "umull r14, r9, r3, r6 \n\t" \ + "adds r10, r10, r14 \n\t" \ + "adcs r11, r11, r9 \n\t" \ + "adc r12, r12, #0 \n\t" \ + "ldr r14, [r0] \n\t" \ + "adds r10, r10, r14 \n\t" \ + "adcs r11, r11, #0 \n\t" \ + "adc r12, r12, #0 \n\t" \ + "stmia r0!, {r10} \n\t" \ + \ + "ldmia r1!, {r4} \n\t" \ + "mov r14, #0 \n\t" \ + "umull r9, r10, r5, r8 \n\t" \ + "adds r11, r11, r9 \n\t" \ + "adcs r12, r12, r10 \n\t" \ + "adc r14, r14, #0 \n\t" \ + "umull r9, r10, r3, r7 \n\t" \ + "adds r11, r11, r9 \n\t" \ + "adcs r12, r12, r10 \n\t" \ + "adc r14, r14, #0 \n\t" \ + "umull r9, r10, r4, r6 \n\t" \ + "adds r11, r11, r9 \n\t" \ + "adcs r12, r12, r10 \n\t" \ + "adc r14, r14, #0 \n\t" \ + "ldr r9, [r0] \n\t" \ + "adds r11, r11, r9 \n\t" \ + "adcs r12, r12, #0 \n\t" \ + "adc r14, r14, #0 \n\t" \ + "stmia r0!, {r11} \n\t" \ + \ + "ldmia r2!, {r6} \n\t" \ + "mov r9, #0 \n\t" \ + "umull r10, r11, r5, r6 \n\t" \ + "adds r12, r12, r10 \n\t" \ + "adcs r14, r14, r11 \n\t" \ + "adc r9, r9, #0 \n\t" \ + "umull r10, r11, r3, r8 \n\t" \ + "adds r12, r12, r10 \n\t" \ + "adcs r14, r14, r11 \n\t" \ + "adc r9, r9, #0 \n\t" \ + "umull r10, r11, r4, r7 \n\t" \ + "adds r12, r12, r10 \n\t" \ + "adcs r14, r14, r11 \n\t" \ + "adc r9, r9, #0 \n\t" \ + "ldr r10, [r0] \n\t" \ + "adds r12, r12, r10 \n\t" \ + "adcs r14, r14, #0 \n\t" \ + "adc r9, r9, #0 \n\t" \ + "stmia r0!, {r12} \n\t" \ + \ + "ldmia r2!, {r7} \n\t" \ + "mov r10, #0 \n\t" \ + "umull r11, r12, r5, r7 \n\t" \ + "adds r14, r14, r11 \n\t" \ + "adcs r9, r9, r12 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "umull r11, r12, r3, r6 \n\t" \ + "adds r14, r14, r11 \n\t" \ + "adcs r9, r9, r12 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "umull r11, r12, r4, r8 \n\t" \ + "adds r14, r14, r11 \n\t" \ + "adcs r9, r9, r12 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "ldr r11, [r0] \n\t" \ + "adds r14, r14, r11 \n\t" \ + "adcs r9, r9, #0 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "stmia r0!, {r14} \n\t" \ + \ + "mov r11, #0 \n\t" \ + "umull r12, r14, r3, r7 \n\t" \ + "adds r9, r9, r12 \n\t" \ + "adcs r10, r10, r14 \n\t" \ + "adc r11, r11, #0 \n\t" \ + "umull r12, r14, r4, r6 \n\t" \ + "adds r9, r9, r12 \n\t" \ + "adcs r10, r10, r14 \n\t" \ + "adc r11, r11, #0 \n\t" \ + "stmia r0!, {r9} \n\t" \ + \ + "umull r14, r9, r4, r7 \n\t" \ + "adds r10, r10, r14 \n\t" \ + "adc r11, r11, r9 \n\t" \ + "stmia r0!, {r10, r11} \n\t" \ + "pop {r3} \n\t" + +#define FAST_MULT_ASM_5_TO_6 \ + "cmp r3, #5 \n\t" \ + "beq 1f \n\t" \ + \ + /* r4 = left high, r5 = right high */ \ + "ldr r4, [r1] \n\t" \ + "ldr r5, [r2] \n\t" \ + \ + "sub r0, #20 \n\t" \ + "sub r1, #20 \n\t" \ + "sub r2, #20 \n\t" \ + \ + "ldr r6, [r0] \n\t" \ + "ldr r7, [r1], #4 \n\t" \ + "ldr r8, [r2], #4 \n\t" \ + "mov r14, #0 \n\t" \ + "umull r9, r10, r4, r8 \n\t" \ + "umull r11, r12, r5, r7 \n\t" \ + "adds r9, r9, r6 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "adds r9, r9, r11 \n\t" \ + "adcs r10, r10, r12 \n\t" \ + "adc r14, r14, #0 \n\t" \ + "str r9, [r0], #4 \n\t" \ + \ + "ldr r6, [r0] \n\t" \ + "adds r10, r10, r6 \n\t" \ + "adcs r14, r14, #0 \n\t" \ + "ldr r7, [r1], #4 \n\t" \ + "ldr r8, [r2], #4 \n\t" \ + "mov r9, #0 \n\t" \ + "umull r11, r12, r4, r8 \n\t" \ + "adds r10, r10, r11 \n\t" \ + "adcs r14, r14, r12 \n\t" \ + "adc r9, r9, #0 \n\t" \ + "umull r11, r12, r5, r7 \n\t" \ + "adds r10, r10, r11 \n\t" \ + "adcs r14, r14, r12 \n\t" \ + "adc r9, r9, #0 \n\t" \ + "str r10, [r0], #4 \n\t" \ + \ + "ldr r6, [r0] \n\t" \ + "adds r14, r14, r6 \n\t" \ + "adcs r9, r9, #0 \n\t" \ + "ldr r7, [r1], #4 \n\t" \ + "ldr r8, [r2], #4 \n\t" \ + "mov r10, #0 \n\t" \ + "umull r11, r12, r4, r8 \n\t" \ + "adds r14, r14, r11 \n\t" \ + "adcs r9, r9, r12 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "umull r11, r12, r5, r7 \n\t" \ + "adds r14, r14, r11 \n\t" \ + "adcs r9, r9, r12 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "str r14, [r0], #4 \n\t" \ + \ + "ldr r6, [r0] \n\t" \ + "adds r9, r9, r6 \n\t" \ + "adcs r10, r10, #0 \n\t" \ + "ldr r7, [r1], #4 \n\t" \ + "ldr r8, [r2], #4 \n\t" \ + "mov r14, #0 \n\t" \ + "umull r11, r12, r4, r8 \n\t" \ + "adds r9, r9, r11 \n\t" \ + "adcs r10, r10, r12 \n\t" \ + "adc r14, r14, #0 \n\t" \ + "umull r11, r12, r5, r7 \n\t" \ + "adds r9, r9, r11 \n\t" \ + "adcs r10, r10, r12 \n\t" \ + "adc r14, r14, #0 \n\t" \ + "str r9, [r0], #4 \n\t" \ + \ + "ldr r6, [r0] \n\t" \ + "adds r10, r10, r6 \n\t" \ + "adcs r14, r14, #0 \n\t" \ + /* skip past already-loaded (r4, r5) */ \ + "ldr r7, [r1], #8 \n\t" \ + "ldr r8, [r2], #8 \n\t" \ + "mov r9, #0 \n\t" \ + "umull r11, r12, r4, r8 \n\t" \ + "adds r10, r10, r11 \n\t" \ + "adcs r14, r14, r12 \n\t" \ + "adc r9, r9, #0 \n\t" \ + "umull r11, r12, r5, r7 \n\t" \ + "adds r10, r10, r11 \n\t" \ + "adcs r14, r14, r12 \n\t" \ + "adc r9, r9, #0 \n\t" \ + "str r10, [r0], #4 \n\t" \ + \ + "umull r11, r12, r4, r5 \n\t" \ + "adds r11, r11, r14 \n\t" \ + "adc r12, r12, r9 \n\t" \ + "stmia r0!, {r11, r12} \n\t" + +#define FAST_MULT_ASM_6 \ + "push {r3} \n\t" \ + "add r0, 12 \n\t" \ + "add r2, 12 \n\t" \ + "ldmia r1!, {r3,r4,r5} \n\t" \ + "ldmia r2!, {r6,r7,r8} \n\t" \ + \ + "umull r11, r12, r3, r6 \n\t" \ + "stmia r0!, {r11} \n\t" \ + \ + "mov r10, #0 \n\t" \ + "umull r11, r9, r3, r7 \n\t" \ + "adds r12, r12, r11 \n\t" \ + "adc r9, r9, #0 \n\t" \ + "umull r11, r14, r4, r6 \n\t" \ + "adds r12, r12, r11 \n\t" \ + "adcs r9, r9, r14 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "stmia r0!, {r12} \n\t" \ + \ + "mov r11, #0 \n\t" \ + "umull r12, r14, r3, r8 \n\t" \ + "adds r9, r9, r12 \n\t" \ + "adcs r10, r10, r14 \n\t" \ + "adc r11, r11, #0 \n\t" \ + "umull r12, r14, r4, r7 \n\t" \ + "adds r9, r9, r12 \n\t" \ + "adcs r10, r10, r14 \n\t" \ + "adc r11, r11, #0 \n\t" \ + "umull r12, r14, r5, r6 \n\t" \ + "adds r9, r9, r12 \n\t" \ + "adcs r10, r10, r14 \n\t" \ + "adc r11, r11, #0 \n\t" \ + "stmia r0!, {r9} \n\t" \ + \ + "mov r12, #0 \n\t" \ + "umull r14, r9, r4, r8 \n\t" \ + "adds r10, r10, r14 \n\t" \ + "adcs r11, r11, r9 \n\t" \ + "adc r12, r12, #0 \n\t" \ + "umull r14, r9, r5, r7 \n\t" \ + "adds r10, r10, r14 \n\t" \ + "adcs r11, r11, r9 \n\t" \ + "adc r12, r12, #0 \n\t" \ + "stmia r0!, {r10} \n\t" \ + \ + "umull r9, r10, r5, r8 \n\t" \ + "adds r11, r11, r9 \n\t" \ + "adc r12, r12, r10 \n\t" \ + "stmia r0!, {r11, r12} \n\t" \ + \ + "sub r0, 36 \n\t" \ + "sub r2, 24 \n\t" \ + "ldmia r2!, {r6,r7,r8} \n\t" \ + \ + "umull r11, r12, r3, r6 \n\t" \ + "stmia r0!, {r11} \n\t" \ + \ + "mov r10, #0 \n\t" \ + "umull r11, r9, r3, r7 \n\t" \ + "adds r12, r12, r11 \n\t" \ + "adc r9, r9, #0 \n\t" \ + "umull r11, r14, r4, r6 \n\t" \ + "adds r12, r12, r11 \n\t" \ + "adcs r9, r9, r14 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "stmia r0!, {r12} \n\t" \ + \ + "mov r11, #0 \n\t" \ + "umull r12, r14, r3, r8 \n\t" \ + "adds r9, r9, r12 \n\t" \ + "adcs r10, r10, r14 \n\t" \ + "adc r11, r11, #0 \n\t" \ + "umull r12, r14, r4, r7 \n\t" \ + "adds r9, r9, r12 \n\t" \ + "adcs r10, r10, r14 \n\t" \ + "adc r11, r11, #0 \n\t" \ + "umull r12, r14, r5, r6 \n\t" \ + "adds r9, r9, r12 \n\t" \ + "adcs r10, r10, r14 \n\t" \ + "adc r11, r11, #0 \n\t" \ + "stmia r0!, {r9} \n\t" \ + \ + "ldmia r1!, {r3} \n\t" \ + "mov r12, #0 \n\t" \ + "umull r14, r9, r4, r8 \n\t" \ + "adds r10, r10, r14 \n\t" \ + "adcs r11, r11, r9 \n\t" \ + "adc r12, r12, #0 \n\t" \ + "umull r14, r9, r5, r7 \n\t" \ + "adds r10, r10, r14 \n\t" \ + "adcs r11, r11, r9 \n\t" \ + "adc r12, r12, #0 \n\t" \ + "umull r14, r9, r3, r6 \n\t" \ + "adds r10, r10, r14 \n\t" \ + "adcs r11, r11, r9 \n\t" \ + "adc r12, r12, #0 \n\t" \ + "ldr r14, [r0] \n\t" \ + "adds r10, r10, r14 \n\t" \ + "adcs r11, r11, #0 \n\t" \ + "adc r12, r12, #0 \n\t" \ + "stmia r0!, {r10} \n\t" \ + \ + "ldmia r1!, {r4} \n\t" \ + "mov r14, #0 \n\t" \ + "umull r9, r10, r5, r8 \n\t" \ + "adds r11, r11, r9 \n\t" \ + "adcs r12, r12, r10 \n\t" \ + "adc r14, r14, #0 \n\t" \ + "umull r9, r10, r3, r7 \n\t" \ + "adds r11, r11, r9 \n\t" \ + "adcs r12, r12, r10 \n\t" \ + "adc r14, r14, #0 \n\t" \ + "umull r9, r10, r4, r6 \n\t" \ + "adds r11, r11, r9 \n\t" \ + "adcs r12, r12, r10 \n\t" \ + "adc r14, r14, #0 \n\t" \ + "ldr r9, [r0] \n\t" \ + "adds r11, r11, r9 \n\t" \ + "adcs r12, r12, #0 \n\t" \ + "adc r14, r14, #0 \n\t" \ + "stmia r0!, {r11} \n\t" \ + \ + "ldmia r1!, {r5} \n\t" \ + "mov r9, #0 \n\t" \ + "umull r10, r11, r3, r8 \n\t" \ + "adds r12, r12, r10 \n\t" \ + "adcs r14, r14, r11 \n\t" \ + "adc r9, r9, #0 \n\t" \ + "umull r10, r11, r4, r7 \n\t" \ + "adds r12, r12, r10 \n\t" \ + "adcs r14, r14, r11 \n\t" \ + "adc r9, r9, #0 \n\t" \ + "umull r10, r11, r5, r6 \n\t" \ + "adds r12, r12, r10 \n\t" \ + "adcs r14, r14, r11 \n\t" \ + "adc r9, r9, #0 \n\t" \ + "ldr r10, [r0] \n\t" \ + "adds r12, r12, r10 \n\t" \ + "adcs r14, r14, #0 \n\t" \ + "adc r9, r9, #0 \n\t" \ + "stmia r0!, {r12} \n\t" \ + \ + "ldmia r2!, {r6} \n\t" \ + "mov r10, #0 \n\t" \ + "umull r11, r12, r3, r6 \n\t" \ + "adds r14, r14, r11 \n\t" \ + "adcs r9, r9, r12 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "umull r11, r12, r4, r8 \n\t" \ + "adds r14, r14, r11 \n\t" \ + "adcs r9, r9, r12 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "umull r11, r12, r5, r7 \n\t" \ + "adds r14, r14, r11 \n\t" \ + "adcs r9, r9, r12 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "ldr r11, [r0] \n\t" \ + "adds r14, r14, r11 \n\t" \ + "adcs r9, r9, #0 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "stmia r0!, {r14} \n\t" \ + \ + "ldmia r2!, {r7} \n\t" \ + "mov r11, #0 \n\t" \ + "umull r12, r14, r3, r7 \n\t" \ + "adds r9, r9, r12 \n\t" \ + "adcs r10, r10, r14 \n\t" \ + "adc r11, r11, #0 \n\t" \ + "umull r12, r14, r4, r6 \n\t" \ + "adds r9, r9, r12 \n\t" \ + "adcs r10, r10, r14 \n\t" \ + "adc r11, r11, #0 \n\t" \ + "umull r12, r14, r5, r8 \n\t" \ + "adds r9, r9, r12 \n\t" \ + "adcs r10, r10, r14 \n\t" \ + "adc r11, r11, #0 \n\t" \ + "ldr r12, [r0] \n\t" \ + "adds r9, r9, r12 \n\t" \ + "adcs r10, r10, #0 \n\t" \ + "adc r11, r11, #0 \n\t" \ + "stmia r0!, {r9} \n\t" \ + \ + "ldmia r2!, {r8} \n\t" \ + "mov r12, #0 \n\t" \ + "umull r14, r9, r3, r8 \n\t" \ + "adds r10, r10, r14 \n\t" \ + "adcs r11, r11, r9 \n\t" \ + "adc r12, r12, #0 \n\t" \ + "umull r14, r9, r4, r7 \n\t" \ + "adds r10, r10, r14 \n\t" \ + "adcs r11, r11, r9 \n\t" \ + "adc r12, r12, #0 \n\t" \ + "umull r14, r9, r5, r6 \n\t" \ + "adds r10, r10, r14 \n\t" \ + "adcs r11, r11, r9 \n\t" \ + "adc r12, r12, #0 \n\t" \ + "ldr r14, [r0] \n\t" \ + "adds r10, r10, r14 \n\t" \ + "adcs r11, r11, #0 \n\t" \ + "adc r12, r12, #0 \n\t" \ + "stmia r0!, {r10} \n\t" \ + \ + "mov r14, #0 \n\t" \ + "umull r9, r10, r4, r8 \n\t" \ + "adds r11, r11, r9 \n\t" \ + "adcs r12, r12, r10 \n\t" \ + "adc r14, r14, #0 \n\t" \ + "umull r9, r10, r5, r7 \n\t" \ + "adds r11, r11, r9 \n\t" \ + "adcs r12, r12, r10 \n\t" \ + "adc r14, r14, #0 \n\t" \ + "stmia r0!, {r11} \n\t" \ + \ + "umull r10, r11, r5, r8 \n\t" \ + "adds r12, r12, r10 \n\t" \ + "adc r14, r14, r11 \n\t" \ + "stmia r0!, {r12, r14} \n\t" \ + "pop {r3} \n\t" + +#define FAST_MULT_ASM_6_TO_7 \ + "cmp r3, #6 \n\t" \ + "beq 1f \n\t" \ + \ + /* r4 = left high, r5 = right high */ \ + "ldr r4, [r1] \n\t" \ + "ldr r5, [r2] \n\t" \ + \ + "sub r0, #24 \n\t" \ + "sub r1, #24 \n\t" \ + "sub r2, #24 \n\t" \ + \ + "ldr r6, [r0] \n\t" \ + "ldr r7, [r1], #4 \n\t" \ + "ldr r8, [r2], #4 \n\t" \ + "mov r14, #0 \n\t" \ + "umull r9, r10, r4, r8 \n\t" \ + "umull r11, r12, r5, r7 \n\t" \ + "adds r9, r9, r6 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "adds r9, r9, r11 \n\t" \ + "adcs r10, r10, r12 \n\t" \ + "adc r14, r14, #0 \n\t" \ + "str r9, [r0], #4 \n\t" \ + \ + "ldr r6, [r0] \n\t" \ + "adds r10, r10, r6 \n\t" \ + "adcs r14, r14, #0 \n\t" \ + "ldr r7, [r1], #4 \n\t" \ + "ldr r8, [r2], #4 \n\t" \ + "mov r9, #0 \n\t" \ + "umull r11, r12, r4, r8 \n\t" \ + "adds r10, r10, r11 \n\t" \ + "adcs r14, r14, r12 \n\t" \ + "adc r9, r9, #0 \n\t" \ + "umull r11, r12, r5, r7 \n\t" \ + "adds r10, r10, r11 \n\t" \ + "adcs r14, r14, r12 \n\t" \ + "adc r9, r9, #0 \n\t" \ + "str r10, [r0], #4 \n\t" \ + \ + "ldr r6, [r0] \n\t" \ + "adds r14, r14, r6 \n\t" \ + "adcs r9, r9, #0 \n\t" \ + "ldr r7, [r1], #4 \n\t" \ + "ldr r8, [r2], #4 \n\t" \ + "mov r10, #0 \n\t" \ + "umull r11, r12, r4, r8 \n\t" \ + "adds r14, r14, r11 \n\t" \ + "adcs r9, r9, r12 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "umull r11, r12, r5, r7 \n\t" \ + "adds r14, r14, r11 \n\t" \ + "adcs r9, r9, r12 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "str r14, [r0], #4 \n\t" \ + \ + "ldr r6, [r0] \n\t" \ + "adds r9, r9, r6 \n\t" \ + "adcs r10, r10, #0 \n\t" \ + "ldr r7, [r1], #4 \n\t" \ + "ldr r8, [r2], #4 \n\t" \ + "mov r14, #0 \n\t" \ + "umull r11, r12, r4, r8 \n\t" \ + "adds r9, r9, r11 \n\t" \ + "adcs r10, r10, r12 \n\t" \ + "adc r14, r14, #0 \n\t" \ + "umull r11, r12, r5, r7 \n\t" \ + "adds r9, r9, r11 \n\t" \ + "adcs r10, r10, r12 \n\t" \ + "adc r14, r14, #0 \n\t" \ + "str r9, [r0], #4 \n\t" \ + \ + "ldr r6, [r0] \n\t" \ + "adds r10, r10, r6 \n\t" \ + "adcs r14, r14, #0 \n\t" \ + "ldr r7, [r1], #4 \n\t" \ + "ldr r8, [r2], #4 \n\t" \ + "mov r9, #0 \n\t" \ + "umull r11, r12, r4, r8 \n\t" \ + "adds r10, r10, r11 \n\t" \ + "adcs r14, r14, r12 \n\t" \ + "adc r9, r9, #0 \n\t" \ + "umull r11, r12, r5, r7 \n\t" \ + "adds r10, r10, r11 \n\t" \ + "adcs r14, r14, r12 \n\t" \ + "adc r9, r9, #0 \n\t" \ + "str r10, [r0], #4 \n\t" \ + \ + "ldr r6, [r0] \n\t" \ + "adds r14, r14, r6 \n\t" \ + "adcs r9, r9, #0 \n\t" \ + /* skip past already-loaded (r4, r5) */ \ + "ldr r7, [r1], #8 \n\t" \ + "ldr r8, [r2], #8 \n\t" \ + "mov r10, #0 \n\t" \ + "umull r11, r12, r4, r8 \n\t" \ + "adds r14, r14, r11 \n\t" \ + "adcs r9, r9, r12 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "umull r11, r12, r5, r7 \n\t" \ + "adds r14, r14, r11 \n\t" \ + "adcs r9, r9, r12 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "str r14, [r0], #4 \n\t" \ + \ + "umull r11, r12, r4, r5 \n\t" \ + "adds r11, r11, r9 \n\t" \ + "adc r12, r12, r10 \n\t" \ + "stmia r0!, {r11, r12} \n\t" + +#define FAST_MULT_ASM_7 \ + "push {r3} \n\t" \ + "add r0, 24 \n\t" \ + "add r2, 24 \n\t" \ + "ldmia r1!, {r3} \n\t" \ + "ldmia r2!, {r6} \n\t" \ + \ + "umull r9, r10, r3, r6 \n\t" \ + "stmia r0!, {r9, r10} \n\t" \ + \ + "sub r0, 20 \n\t" \ + "sub r2, 16 \n\t" \ + "ldmia r2!, {r6, r7, r8} \n\t" \ + "ldmia r1!, {r4, r5} \n\t" \ + \ + "umull r9, r10, r3, r6 \n\t" \ + "stmia r0!, {r9} \n\t" \ + \ + "mov r14, #0 \n\t" \ + "umull r9, r12, r3, r7 \n\t" \ + "adds r10, r10, r9 \n\t" \ + "adc r12, r12, #0 \n\t" \ + "umull r9, r11, r4, r6 \n\t" \ + "adds r10, r10, r9 \n\t" \ + "adcs r12, r12, r11 \n\t" \ + "adc r14, r14, #0 \n\t" \ + "stmia r0!, {r10} \n\t" \ + \ + "mov r9, #0 \n\t" \ + "umull r10, r11, r3, r8 \n\t" \ + "adds r12, r12, r10 \n\t" \ + "adcs r14, r14, r11 \n\t" \ + "adc r9, r9, #0 \n\t" \ + "umull r10, r11, r4, r7 \n\t" \ + "adds r12, r12, r10 \n\t" \ + "adcs r14, r14, r11 \n\t" \ + "adc r9, r9, #0 \n\t" \ + "umull r10, r11, r5, r6 \n\t" \ + "adds r12, r12, r10 \n\t" \ + "adcs r14, r14, r11 \n\t" \ + "adc r9, r9, #0 \n\t" \ + "stmia r0!, {r12} \n\t" \ + \ + "ldmia r1!, {r3} \n\t" \ + "mov r10, #0 \n\t" \ + "umull r11, r12, r4, r8 \n\t" \ + "adds r14, r14, r11 \n\t" \ + "adcs r9, r9, r12 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "umull r11, r12, r5, r7 \n\t" \ + "adds r14, r14, r11 \n\t" \ + "adcs r9, r9, r12 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "umull r11, r12, r3, r6 \n\t" \ + "adds r14, r14, r11 \n\t" \ + "adcs r9, r9, r12 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "ldr r11, [r0] \n\t" \ + "adds r14, r14, r11 \n\t" \ + "adcs r9, r9, #0 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "stmia r0!, {r14} \n\t" \ + \ + "ldmia r2!, {r6} \n\t" \ + "mov r11, #0 \n\t" \ + "umull r12, r14, r4, r6 \n\t" \ + "adds r9, r9, r12 \n\t" \ + "adcs r10, r10, r14 \n\t" \ + "adc r11, r11, #0 \n\t" \ + "umull r12, r14, r5, r8 \n\t" \ + "adds r9, r9, r12 \n\t" \ + "adcs r10, r10, r14 \n\t" \ + "adc r11, r11, #0 \n\t" \ + "umull r12, r14, r3, r7 \n\t" \ + "adds r9, r9, r12 \n\t" \ + "adcs r10, r10, r14 \n\t" \ + "adc r11, r11, #0 \n\t" \ + "ldr r12, [r0] \n\t" \ + "adds r9, r9, r12 \n\t" \ + "adcs r10, r10, #0 \n\t" \ + "adc r11, r11, #0 \n\t" \ + "stmia r0!, {r9} \n\t" \ + \ + "mov r12, #0 \n\t" \ + "umull r14, r9, r5, r6 \n\t" \ + "adds r10, r10, r14 \n\t" \ + "adcs r11, r11, r9 \n\t" \ + "adc r12, r12, #0 \n\t" \ + "umull r14, r9, r3, r8 \n\t" \ + "adds r10, r10, r14 \n\t" \ + "adcs r11, r11, r9 \n\t" \ + "adc r12, r12, #0 \n\t" \ + "stmia r0!, {r10} \n\t" \ + \ + "umull r9, r10, r3, r6 \n\t" \ + "adds r11, r11, r9 \n\t" \ + "adc r12, r12, r10 \n\t" \ + "stmia r0!, {r11, r12} \n\t" \ + \ + "sub r0, 44 \n\t" \ + "sub r1, 16 \n\t" \ + "sub r2, 28 \n\t" \ + "ldmia r1!, {r3,r4,r5} \n\t" \ + "ldmia r2!, {r6,r7,r8} \n\t" \ + \ + "umull r9, r10, r3, r6 \n\t" \ + "stmia r0!, {r9} \n\t" \ + \ + "mov r14, #0 \n\t" \ + "umull r9, r12, r3, r7 \n\t" \ + "adds r10, r10, r9 \n\t" \ + "adc r12, r12, #0 \n\t" \ + "umull r9, r11, r4, r6 \n\t" \ + "adds r10, r10, r9 \n\t" \ + "adcs r12, r12, r11 \n\t" \ + "adc r14, r14, #0 \n\t" \ + "stmia r0!, {r10} \n\t" \ + \ + "mov r9, #0 \n\t" \ + "umull r10, r11, r3, r8 \n\t" \ + "adds r12, r12, r10 \n\t" \ + "adcs r14, r14, r11 \n\t" \ + "adc r9, r9, #0 \n\t" \ + "umull r10, r11, r4, r7 \n\t" \ + "adds r12, r12, r10 \n\t" \ + "adcs r14, r14, r11 \n\t" \ + "adc r9, r9, #0 \n\t" \ + "umull r10, r11, r5, r6 \n\t" \ + "adds r12, r12, r10 \n\t" \ + "adcs r14, r14, r11 \n\t" \ + "adc r9, r9, #0 \n\t" \ + "stmia r0!, {r12} \n\t" \ + \ + "ldmia r1!, {r3} \n\t" \ + "mov r10, #0 \n\t" \ + "umull r11, r12, r4, r8 \n\t" \ + "adds r14, r14, r11 \n\t" \ + "adcs r9, r9, r12 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "umull r11, r12, r5, r7 \n\t" \ + "adds r14, r14, r11 \n\t" \ + "adcs r9, r9, r12 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "umull r11, r12, r3, r6 \n\t" \ + "adds r14, r14, r11 \n\t" \ + "adcs r9, r9, r12 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "ldr r11, [r0] \n\t" \ + "adds r14, r14, r11 \n\t" \ + "adcs r9, r9, #0 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "stmia r0!, {r14} \n\t" \ + \ + "ldmia r1!, {r4} \n\t" \ + "mov r11, #0 \n\t" \ + "umull r12, r14, r5, r8 \n\t" \ + "adds r9, r9, r12 \n\t" \ + "adcs r10, r10, r14 \n\t" \ + "adc r11, r11, #0 \n\t" \ + "umull r12, r14, r3, r7 \n\t" \ + "adds r9, r9, r12 \n\t" \ + "adcs r10, r10, r14 \n\t" \ + "adc r11, r11, #0 \n\t" \ + "umull r12, r14, r4, r6 \n\t" \ + "adds r9, r9, r12 \n\t" \ + "adcs r10, r10, r14 \n\t" \ + "adc r11, r11, #0 \n\t" \ + "ldr r12, [r0] \n\t" \ + "adds r9, r9, r12 \n\t" \ + "adcs r10, r10, #0 \n\t" \ + "adc r11, r11, #0 \n\t" \ + "stmia r0!, {r9} \n\t" \ + \ + "ldmia r1!, {r5} \n\t" \ + "mov r12, #0 \n\t" \ + "umull r14, r9, r3, r8 \n\t" \ + "adds r10, r10, r14 \n\t" \ + "adcs r11, r11, r9 \n\t" \ + "adc r12, r12, #0 \n\t" \ + "umull r14, r9, r4, r7 \n\t" \ + "adds r10, r10, r14 \n\t" \ + "adcs r11, r11, r9 \n\t" \ + "adc r12, r12, #0 \n\t" \ + "umull r14, r9, r5, r6 \n\t" \ + "adds r10, r10, r14 \n\t" \ + "adcs r11, r11, r9 \n\t" \ + "adc r12, r12, #0 \n\t" \ + "ldr r14, [r0] \n\t" \ + "adds r10, r10, r14 \n\t" \ + "adcs r11, r11, #0 \n\t" \ + "adc r12, r12, #0 \n\t" \ + "stmia r0!, {r10} \n\t" \ + \ + "ldmia r1!, {r3} \n\t" \ + "mov r14, #0 \n\t" \ + "umull r9, r10, r4, r8 \n\t" \ + "adds r11, r11, r9 \n\t" \ + "adcs r12, r12, r10 \n\t" \ + "adc r14, r14, #0 \n\t" \ + "umull r9, r10, r5, r7 \n\t" \ + "adds r11, r11, r9 \n\t" \ + "adcs r12, r12, r10 \n\t" \ + "adc r14, r14, #0 \n\t" \ + "umull r9, r10, r3, r6 \n\t" \ + "adds r11, r11, r9 \n\t" \ + "adcs r12, r12, r10 \n\t" \ + "adc r14, r14, #0 \n\t" \ + "ldr r9, [r0] \n\t" \ + "adds r11, r11, r9 \n\t" \ + "adcs r12, r12, #0 \n\t" \ + "adc r14, r14, #0 \n\t" \ + "stmia r0!, {r11} \n\t" \ + \ + "ldmia r2!, {r6} \n\t" \ + "mov r9, #0 \n\t" \ + "umull r10, r11, r4, r6 \n\t" \ + "adds r12, r12, r10 \n\t" \ + "adcs r14, r14, r11 \n\t" \ + "adc r9, r9, #0 \n\t" \ + "umull r10, r11, r5, r8 \n\t" \ + "adds r12, r12, r10 \n\t" \ + "adcs r14, r14, r11 \n\t" \ + "adc r9, r9, #0 \n\t" \ + "umull r10, r11, r3, r7 \n\t" \ + "adds r12, r12, r10 \n\t" \ + "adcs r14, r14, r11 \n\t" \ + "adc r9, r9, #0 \n\t" \ + "ldr r10, [r0] \n\t" \ + "adds r12, r12, r10 \n\t" \ + "adcs r14, r14, #0 \n\t" \ + "adc r9, r9, #0 \n\t" \ + "stmia r0!, {r12} \n\t" \ + \ + "ldmia r2!, {r7} \n\t" \ + "mov r10, #0 \n\t" \ + "umull r11, r12, r4, r7 \n\t" \ + "adds r14, r14, r11 \n\t" \ + "adcs r9, r9, r12 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "umull r11, r12, r5, r6 \n\t" \ + "adds r14, r14, r11 \n\t" \ + "adcs r9, r9, r12 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "umull r11, r12, r3, r8 \n\t" \ + "adds r14, r14, r11 \n\t" \ + "adcs r9, r9, r12 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "ldr r11, [r0] \n\t" \ + "adds r14, r14, r11 \n\t" \ + "adcs r9, r9, #0 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "stmia r0!, {r14} \n\t" \ + \ + "ldmia r2!, {r8} \n\t" \ + "mov r11, #0 \n\t" \ + "umull r12, r14, r4, r8 \n\t" \ + "adds r9, r9, r12 \n\t" \ + "adcs r10, r10, r14 \n\t" \ + "adc r11, r11, #0 \n\t" \ + "umull r12, r14, r5, r7 \n\t" \ + "adds r9, r9, r12 \n\t" \ + "adcs r10, r10, r14 \n\t" \ + "adc r11, r11, #0 \n\t" \ + "umull r12, r14, r3, r6 \n\t" \ + "adds r9, r9, r12 \n\t" \ + "adcs r10, r10, r14 \n\t" \ + "adc r11, r11, #0 \n\t" \ + "ldr r12, [r0] \n\t" \ + "adds r9, r9, r12 \n\t" \ + "adcs r10, r10, #0 \n\t" \ + "adc r11, r11, #0 \n\t" \ + "stmia r0!, {r9} \n\t" \ + \ + "ldmia r2!, {r6} \n\t" \ + "mov r12, #0 \n\t" \ + "umull r14, r9, r4, r6 \n\t" \ + "adds r10, r10, r14 \n\t" \ + "adcs r11, r11, r9 \n\t" \ + "adc r12, r12, #0 \n\t" \ + "umull r14, r9, r5, r8 \n\t" \ + "adds r10, r10, r14 \n\t" \ + "adcs r11, r11, r9 \n\t" \ + "adc r12, r12, #0 \n\t" \ + "umull r14, r9, r3, r7 \n\t" \ + "adds r10, r10, r14 \n\t" \ + "adcs r11, r11, r9 \n\t" \ + "adc r12, r12, #0 \n\t" \ + "ldr r14, [r0] \n\t" \ + "adds r10, r10, r14 \n\t" \ + "adcs r11, r11, #0 \n\t" \ + "adc r12, r12, #0 \n\t" \ + "stmia r0!, {r10} \n\t" \ + \ + "mov r14, #0 \n\t" \ + "umull r9, r10, r5, r6 \n\t" \ + "adds r11, r11, r9 \n\t" \ + "adcs r12, r12, r10 \n\t" \ + "adc r14, r14, #0 \n\t" \ + "umull r9, r10, r3, r8 \n\t" \ + "adds r11, r11, r9 \n\t" \ + "adcs r12, r12, r10 \n\t" \ + "adc r14, r14, #0 \n\t" \ + "stmia r0!, {r11} \n\t" \ + \ + "umull r10, r11, r3, r6 \n\t" \ + "adds r12, r12, r10 \n\t" \ + "adc r14, r14, r11 \n\t" \ + "stmia r0!, {r12, r14} \n\t" \ + "pop {r3} \n\t" + +#define FAST_MULT_ASM_7_TO_8 \ + "cmp r3, #7 \n\t" \ + "beq 1f \n\t" \ + \ + /* r4 = left high, r5 = right high */ \ + "ldr r4, [r1] \n\t" \ + "ldr r5, [r2] \n\t" \ + \ + "sub r0, #28 \n\t" \ + "sub r1, #28 \n\t" \ + "sub r2, #28 \n\t" \ + \ + "ldr r6, [r0] \n\t" \ + "ldr r7, [r1], #4 \n\t" \ + "ldr r8, [r2], #4 \n\t" \ + "mov r14, #0 \n\t" \ + "umull r9, r10, r4, r8 \n\t" \ + "umull r11, r12, r5, r7 \n\t" \ + "adds r9, r9, r6 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "adds r9, r9, r11 \n\t" \ + "adcs r10, r10, r12 \n\t" \ + "adc r14, r14, #0 \n\t" \ + "str r9, [r0], #4 \n\t" \ + \ + "ldr r6, [r0] \n\t" \ + "adds r10, r10, r6 \n\t" \ + "adcs r14, r14, #0 \n\t" \ + "ldr r7, [r1], #4 \n\t" \ + "ldr r8, [r2], #4 \n\t" \ + "mov r9, #0 \n\t" \ + "umull r11, r12, r4, r8 \n\t" \ + "adds r10, r10, r11 \n\t" \ + "adcs r14, r14, r12 \n\t" \ + "adc r9, r9, #0 \n\t" \ + "umull r11, r12, r5, r7 \n\t" \ + "adds r10, r10, r11 \n\t" \ + "adcs r14, r14, r12 \n\t" \ + "adc r9, r9, #0 \n\t" \ + "str r10, [r0], #4 \n\t" \ + \ + "ldr r6, [r0] \n\t" \ + "adds r14, r14, r6 \n\t" \ + "adcs r9, r9, #0 \n\t" \ + "ldr r7, [r1], #4 \n\t" \ + "ldr r8, [r2], #4 \n\t" \ + "mov r10, #0 \n\t" \ + "umull r11, r12, r4, r8 \n\t" \ + "adds r14, r14, r11 \n\t" \ + "adcs r9, r9, r12 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "umull r11, r12, r5, r7 \n\t" \ + "adds r14, r14, r11 \n\t" \ + "adcs r9, r9, r12 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "str r14, [r0], #4 \n\t" \ + \ + "ldr r6, [r0] \n\t" \ + "adds r9, r9, r6 \n\t" \ + "adcs r10, r10, #0 \n\t" \ + "ldr r7, [r1], #4 \n\t" \ + "ldr r8, [r2], #4 \n\t" \ + "mov r14, #0 \n\t" \ + "umull r11, r12, r4, r8 \n\t" \ + "adds r9, r9, r11 \n\t" \ + "adcs r10, r10, r12 \n\t" \ + "adc r14, r14, #0 \n\t" \ + "umull r11, r12, r5, r7 \n\t" \ + "adds r9, r9, r11 \n\t" \ + "adcs r10, r10, r12 \n\t" \ + "adc r14, r14, #0 \n\t" \ + "str r9, [r0], #4 \n\t" \ + \ + "ldr r6, [r0] \n\t" \ + "adds r10, r10, r6 \n\t" \ + "adcs r14, r14, #0 \n\t" \ + "ldr r7, [r1], #4 \n\t" \ + "ldr r8, [r2], #4 \n\t" \ + "mov r9, #0 \n\t" \ + "umull r11, r12, r4, r8 \n\t" \ + "adds r10, r10, r11 \n\t" \ + "adcs r14, r14, r12 \n\t" \ + "adc r9, r9, #0 \n\t" \ + "umull r11, r12, r5, r7 \n\t" \ + "adds r10, r10, r11 \n\t" \ + "adcs r14, r14, r12 \n\t" \ + "adc r9, r9, #0 \n\t" \ + "str r10, [r0], #4 \n\t" \ + \ + "ldr r6, [r0] \n\t" \ + "adds r14, r14, r6 \n\t" \ + "adcs r9, r9, #0 \n\t" \ + "ldr r7, [r1], #4 \n\t" \ + "ldr r8, [r2], #4 \n\t" \ + "mov r10, #0 \n\t" \ + "umull r11, r12, r4, r8 \n\t" \ + "adds r14, r14, r11 \n\t" \ + "adcs r9, r9, r12 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "umull r11, r12, r5, r7 \n\t" \ + "adds r14, r14, r11 \n\t" \ + "adcs r9, r9, r12 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "str r14, [r0], #4 \n\t" \ + \ + "ldr r6, [r0] \n\t" \ + "adds r9, r9, r6 \n\t" \ + "adcs r10, r10, #0 \n\t" \ + /* skip past already-loaded (r4, r5) */ \ + "ldr r7, [r1], #8 \n\t" \ + "ldr r8, [r2], #8 \n\t" \ + "mov r14, #0 \n\t" \ + "umull r11, r12, r4, r8 \n\t" \ + "adds r9, r9, r11 \n\t" \ + "adcs r10, r10, r12 \n\t" \ + "adc r14, r14, #0 \n\t" \ + "umull r11, r12, r5, r7 \n\t" \ + "adds r9, r9, r11 \n\t" \ + "adcs r10, r10, r12 \n\t" \ + "adc r14, r14, #0 \n\t" \ + "str r9, [r0], #4 \n\t" \ + \ + "umull r11, r12, r4, r5 \n\t" \ + "adds r11, r11, r10 \n\t" \ + "adc r12, r12, r14 \n\t" \ + "stmia r0!, {r11, r12} \n\t" + +#define FAST_MULT_ASM_8 \ + "push {r3} \n\t" \ + "add r0, 24 \n\t" \ + "add r2, 24 \n\t" \ + "ldmia r1!, {r3,r4} \n\t" \ + "ldmia r2!, {r6,r7} \n\t" \ + \ + "umull r11, r12, r3, r6 \n\t" \ + "stmia r0!, {r11} \n\t" \ + \ + "mov r10, #0 \n\t" \ + "umull r11, r9, r3, r7 \n\t" \ + "adds r12, r12, r11 \n\t" \ + "adc r9, r9, #0 \n\t" \ + "umull r11, r14, r4, r6 \n\t" \ + "adds r12, r12, r11 \n\t" \ + "adcs r9, r9, r14 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "stmia r0!, {r12} \n\t" \ + \ + "umull r12, r14, r4, r7 \n\t" \ + "adds r9, r9, r12 \n\t" \ + "adc r10, r10, r14 \n\t" \ + "stmia r0!, {r9, r10} \n\t" \ + \ + "sub r0, 28 \n\t" \ + "sub r2, 20 \n\t" \ + "ldmia r2!, {r6,r7,r8} \n\t" \ + "ldmia r1!, {r5} \n\t" \ + \ + "umull r11, r12, r3, r6 \n\t" \ + "stmia r0!, {r11} \n\t" \ + \ + "mov r10, #0 \n\t" \ + "umull r11, r9, r3, r7 \n\t" \ + "adds r12, r12, r11 \n\t" \ + "adc r9, r9, #0 \n\t" \ + "umull r11, r14, r4, r6 \n\t" \ + "adds r12, r12, r11 \n\t" \ + "adcs r9, r9, r14 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "stmia r0!, {r12} \n\t" \ + \ + "mov r11, #0 \n\t" \ + "umull r12, r14, r3, r8 \n\t" \ + "adds r9, r9, r12 \n\t" \ + "adcs r10, r10, r14 \n\t" \ + "adc r11, r11, #0 \n\t" \ + "umull r12, r14, r4, r7 \n\t" \ + "adds r9, r9, r12 \n\t" \ + "adcs r10, r10, r14 \n\t" \ + "adc r11, r11, #0 \n\t" \ + "umull r12, r14, r5, r6 \n\t" \ + "adds r9, r9, r12 \n\t" \ + "adcs r10, r10, r14 \n\t" \ + "adc r11, r11, #0 \n\t" \ + "stmia r0!, {r9} \n\t" \ + \ + "ldmia r1!, {r3} \n\t" \ + "mov r12, #0 \n\t" \ + "umull r14, r9, r4, r8 \n\t" \ + "adds r10, r10, r14 \n\t" \ + "adcs r11, r11, r9 \n\t" \ + "adc r12, r12, #0 \n\t" \ + "umull r14, r9, r5, r7 \n\t" \ + "adds r10, r10, r14 \n\t" \ + "adcs r11, r11, r9 \n\t" \ + "adc r12, r12, #0 \n\t" \ + "umull r14, r9, r3, r6 \n\t" \ + "adds r10, r10, r14 \n\t" \ + "adcs r11, r11, r9 \n\t" \ + "adc r12, r12, #0 \n\t" \ + "ldr r14, [r0] \n\t" \ + "adds r10, r10, r14 \n\t" \ + "adcs r11, r11, #0 \n\t" \ + "adc r12, r12, #0 \n\t" \ + "stmia r0!, {r10} \n\t" \ + \ + "ldmia r1!, {r4} \n\t" \ + "mov r14, #0 \n\t" \ + "umull r9, r10, r5, r8 \n\t" \ + "adds r11, r11, r9 \n\t" \ + "adcs r12, r12, r10 \n\t" \ + "adc r14, r14, #0 \n\t" \ + "umull r9, r10, r3, r7 \n\t" \ + "adds r11, r11, r9 \n\t" \ + "adcs r12, r12, r10 \n\t" \ + "adc r14, r14, #0 \n\t" \ + "umull r9, r10, r4, r6 \n\t" \ + "adds r11, r11, r9 \n\t" \ + "adcs r12, r12, r10 \n\t" \ + "adc r14, r14, #0 \n\t" \ + "ldr r9, [r0] \n\t" \ + "adds r11, r11, r9 \n\t" \ + "adcs r12, r12, #0 \n\t" \ + "adc r14, r14, #0 \n\t" \ + "stmia r0!, {r11} \n\t" \ + \ + "ldmia r2!, {r6} \n\t" \ + "mov r9, #0 \n\t" \ + "umull r10, r11, r5, r6 \n\t" \ + "adds r12, r12, r10 \n\t" \ + "adcs r14, r14, r11 \n\t" \ + "adc r9, r9, #0 \n\t" \ + "umull r10, r11, r3, r8 \n\t" \ + "adds r12, r12, r10 \n\t" \ + "adcs r14, r14, r11 \n\t" \ + "adc r9, r9, #0 \n\t" \ + "umull r10, r11, r4, r7 \n\t" \ + "adds r12, r12, r10 \n\t" \ + "adcs r14, r14, r11 \n\t" \ + "adc r9, r9, #0 \n\t" \ + "ldr r10, [r0] \n\t" \ + "adds r12, r12, r10 \n\t" \ + "adcs r14, r14, #0 \n\t" \ + "adc r9, r9, #0 \n\t" \ + "stmia r0!, {r12} \n\t" \ + \ + "ldmia r2!, {r7} \n\t" \ + "mov r10, #0 \n\t" \ + "umull r11, r12, r5, r7 \n\t" \ + "adds r14, r14, r11 \n\t" \ + "adcs r9, r9, r12 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "umull r11, r12, r3, r6 \n\t" \ + "adds r14, r14, r11 \n\t" \ + "adcs r9, r9, r12 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "umull r11, r12, r4, r8 \n\t" \ + "adds r14, r14, r11 \n\t" \ + "adcs r9, r9, r12 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "ldr r11, [r0] \n\t" \ + "adds r14, r14, r11 \n\t" \ + "adcs r9, r9, #0 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "stmia r0!, {r14} \n\t" \ + \ + "mov r11, #0 \n\t" \ + "umull r12, r14, r3, r7 \n\t" \ + "adds r9, r9, r12 \n\t" \ + "adcs r10, r10, r14 \n\t" \ + "adc r11, r11, #0 \n\t" \ + "umull r12, r14, r4, r6 \n\t" \ + "adds r9, r9, r12 \n\t" \ + "adcs r10, r10, r14 \n\t" \ + "adc r11, r11, #0 \n\t" \ + "stmia r0!, {r9} \n\t" \ + \ + "umull r14, r9, r4, r7 \n\t" \ + "adds r10, r10, r14 \n\t" \ + "adc r11, r11, r9 \n\t" \ + "stmia r0!, {r10, r11} \n\t" \ + \ + "sub r0, 52 \n\t" \ + "sub r1, 20 \n\t" \ + "sub r2, 32 \n\t" \ + "ldmia r1!, {r3,r4,r5} \n\t" \ + "ldmia r2!, {r6,r7,r8} \n\t" \ + \ + "umull r11, r12, r3, r6 \n\t" \ + "stmia r0!, {r11} \n\t" \ + \ + "mov r10, #0 \n\t" \ + "umull r11, r9, r3, r7 \n\t" \ + "adds r12, r12, r11 \n\t" \ + "adc r9, r9, #0 \n\t" \ + "umull r11, r14, r4, r6 \n\t" \ + "adds r12, r12, r11 \n\t" \ + "adcs r9, r9, r14 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "stmia r0!, {r12} \n\t" \ + \ + "mov r11, #0 \n\t" \ + "umull r12, r14, r3, r8 \n\t" \ + "adds r9, r9, r12 \n\t" \ + "adcs r10, r10, r14 \n\t" \ + "adc r11, r11, #0 \n\t" \ + "umull r12, r14, r4, r7 \n\t" \ + "adds r9, r9, r12 \n\t" \ + "adcs r10, r10, r14 \n\t" \ + "adc r11, r11, #0 \n\t" \ + "umull r12, r14, r5, r6 \n\t" \ + "adds r9, r9, r12 \n\t" \ + "adcs r10, r10, r14 \n\t" \ + "adc r11, r11, #0 \n\t" \ + "stmia r0!, {r9} \n\t" \ + \ + "ldmia r1!, {r3} \n\t" \ + "mov r12, #0 \n\t" \ + "umull r14, r9, r4, r8 \n\t" \ + "adds r10, r10, r14 \n\t" \ + "adcs r11, r11, r9 \n\t" \ + "adc r12, r12, #0 \n\t" \ + "umull r14, r9, r5, r7 \n\t" \ + "adds r10, r10, r14 \n\t" \ + "adcs r11, r11, r9 \n\t" \ + "adc r12, r12, #0 \n\t" \ + "umull r14, r9, r3, r6 \n\t" \ + "adds r10, r10, r14 \n\t" \ + "adcs r11, r11, r9 \n\t" \ + "adc r12, r12, #0 \n\t" \ + "ldr r14, [r0] \n\t" \ + "adds r10, r10, r14 \n\t" \ + "adcs r11, r11, #0 \n\t" \ + "adc r12, r12, #0 \n\t" \ + "stmia r0!, {r10} \n\t" \ + \ + "ldmia r1!, {r4} \n\t" \ + "mov r14, #0 \n\t" \ + "umull r9, r10, r5, r8 \n\t" \ + "adds r11, r11, r9 \n\t" \ + "adcs r12, r12, r10 \n\t" \ + "adc r14, r14, #0 \n\t" \ + "umull r9, r10, r3, r7 \n\t" \ + "adds r11, r11, r9 \n\t" \ + "adcs r12, r12, r10 \n\t" \ + "adc r14, r14, #0 \n\t" \ + "umull r9, r10, r4, r6 \n\t" \ + "adds r11, r11, r9 \n\t" \ + "adcs r12, r12, r10 \n\t" \ + "adc r14, r14, #0 \n\t" \ + "ldr r9, [r0] \n\t" \ + "adds r11, r11, r9 \n\t" \ + "adcs r12, r12, #0 \n\t" \ + "adc r14, r14, #0 \n\t" \ + "stmia r0!, {r11} \n\t" \ + \ + "ldmia r1!, {r5} \n\t" \ + "mov r9, #0 \n\t" \ + "umull r10, r11, r3, r8 \n\t" \ + "adds r12, r12, r10 \n\t" \ + "adcs r14, r14, r11 \n\t" \ + "adc r9, r9, #0 \n\t" \ + "umull r10, r11, r4, r7 \n\t" \ + "adds r12, r12, r10 \n\t" \ + "adcs r14, r14, r11 \n\t" \ + "adc r9, r9, #0 \n\t" \ + "umull r10, r11, r5, r6 \n\t" \ + "adds r12, r12, r10 \n\t" \ + "adcs r14, r14, r11 \n\t" \ + "adc r9, r9, #0 \n\t" \ + "ldr r10, [r0] \n\t" \ + "adds r12, r12, r10 \n\t" \ + "adcs r14, r14, #0 \n\t" \ + "adc r9, r9, #0 \n\t" \ + "stmia r0!, {r12} \n\t" \ + \ + "ldmia r1!, {r3} \n\t" \ + "mov r10, #0 \n\t" \ + "umull r11, r12, r4, r8 \n\t" \ + "adds r14, r14, r11 \n\t" \ + "adcs r9, r9, r12 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "umull r11, r12, r5, r7 \n\t" \ + "adds r14, r14, r11 \n\t" \ + "adcs r9, r9, r12 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "umull r11, r12, r3, r6 \n\t" \ + "adds r14, r14, r11 \n\t" \ + "adcs r9, r9, r12 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "ldr r11, [r0] \n\t" \ + "adds r14, r14, r11 \n\t" \ + "adcs r9, r9, #0 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "stmia r0!, {r14} \n\t" \ + \ + "ldmia r1!, {r4} \n\t" \ + "mov r11, #0 \n\t" \ + "umull r12, r14, r5, r8 \n\t" \ + "adds r9, r9, r12 \n\t" \ + "adcs r10, r10, r14 \n\t" \ + "adc r11, r11, #0 \n\t" \ + "umull r12, r14, r3, r7 \n\t" \ + "adds r9, r9, r12 \n\t" \ + "adcs r10, r10, r14 \n\t" \ + "adc r11, r11, #0 \n\t" \ + "umull r12, r14, r4, r6 \n\t" \ + "adds r9, r9, r12 \n\t" \ + "adcs r10, r10, r14 \n\t" \ + "adc r11, r11, #0 \n\t" \ + "ldr r12, [r0] \n\t" \ + "adds r9, r9, r12 \n\t" \ + "adcs r10, r10, #0 \n\t" \ + "adc r11, r11, #0 \n\t" \ + "stmia r0!, {r9} \n\t" \ + \ + "ldmia r2!, {r6} \n\t" \ + "mov r12, #0 \n\t" \ + "umull r14, r9, r5, r6 \n\t" \ + "adds r10, r10, r14 \n\t" \ + "adcs r11, r11, r9 \n\t" \ + "adc r12, r12, #0 \n\t" \ + "umull r14, r9, r3, r8 \n\t" \ + "adds r10, r10, r14 \n\t" \ + "adcs r11, r11, r9 \n\t" \ + "adc r12, r12, #0 \n\t" \ + "umull r14, r9, r4, r7 \n\t" \ + "adds r10, r10, r14 \n\t" \ + "adcs r11, r11, r9 \n\t" \ + "adc r12, r12, #0 \n\t" \ + "ldr r14, [r0] \n\t" \ + "adds r10, r10, r14 \n\t" \ + "adcs r11, r11, #0 \n\t" \ + "adc r12, r12, #0 \n\t" \ + "stmia r0!, {r10} \n\t" \ + \ + "ldmia r2!, {r7} \n\t" \ + "mov r14, #0 \n\t" \ + "umull r9, r10, r5, r7 \n\t" \ + "adds r11, r11, r9 \n\t" \ + "adcs r12, r12, r10 \n\t" \ + "adc r14, r14, #0 \n\t" \ + "umull r9, r10, r3, r6 \n\t" \ + "adds r11, r11, r9 \n\t" \ + "adcs r12, r12, r10 \n\t" \ + "adc r14, r14, #0 \n\t" \ + "umull r9, r10, r4, r8 \n\t" \ + "adds r11, r11, r9 \n\t" \ + "adcs r12, r12, r10 \n\t" \ + "adc r14, r14, #0 \n\t" \ + "ldr r9, [r0] \n\t" \ + "adds r11, r11, r9 \n\t" \ + "adcs r12, r12, #0 \n\t" \ + "adc r14, r14, #0 \n\t" \ + "stmia r0!, {r11} \n\t" \ + \ + "ldmia r2!, {r8} \n\t" \ + "mov r9, #0 \n\t" \ + "umull r10, r11, r5, r8 \n\t" \ + "adds r12, r12, r10 \n\t" \ + "adcs r14, r14, r11 \n\t" \ + "adc r9, r9, #0 \n\t" \ + "umull r10, r11, r3, r7 \n\t" \ + "adds r12, r12, r10 \n\t" \ + "adcs r14, r14, r11 \n\t" \ + "adc r9, r9, #0 \n\t" \ + "umull r10, r11, r4, r6 \n\t" \ + "adds r12, r12, r10 \n\t" \ + "adcs r14, r14, r11 \n\t" \ + "adc r9, r9, #0 \n\t" \ + "ldr r10, [r0] \n\t" \ + "adds r12, r12, r10 \n\t" \ + "adcs r14, r14, #0 \n\t" \ + "adc r9, r9, #0 \n\t" \ + "stmia r0!, {r12} \n\t" \ + \ + "ldmia r2!, {r6} \n\t" \ + "mov r10, #0 \n\t" \ + "umull r11, r12, r5, r6 \n\t" \ + "adds r14, r14, r11 \n\t" \ + "adcs r9, r9, r12 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "umull r11, r12, r3, r8 \n\t" \ + "adds r14, r14, r11 \n\t" \ + "adcs r9, r9, r12 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "umull r11, r12, r4, r7 \n\t" \ + "adds r14, r14, r11 \n\t" \ + "adcs r9, r9, r12 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "ldr r11, [r0] \n\t" \ + "adds r14, r14, r11 \n\t" \ + "adcs r9, r9, #0 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "stmia r0!, {r14} \n\t" \ + \ + "ldmia r2!, {r7} \n\t" \ + "mov r11, #0 \n\t" \ + "umull r12, r14, r5, r7 \n\t" \ + "adds r9, r9, r12 \n\t" \ + "adcs r10, r10, r14 \n\t" \ + "adc r11, r11, #0 \n\t" \ + "umull r12, r14, r3, r6 \n\t" \ + "adds r9, r9, r12 \n\t" \ + "adcs r10, r10, r14 \n\t" \ + "adc r11, r11, #0 \n\t" \ + "umull r12, r14, r4, r8 \n\t" \ + "adds r9, r9, r12 \n\t" \ + "adcs r10, r10, r14 \n\t" \ + "adc r11, r11, #0 \n\t" \ + "ldr r12, [r0] \n\t" \ + "adds r9, r9, r12 \n\t" \ + "adcs r10, r10, #0 \n\t" \ + "adc r11, r11, #0 \n\t" \ + "stmia r0!, {r9} \n\t" \ + \ + "mov r12, #0 \n\t" \ + "umull r14, r9, r3, r7 \n\t" \ + "adds r10, r10, r14 \n\t" \ + "adcs r11, r11, r9 \n\t" \ + "adc r12, r12, #0 \n\t" \ + "umull r14, r9, r4, r6 \n\t" \ + "adds r10, r10, r14 \n\t" \ + "adcs r11, r11, r9 \n\t" \ + "adc r12, r12, #0 \n\t" \ + "stmia r0!, {r10} \n\t" \ + \ + "umull r9, r10, r4, r7 \n\t" \ + "adds r11, r11, r9 \n\t" \ + "adc r12, r12, r10 \n\t" \ + "stmia r0!, {r11, r12} \n\t" \ + "pop {r3} \n\t" + +#define FAST_SQUARE_ASM_5 \ + "push {r2} \n\t" \ + "ldmia r1!, {r2,r3,r4,r5,r6} \n\t" \ + "push {r1} \n\t" \ + \ + "umull r11, r12, r2, r2 \n\t" \ + "stmia r0!, {r11} \n\t" \ + \ + "mov r9, #0 \n\t" \ + "umull r10, r11, r2, r3 \n\t" \ + "adds r12, r12, r10 \n\t" \ + "adcs r8, r11, #0 \n\t" \ + "adc r9, r9, #0 \n\t" \ + "adds r12, r12, r10 \n\t" \ + "adcs r8, r8, r11 \n\t" \ + "adc r9, r9, #0 \n\t" \ + "stmia r0!, {r12} \n\t" \ + \ + "mov r10, #0 \n\t" \ + "umull r11, r12, r2, r4 \n\t" \ + "adds r11, r11, r11 \n\t" \ + "adcs r12, r12, r12 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "adds r8, r8, r11 \n\t" \ + "adcs r9, r9, r12 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "umull r11, r12, r3, r3 \n\t" \ + "adds r8, r8, r11 \n\t" \ + "adcs r9, r9, r12 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "stmia r0!, {r8} \n\t" \ + \ + "mov r12, #0 \n\t" \ + "umull r8, r11, r2, r5 \n\t" \ + "umull r1, r14, r3, r4 \n\t" \ + "adds r8, r8, r1 \n\t" \ + "adcs r11, r11, r14 \n\t" \ + "adc r12, r12, #0 \n\t" \ + "adds r8, r8, r8 \n\t" \ + "adcs r11, r11, r11 \n\t" \ + "adc r12, r12, r12 \n\t" \ + "adds r8, r8, r9 \n\t" \ + "adcs r11, r11, r10 \n\t" \ + "adc r12, r12, #0 \n\t" \ + "stmia r0!, {r8} \n\t" \ + \ + "mov r10, #0 \n\t" \ + "umull r8, r9, r2, r6 \n\t" \ + "umull r1, r14, r3, r5 \n\t" \ + "adds r8, r8, r1 \n\t" \ + "adcs r9, r9, r14 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "adds r8, r8, r8 \n\t" \ + "adcs r9, r9, r9 \n\t" \ + "adc r10, r10, r10 \n\t" \ + "umull r1, r14, r4, r4 \n\t" \ + "adds r8, r8, r1 \n\t" \ + "adcs r9, r9, r14 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "adds r8, r8, r11 \n\t" \ + "adcs r9, r9, r12 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "stmia r0!, {r8} \n\t" \ + \ + "mov r12, #0 \n\t" \ + "umull r8, r11, r3, r6 \n\t" \ + "umull r1, r14, r4, r5 \n\t" \ + "adds r8, r8, r1 \n\t" \ + "adcs r11, r11, r14 \n\t" \ + "adc r12, r12, #0 \n\t" \ + "adds r8, r8, r8 \n\t" \ + "adcs r11, r11, r11 \n\t" \ + "adc r12, r12, r12 \n\t" \ + "adds r8, r8, r9 \n\t" \ + "adcs r11, r11, r10 \n\t" \ + "adc r12, r12, #0 \n\t" \ + "stmia r0!, {r8} \n\t" \ + \ + "mov r8, #0 \n\t" \ + "umull r1, r10, r4, r6 \n\t" \ + "adds r1, r1, r1 \n\t" \ + "adcs r10, r10, r10 \n\t" \ + "adc r8, r8, #0 \n\t" \ + "adds r11, r11, r1 \n\t" \ + "adcs r12, r12, r10 \n\t" \ + "adc r8, r8, #0 \n\t" \ + "umull r1, r10, r5, r5 \n\t" \ + "adds r11, r11, r1 \n\t" \ + "adcs r12, r12, r10 \n\t" \ + "adc r8, r8, #0 \n\t" \ + "stmia r0!, {r11} \n\t" \ + \ + "mov r11, #0 \n\t" \ + "umull r1, r10, r5, r6 \n\t" \ + "adds r1, r1, r1 \n\t" \ + "adcs r10, r10, r10 \n\t" \ + "adc r11, r11, #0 \n\t" \ + "adds r12, r12, r1 \n\t" \ + "adcs r8, r8, r10 \n\t" \ + "adc r11, r11, #0 \n\t" \ + "stmia r0!, {r12} \n\t" \ + \ + "umull r1, r10, r6, r6 \n\t" \ + "adds r8, r8, r1 \n\t" \ + "adcs r11, r11, r10 \n\t" \ + "stmia r0!, {r8, r11} \n\t" \ + "pop {r1, r2} \n\t" + +#define FAST_SQUARE_ASM_5_TO_6 \ + "cmp r2, #5 \n\t" \ + "beq 1f \n\t" \ + \ + "sub r0, #20 \n\t" \ + "sub r1, #20 \n\t" \ + \ + /* Do off-center multiplication */ \ + "ldmia r1!, {r6,r7,r8,r9,r10,r11} \n\t" \ + "umull r3, r4, r6, r11 \n\t" \ + "umull r6, r5, r7, r11 \n\t" \ + "adds r4, r4, r6 \n\t" \ + "umull r7, r6, r8, r11 \n\t" \ + "adcs r5, r5, r7 \n\t" \ + "umull r8, r7, r9, r11 \n\t" \ + "adcs r6, r6, r8 \n\t" \ + "umull r9, r8, r10, r11 \n\t" \ + "adcs r7, r7, r9 \n\t" \ + "adcs r8, r8, #0 \n\t" \ + \ + /* Multiply by 2 */ \ + "mov r9, #0 \n\t" \ + "adds r3, r3, r3 \n\t" \ + "adcs r4, r4, r4 \n\t" \ + "adcs r5, r5, r5 \n\t" \ + "adcs r6, r6, r6 \n\t" \ + "adcs r7, r7, r7 \n\t" \ + "adcs r8, r8, r8 \n\t" \ + "adcs r9, r9, #0 \n\t" \ + \ + /* Add into previous */ \ + "ldr r14, [r0], #4 \n\t" \ + "adds r3, r3, r14 \n\t" \ + "ldr r14, [r0], #4 \n\t" \ + "adcs r4, r4, r14 \n\t" \ + "ldr r14, [r0], #4 \n\t" \ + "adcs r5, r5, r14 \n\t" \ + "ldr r14, [r0], #4 \n\t" \ + "adcs r6, r6, r14 \n\t" \ + "ldr r14, [r0], #4 \n\t" \ + "adcs r7, r7, r14 \n\t" \ + "adcs r8, r8, #0 \n\t" \ + "adcs r9, r9, #0 \n\t" \ + "sub r0, #20 \n\t" \ + \ + /* Perform center multiplication */ \ + "umlal r8, r9, r11, r11 \n\t" \ + "stmia r0!, {r3,r4,r5,r6,r7,r8,r9} \n\t" + +#define FAST_SQUARE_ASM_6 \ + "push {r2} \n\t" \ + "ldmia r1!, {r2,r3,r4,r5,r6,r7} \n\t" \ + "push {r1} \n\t" \ + \ + "umull r11, r12, r2, r2 \n\t" \ + "stmia r0!, {r11} \n\t" \ + \ + "mov r9, #0 \n\t" \ + "umull r10, r11, r2, r3 \n\t" \ + "adds r12, r12, r10 \n\t" \ + "adcs r8, r11, #0 \n\t" \ + "adc r9, r9, #0 \n\t" \ + "adds r12, r12, r10 \n\t" \ + "adcs r8, r8, r11 \n\t" \ + "adc r9, r9, #0 \n\t" \ + "stmia r0!, {r12} \n\t" \ + \ + "mov r10, #0 \n\t" \ + "umull r11, r12, r2, r4 \n\t" \ + "adds r11, r11, r11 \n\t" \ + "adcs r12, r12, r12 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "adds r8, r8, r11 \n\t" \ + "adcs r9, r9, r12 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "umull r11, r12, r3, r3 \n\t" \ + "adds r8, r8, r11 \n\t" \ + "adcs r9, r9, r12 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "stmia r0!, {r8} \n\t" \ + \ + "mov r12, #0 \n\t" \ + "umull r8, r11, r2, r5 \n\t" \ + "umull r1, r14, r3, r4 \n\t" \ + "adds r8, r8, r1 \n\t" \ + "adcs r11, r11, r14 \n\t" \ + "adc r12, r12, #0 \n\t" \ + "adds r8, r8, r8 \n\t" \ + "adcs r11, r11, r11 \n\t" \ + "adc r12, r12, r12 \n\t" \ + "adds r8, r8, r9 \n\t" \ + "adcs r11, r11, r10 \n\t" \ + "adc r12, r12, #0 \n\t" \ + "stmia r0!, {r8} \n\t" \ + \ + "mov r10, #0 \n\t" \ + "umull r8, r9, r2, r6 \n\t" \ + "umull r1, r14, r3, r5 \n\t" \ + "adds r8, r8, r1 \n\t" \ + "adcs r9, r9, r14 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "adds r8, r8, r8 \n\t" \ + "adcs r9, r9, r9 \n\t" \ + "adc r10, r10, r10 \n\t" \ + "umull r1, r14, r4, r4 \n\t" \ + "adds r8, r8, r1 \n\t" \ + "adcs r9, r9, r14 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "adds r8, r8, r11 \n\t" \ + "adcs r9, r9, r12 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "stmia r0!, {r8} \n\t" \ + \ + "mov r12, #0 \n\t" \ + "umull r8, r11, r2, r7 \n\t" \ + "umull r1, r14, r3, r6 \n\t" \ + "adds r8, r8, r1 \n\t" \ + "adcs r11, r11, r14 \n\t" \ + "adc r12, r12, #0 \n\t" \ + "umull r1, r14, r4, r5 \n\t" \ + "adds r8, r8, r1 \n\t" \ + "adcs r11, r11, r14 \n\t" \ + "adc r12, r12, #0 \n\t" \ + "adds r8, r8, r8 \n\t" \ + "adcs r11, r11, r11 \n\t" \ + "adc r12, r12, r12 \n\t" \ + "adds r8, r8, r9 \n\t" \ + "adcs r11, r11, r10 \n\t" \ + "adc r12, r12, #0 \n\t" \ + "stmia r0!, {r8} \n\t" \ + \ + "mov r10, #0 \n\t" \ + "umull r8, r9, r3, r7 \n\t" \ + "umull r1, r14, r4, r6 \n\t" \ + "adds r8, r8, r1 \n\t" \ + "adcs r9, r9, r14 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "adds r8, r8, r8 \n\t" \ + "adcs r9, r9, r9 \n\t" \ + "adc r10, r10, r10 \n\t" \ + "umull r1, r14, r5, r5 \n\t" \ + "adds r8, r8, r1 \n\t" \ + "adcs r9, r9, r14 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "adds r8, r8, r11 \n\t" \ + "adcs r9, r9, r12 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "stmia r0!, {r8} \n\t" \ + \ + "mov r12, #0 \n\t" \ + "umull r8, r11, r4, r7 \n\t" \ + "umull r1, r14, r5, r6 \n\t" \ + "adds r8, r8, r1 \n\t" \ + "adcs r11, r11, r14 \n\t" \ + "adc r12, r12, #0 \n\t" \ + "adds r8, r8, r8 \n\t" \ + "adcs r11, r11, r11 \n\t" \ + "adc r12, r12, r12 \n\t" \ + "adds r8, r8, r9 \n\t" \ + "adcs r11, r11, r10 \n\t" \ + "adc r12, r12, #0 \n\t" \ + "stmia r0!, {r8} \n\t" \ + \ + "mov r8, #0 \n\t" \ + "umull r1, r10, r5, r7 \n\t" \ + "adds r1, r1, r1 \n\t" \ + "adcs r10, r10, r10 \n\t" \ + "adc r8, r8, #0 \n\t" \ + "adds r11, r11, r1 \n\t" \ + "adcs r12, r12, r10 \n\t" \ + "adc r8, r8, #0 \n\t" \ + "umull r1, r10, r6, r6 \n\t" \ + "adds r11, r11, r1 \n\t" \ + "adcs r12, r12, r10 \n\t" \ + "adc r8, r8, #0 \n\t" \ + "stmia r0!, {r11} \n\t" \ + \ + "mov r11, #0 \n\t" \ + "umull r1, r10, r6, r7 \n\t" \ + "adds r1, r1, r1 \n\t" \ + "adcs r10, r10, r10 \n\t" \ + "adc r11, r11, #0 \n\t" \ + "adds r12, r12, r1 \n\t" \ + "adcs r8, r8, r10 \n\t" \ + "adc r11, r11, #0 \n\t" \ + "stmia r0!, {r12} \n\t" \ + \ + "umull r1, r10, r7, r7 \n\t" \ + "adds r8, r8, r1 \n\t" \ + "adcs r11, r11, r10 \n\t" \ + "stmia r0!, {r8, r11} \n\t" \ + "pop {r1, r2} \n\t" + +#define FAST_SQUARE_ASM_6_TO_7 \ + "cmp r2, #6 \n\t" \ + "beq 1f \n\t" \ + \ + "sub r0, #24 \n\t" \ + "sub r1, #24 \n\t" \ + \ + /* Do off-center multiplication */ \ + "ldmia r1!, {r6,r7,r8,r9,r10,r11,r12} \n\t" \ + "umull r3, r4, r6, r12 \n\t" \ + "umull r6, r5, r7, r12 \n\t" \ + "adds r4, r4, r6 \n\t" \ + "umull r7, r6, r8, r12 \n\t" \ + "adcs r5, r5, r7 \n\t" \ + "umull r8, r7, r9, r12 \n\t" \ + "adcs r6, r6, r8 \n\t" \ + "umull r9, r8, r10, r12 \n\t" \ + "adcs r7, r7, r9 \n\t" \ + "umull r10, r9, r11, r12 \n\t" \ + "adcs r8, r8, r10 \n\t" \ + "adcs r9, r9, #0 \n\t" \ + \ + /* Multiply by 2 */ \ + "mov r10, #0 \n\t" \ + "adds r3, r3, r3 \n\t" \ + "adcs r4, r4, r4 \n\t" \ + "adcs r5, r5, r5 \n\t" \ + "adcs r6, r6, r6 \n\t" \ + "adcs r7, r7, r7 \n\t" \ + "adcs r8, r8, r8 \n\t" \ + "adcs r9, r9, r9 \n\t" \ + "adcs r10, r10, #0 \n\t" \ + \ + /* Add into previous */ \ + "ldr r14, [r0], #4 \n\t" \ + "adds r3, r3, r14 \n\t" \ + "ldr r14, [r0], #4 \n\t" \ + "adcs r4, r4, r14 \n\t" \ + "ldr r14, [r0], #4 \n\t" \ + "adcs r5, r5, r14 \n\t" \ + "ldr r14, [r0], #4 \n\t" \ + "adcs r6, r6, r14 \n\t" \ + "ldr r14, [r0], #4 \n\t" \ + "adcs r7, r7, r14 \n\t" \ + "ldr r14, [r0], #4 \n\t" \ + "adcs r8, r8, r14 \n\t" \ + "adcs r9, r9, #0 \n\t" \ + "adcs r10, r10, #0 \n\t" \ + "sub r0, #24 \n\t" \ + \ + /* Perform center multiplication */ \ + "umlal r9, r10, r12, r12 \n\t" \ + "stmia r0!, {r3,r4,r5,r6,r7,r8,r9,r10} \n\t" + +#define FAST_SQUARE_ASM_7 \ + "push {r2} \n\t" \ + "ldmia r1!, {r2, r3, r4, r5, r6, r7, r8} \n\t" \ + "push {r1} \n\t" \ + "sub r1, 4 \n\t" \ + \ + "add r0, 24 \n\t" \ + "umull r9, r10, r2, r8 \n\t" \ + "stmia r0!, {r9, r10} \n\t" \ + "sub r0, 32 \n\t" \ + \ + "umull r11, r12, r2, r2 \n\t" \ + "stmia r0!, {r11} \n\t" \ + \ + "mov r9, #0 \n\t" \ + "umull r10, r11, r2, r3 \n\t" \ + "adds r12, r12, r10 \n\t" \ + "adcs r8, r11, #0 \n\t" \ + "adc r9, r9, #0 \n\t" \ + "adds r12, r12, r10 \n\t" \ + "adcs r8, r8, r11 \n\t" \ + "adc r9, r9, #0 \n\t" \ + "stmia r0!, {r12} \n\t" \ + \ + "mov r10, #0 \n\t" \ + "umull r11, r12, r2, r4 \n\t" \ + "adds r11, r11, r11 \n\t" \ + "adcs r12, r12, r12 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "adds r8, r8, r11 \n\t" \ + "adcs r9, r9, r12 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "umull r11, r12, r3, r3 \n\t" \ + "adds r8, r8, r11 \n\t" \ + "adcs r9, r9, r12 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "stmia r0!, {r8} \n\t" \ + \ + "mov r12, #0 \n\t" \ + "umull r8, r11, r2, r5 \n\t" \ + "mov r14, r11 \n\t" \ + "umlal r8, r11, r3, r4 \n\t" \ + "cmp r14, r11 \n\t" \ + "it hi \n\t" \ + "adchi r12, r12, #0 \n\t" \ + "adds r8, r8, r8 \n\t" \ + "adcs r11, r11, r11 \n\t" \ + "adc r12, r12, r12 \n\t" \ + "adds r8, r8, r9 \n\t" \ + "adcs r11, r11, r10 \n\t" \ + "adc r12, r12, #0 \n\t" \ + "stmia r0!, {r8} \n\t" \ + \ + "mov r10, #0 \n\t" \ + "umull r8, r9, r2, r6 \n\t" \ + "mov r14, r9 \n\t" \ + "umlal r8, r9, r3, r5 \n\t" \ + "cmp r14, r9 \n\t" \ + "it hi \n\t" \ + "adchi r10, r10, #0 \n\t" \ + "adds r8, r8, r8 \n\t" \ + "adcs r9, r9, r9 \n\t" \ + "adc r10, r10, r10 \n\t" \ + "mov r14, r9 \n\t" \ + "umlal r8, r9, r4, r4 \n\t" \ + "cmp r14, r9 \n\t" \ + "it hi \n\t" \ + "adchi r10, r10, #0 \n\t" \ + "adds r8, r8, r11 \n\t" \ + "adcs r9, r9, r12 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "stmia r0!, {r8} \n\t" \ + \ + "mov r12, #0 \n\t" \ + "umull r8, r11, r2, r7 \n\t" \ + "mov r14, r11 \n\t" \ + "umlal r8, r11, r3, r6 \n\t" \ + "cmp r14, r11 \n\t" \ + "it hi \n\t" \ + "adchi r12, r12, #0 \n\t" \ + "mov r14, r11 \n\t" \ + "umlal r8, r11, r4, r5 \n\t" \ + "cmp r14, r11 \n\t" \ + "it hi \n\t" \ + "adchi r12, r12, #0 \n\t" \ + "adds r8, r8, r8 \n\t" \ + "adcs r11, r11, r11 \n\t" \ + "adc r12, r12, r12 \n\t" \ + "adds r8, r8, r9 \n\t" \ + "adcs r11, r11, r10 \n\t" \ + "adc r12, r12, #0 \n\t" \ + "stmia r0!, {r8} \n\t" \ + \ + "ldmia r1!, {r2} \n\t" \ + "mov r10, #0 \n\t" \ + "umull r8, r9, r3, r7 \n\t" \ + "mov r14, r9 \n\t" \ + "umlal r8, r9, r4, r6 \n\t" \ + "cmp r14, r9 \n\t" \ + "it hi \n\t" \ + "adchi r10, r10, #0 \n\t" \ + "ldr r14, [r0] \n\t" \ + "adds r8, r8, r14 \n\t" \ + "adcs r9, r9, #0 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "adds r8, r8, r8 \n\t" \ + "adcs r9, r9, r9 \n\t" \ + "adc r10, r10, r10 \n\t" \ + "mov r14, r9 \n\t" \ + "umlal r8, r9, r5, r5 \n\t" \ + "cmp r14, r9 \n\t" \ + "it hi \n\t" \ + "adchi r10, r10, #0 \n\t" \ + "adds r8, r8, r11 \n\t" \ + "adcs r9, r9, r12 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "stmia r0!, {r8} \n\t" \ + \ + "mov r12, #0 \n\t" \ + "umull r8, r11, r3, r2 \n\t" \ + "mov r14, r11 \n\t" \ + "umlal r8, r11, r4, r7 \n\t" \ + "cmp r14, r11 \n\t" \ + "it hi \n\t" \ + "adchi r12, r12, #0 \n\t" \ + "mov r14, r11 \n\t" \ + "umlal r8, r11, r5, r6 \n\t" \ + "cmp r14, r11 \n\t" \ + "it hi \n\t" \ + "adchi r12, r12, #0 \n\t" \ + "ldr r14, [r0] \n\t" \ + "adds r8, r8, r14 \n\t" \ + "adcs r11, r11, #0 \n\t" \ + "adc r12, r12, #0 \n\t" \ + "adds r8, r8, r8 \n\t" \ + "adcs r11, r11, r11 \n\t" \ + "adc r12, r12, r12 \n\t" \ + "adds r8, r8, r9 \n\t" \ + "adcs r11, r11, r10 \n\t" \ + "adc r12, r12, #0 \n\t" \ + "stmia r0!, {r8} \n\t" \ + \ + "mov r10, #0 \n\t" \ + "umull r8, r9, r4, r2 \n\t" \ + "mov r14, r9 \n\t" \ + "umlal r8, r9, r5, r7 \n\t" \ + "cmp r14, r9 \n\t" \ + "it hi \n\t" \ + "adchi r10, r10, #0 \n\t" \ + "adds r8, r8, r8 \n\t" \ + "adcs r9, r9, r9 \n\t" \ + "adc r10, r10, r10 \n\t" \ + "mov r14, r9 \n\t" \ + "umlal r8, r9, r6, r6 \n\t" \ + "cmp r14, r9 \n\t" \ + "it hi \n\t" \ + "adchi r10, r10, #0 \n\t" \ + "adds r8, r8, r11 \n\t" \ + "adcs r9, r9, r12 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "stmia r0!, {r8} \n\t" \ + \ + "mov r12, #0 \n\t" \ + "umull r8, r11, r5, r2 \n\t" \ + "mov r14, r11 \n\t" \ + "umlal r8, r11, r6, r7 \n\t" \ + "cmp r14, r11 \n\t" \ + "it hi \n\t" \ + "adchi r12, r12, #0 \n\t" \ + "adds r8, r8, r8 \n\t" \ + "adcs r11, r11, r11 \n\t" \ + "adc r12, r12, r12 \n\t" \ + "adds r8, r8, r9 \n\t" \ + "adcs r11, r11, r10 \n\t" \ + "adc r12, r12, #0 \n\t" \ + "stmia r0!, {r8} \n\t" \ + \ + "mov r8, #0 \n\t" \ + "umull r1, r10, r6, r2 \n\t" \ + "adds r1, r1, r1 \n\t" \ + "adcs r10, r10, r10 \n\t" \ + "adc r8, r8, #0 \n\t" \ + "adds r11, r11, r1 \n\t" \ + "adcs r12, r12, r10 \n\t" \ + "adc r8, r8, #0 \n\t" \ + "umull r1, r10, r7, r7 \n\t" \ + "adds r11, r11, r1 \n\t" \ + "adcs r12, r12, r10 \n\t" \ + "adc r8, r8, #0 \n\t" \ + "stmia r0!, {r11} \n\t" \ + \ + "mov r11, #0 \n\t" \ + "umull r1, r10, r7, r2 \n\t" \ + "adds r1, r1, r1 \n\t" \ + "adcs r10, r10, r10 \n\t" \ + "adc r11, r11, #0 \n\t" \ + "adds r12, r12, r1 \n\t" \ + "adcs r8, r8, r10 \n\t" \ + "adc r11, r11, #0 \n\t" \ + "stmia r0!, {r12} \n\t" \ + \ + "umull r1, r10, r2, r2 \n\t" \ + "adds r8, r8, r1 \n\t" \ + "adcs r11, r11, r10 \n\t" \ + "stmia r0!, {r8, r11} \n\t" \ + "pop {r1, r2} \n\t" + +#define FAST_SQUARE_ASM_7_TO_8 \ + "cmp r2, #7 \n\t" \ + "beq 1f \n\t" \ + \ + "sub r0, #28 \n\t" \ + "sub r1, #28 \n\t" \ + \ + /* Do off-center multiplication */ \ + "ldmia r1!, {r6,r7,r8,r9,r10,r11,r12,r14} \n\t" \ + "umull r3, r4, r6, r14 \n\t" \ + "umull r6, r5, r7, r14 \n\t" \ + "adds r4, r4, r6 \n\t" \ + "umull r7, r6, r8, r14 \n\t" \ + "adcs r5, r5, r7 \n\t" \ + "umull r8, r7, r9, r14 \n\t" \ + "adcs r6, r6, r8 \n\t" \ + "umull r9, r8, r10, r14 \n\t" \ + "adcs r7, r7, r9 \n\t" \ + "umull r10, r9, r11, r14 \n\t" \ + "adcs r8, r8, r10 \n\t" \ + "umull r11, r10, r12, r14 \n\t" \ + "adcs r9, r9, r11 \n\t" \ + "adcs r10, r10, #0 \n\t" \ + \ + /* Multiply by 2 */ \ + "mov r11, #0 \n\t" \ + "adds r3, r3, r3 \n\t" \ + "adcs r4, r4, r4 \n\t" \ + "adcs r5, r5, r5 \n\t" \ + "adcs r6, r6, r6 \n\t" \ + "adcs r7, r7, r7 \n\t" \ + "adcs r8, r8, r8 \n\t" \ + "adcs r9, r9, r9 \n\t" \ + "adcs r10, r10, r10 \n\t" \ + "adcs r11, r11, #0 \n\t" \ + \ + /* Add into previous */ \ + "ldr r12, [r0], #4 \n\t" \ + "adds r3, r3, r12 \n\t" \ + "ldr r12, [r0], #4 \n\t" \ + "adcs r4, r4, r12 \n\t" \ + "ldr r12, [r0], #4 \n\t" \ + "adcs r5, r5, r12 \n\t" \ + "ldr r12, [r0], #4 \n\t" \ + "adcs r6, r6, r12 \n\t" \ + "ldr r12, [r0], #4 \n\t" \ + "adcs r7, r7, r12 \n\t" \ + "ldr r12, [r0], #4 \n\t" \ + "adcs r8, r8, r12 \n\t" \ + "ldr r12, [r0], #4 \n\t" \ + "adcs r9, r9, r12 \n\t" \ + "adcs r10, r10, #0 \n\t" \ + "adcs r11, r11, #0 \n\t" \ + "sub r0, #28 \n\t" \ + \ + /* Perform center multiplication */ \ + "umlal r10, r11, r14, r14 \n\t" \ + "stmia r0!, {r3,r4,r5,r6,r7,r8,r9,r10,r11} \n\t" + +#define FAST_SQUARE_ASM_8 \ + "push {r2} \n\t" \ + "ldmia r1!, {r2,r3,r4,r5,r6,r7,r8,r9} \n\t" \ + "push {r1} \n\t" \ + "sub r1, 8 \n\t" \ + \ + "add r0, 24 \n\t" \ + "umull r10, r11, r2, r8 \n\t" \ + "umull r12, r14, r2, r9 \n\t" \ + "umull r8, r9, r3, r9 \n\t" \ + "adds r11, r11, r12 \n\t" \ + "adcs r12, r14, r8 \n\t" \ + "adcs r14, r9, #0 \n\t" \ + "stmia r0!, {r10, r11, r12, r14} \n\t" \ + "sub r0, 40 \n\t" \ + \ + "umull r11, r12, r2, r2 \n\t" \ + "stmia r0!, {r11} \n\t" \ + \ + "mov r9, #0 \n\t" \ + "umull r10, r11, r2, r3 \n\t" \ + "adds r12, r12, r10 \n\t" \ + "adcs r8, r11, #0 \n\t" \ + "adc r9, r9, #0 \n\t" \ + "adds r12, r12, r10 \n\t" \ + "adcs r8, r8, r11 \n\t" \ + "adc r9, r9, #0 \n\t" \ + "stmia r0!, {r12} \n\t" \ + \ + "mov r10, #0 \n\t" \ + "umull r11, r12, r2, r4 \n\t" \ + "adds r11, r11, r11 \n\t" \ + "adcs r12, r12, r12 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "adds r8, r8, r11 \n\t" \ + "adcs r9, r9, r12 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "umull r11, r12, r3, r3 \n\t" \ + "adds r8, r8, r11 \n\t" \ + "adcs r9, r9, r12 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "stmia r0!, {r8} \n\t" \ + \ + "mov r12, #0 \n\t" \ + "umull r8, r11, r2, r5 \n\t" \ + "mov r14, r11 \n\t" \ + "umlal r8, r11, r3, r4 \n\t" \ + "cmp r14, r11 \n\t" \ + "it hi \n\t" \ + "adchi r12, r12, #0 \n\t" \ + "adds r8, r8, r8 \n\t" \ + "adcs r11, r11, r11 \n\t" \ + "adc r12, r12, r12 \n\t" \ + "adds r8, r8, r9 \n\t" \ + "adcs r11, r11, r10 \n\t" \ + "adc r12, r12, #0 \n\t" \ + "stmia r0!, {r8} \n\t" \ + \ + "mov r10, #0 \n\t" \ + "umull r8, r9, r2, r6 \n\t" \ + "mov r14, r9 \n\t" \ + "umlal r8, r9, r3, r5 \n\t" \ + "cmp r14, r9 \n\t" \ + "it hi \n\t" \ + "adchi r10, r10, #0 \n\t" \ + "adds r8, r8, r8 \n\t" \ + "adcs r9, r9, r9 \n\t" \ + "adc r10, r10, r10 \n\t" \ + "mov r14, r9 \n\t" \ + "umlal r8, r9, r4, r4 \n\t" \ + "cmp r14, r9 \n\t" \ + "it hi \n\t" \ + "adchi r10, r10, #0 \n\t" \ + "adds r8, r8, r11 \n\t" \ + "adcs r9, r9, r12 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "stmia r0!, {r8} \n\t" \ + \ + "mov r12, #0 \n\t" \ + "umull r8, r11, r2, r7 \n\t" \ + "mov r14, r11 \n\t" \ + "umlal r8, r11, r3, r6 \n\t" \ + "cmp r14, r11 \n\t" \ + "it hi \n\t" \ + "adchi r12, r12, #0 \n\t" \ + "mov r14, r11 \n\t" \ + "umlal r8, r11, r4, r5 \n\t" \ + "cmp r14, r11 \n\t" \ + "it hi \n\t" \ + "adchi r12, r12, #0 \n\t" \ + "adds r8, r8, r8 \n\t" \ + "adcs r11, r11, r11 \n\t" \ + "adc r12, r12, r12 \n\t" \ + "adds r8, r8, r9 \n\t" \ + "adcs r11, r11, r10 \n\t" \ + "adc r12, r12, #0 \n\t" \ + "stmia r0!, {r8} \n\t" \ + \ + "ldmia r1!, {r2} \n\t" \ + "mov r10, #0 \n\t" \ + "umull r8, r9, r3, r7 \n\t" \ + "mov r14, r9 \n\t" \ + "umlal r8, r9, r4, r6 \n\t" \ + "cmp r14, r9 \n\t" \ + "it hi \n\t" \ + "adchi r10, r10, #0 \n\t" \ + "ldr r14, [r0] \n\t" \ + "adds r8, r8, r14 \n\t" \ + "adcs r9, r9, #0 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "adds r8, r8, r8 \n\t" \ + "adcs r9, r9, r9 \n\t" \ + "adc r10, r10, r10 \n\t" \ + "mov r14, r9 \n\t" \ + "umlal r8, r9, r5, r5 \n\t" \ + "cmp r14, r9 \n\t" \ + "it hi \n\t" \ + "adchi r10, r10, #0 \n\t" \ + "adds r8, r8, r11 \n\t" \ + "adcs r9, r9, r12 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "stmia r0!, {r8} \n\t" \ + \ + "mov r12, #0 \n\t" \ + "umull r8, r11, r3, r2 \n\t" \ + "mov r14, r11 \n\t" \ + "umlal r8, r11, r4, r7 \n\t" \ + "cmp r14, r11 \n\t" \ + "it hi \n\t" \ + "adchi r12, r12, #0 \n\t" \ + "mov r14, r11 \n\t" \ + "umlal r8, r11, r5, r6 \n\t" \ + "cmp r14, r11 \n\t" \ + "it hi \n\t" \ + "adchi r12, r12, #0 \n\t" \ + "ldr r14, [r0] \n\t" \ + "adds r8, r8, r14 \n\t" \ + "adcs r11, r11, #0 \n\t" \ + "adc r12, r12, #0 \n\t" \ + "adds r8, r8, r8 \n\t" \ + "adcs r11, r11, r11 \n\t" \ + "adc r12, r12, r12 \n\t" \ + "adds r8, r8, r9 \n\t" \ + "adcs r11, r11, r10 \n\t" \ + "adc r12, r12, #0 \n\t" \ + "stmia r0!, {r8} \n\t" \ + \ + "ldmia r1!, {r3} \n\t" \ + "mov r10, #0 \n\t" \ + "umull r8, r9, r4, r2 \n\t" \ + "mov r14, r9 \n\t" \ + "umlal r8, r9, r5, r7 \n\t" \ + "cmp r14, r9 \n\t" \ + "it hi \n\t" \ + "adchi r10, r10, #0 \n\t" \ + "ldr r14, [r0] \n\t" \ + "adds r8, r8, r14 \n\t" \ + "adcs r9, r9, #0 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "adds r8, r8, r8 \n\t" \ + "adcs r9, r9, r9 \n\t" \ + "adc r10, r10, r10 \n\t" \ + "mov r14, r9 \n\t" \ + "umlal r8, r9, r6, r6 \n\t" \ + "cmp r14, r9 \n\t" \ + "it hi \n\t" \ + "adchi r10, r10, #0 \n\t" \ + "adds r8, r8, r11 \n\t" \ + "adcs r9, r9, r12 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "stmia r0!, {r8} \n\t" \ + \ + "mov r12, #0 \n\t" \ + "umull r8, r11, r4, r3 \n\t" \ + "mov r14, r11 \n\t" \ + "umlal r8, r11, r5, r2 \n\t" \ + "cmp r14, r11 \n\t" \ + "it hi \n\t" \ + "adchi r12, r12, #0 \n\t" \ + "mov r14, r11 \n\t" \ + "umlal r8, r11, r6, r7 \n\t" \ + "cmp r14, r11 \n\t" \ + "it hi \n\t" \ + "adchi r12, r12, #0 \n\t" \ + "ldr r14, [r0] \n\t" \ + "adds r8, r8, r14 \n\t" \ + "adcs r11, r11, #0 \n\t" \ + "adc r12, r12, #0 \n\t" \ + "adds r8, r8, r8 \n\t" \ + "adcs r11, r11, r11 \n\t" \ + "adc r12, r12, r12 \n\t" \ + "adds r8, r8, r9 \n\t" \ + "adcs r11, r11, r10 \n\t" \ + "adc r12, r12, #0 \n\t" \ + "stmia r0!, {r8} \n\t" \ + \ + "mov r10, #0 \n\t" \ + "umull r8, r9, r5, r3 \n\t" \ + "mov r14, r9 \n\t" \ + "umlal r8, r9, r6, r2 \n\t" \ + "cmp r14, r9 \n\t" \ + "it hi \n\t" \ + "adchi r10, r10, #0 \n\t" \ + "adds r8, r8, r8 \n\t" \ + "adcs r9, r9, r9 \n\t" \ + "adc r10, r10, r10 \n\t" \ + "mov r14, r9 \n\t" \ + "umlal r8, r9, r7, r7 \n\t" \ + "cmp r14, r9 \n\t" \ + "it hi \n\t" \ + "adchi r10, r10, #0 \n\t" \ + "adds r8, r8, r11 \n\t" \ + "adcs r9, r9, r12 \n\t" \ + "adc r10, r10, #0 \n\t" \ + "stmia r0!, {r8} \n\t" \ + \ + "mov r12, #0 \n\t" \ + "umull r8, r11, r6, r3 \n\t" \ + "mov r14, r11 \n\t" \ + "umlal r8, r11, r7, r2 \n\t" \ + "cmp r14, r11 \n\t" \ + "it hi \n\t" \ + "adchi r12, r12, #0 \n\t" \ + "adds r8, r8, r8 \n\t" \ + "adcs r11, r11, r11 \n\t" \ + "adc r12, r12, r12 \n\t" \ + "adds r8, r8, r9 \n\t" \ + "adcs r11, r11, r10 \n\t" \ + "adc r12, r12, #0 \n\t" \ + "stmia r0!, {r8} \n\t" \ + \ + "mov r8, #0 \n\t" \ + "umull r1, r10, r7, r3 \n\t" \ + "adds r1, r1, r1 \n\t" \ + "adcs r10, r10, r10 \n\t" \ + "adc r8, r8, #0 \n\t" \ + "adds r11, r11, r1 \n\t" \ + "adcs r12, r12, r10 \n\t" \ + "adc r8, r8, #0 \n\t" \ + "umull r1, r10, r2, r2 \n\t" \ + "adds r11, r11, r1 \n\t" \ + "adcs r12, r12, r10 \n\t" \ + "adc r8, r8, #0 \n\t" \ + "stmia r0!, {r11} \n\t" \ + \ + "mov r11, #0 \n\t" \ + "umull r1, r10, r2, r3 \n\t" \ + "adds r1, r1, r1 \n\t" \ + "adcs r10, r10, r10 \n\t" \ + "adc r11, r11, #0 \n\t" \ + "adds r12, r12, r1 \n\t" \ + "adcs r8, r8, r10 \n\t" \ + "adc r11, r11, #0 \n\t" \ + "stmia r0!, {r12} \n\t" \ + \ + "umull r1, r10, r3, r3 \n\t" \ + "adds r8, r8, r1 \n\t" \ + "adcs r11, r11, r10 \n\t" \ + "stmia r0!, {r8, r11} \n\t" \ + "pop {r1, r2} \n\t" + +#endif /* _UECC_ASM_ARM_MULT_SQUARE_H_ */ diff --git a/deps/picoquic/picotls/deps/micro-ecc/asm_arm_mult_square_umaal.inc b/deps/picoquic/picotls/deps/micro-ecc/asm_arm_mult_square_umaal.inc new file mode 100644 index 0000000..c554d20 --- /dev/null +++ b/deps/picoquic/picotls/deps/micro-ecc/asm_arm_mult_square_umaal.inc @@ -0,0 +1,1202 @@ +/* Copyright 2015, Kenneth MacKay. Licensed under the BSD 2-clause license. */ + +#ifndef _UECC_ASM_ARM_MULT_SQUARE_H_ +#define _UECC_ASM_ARM_MULT_SQUARE_H_ + +#define FAST_MULT_ASM_5 \ + "push {r3} \n\t" \ + "ldmia r2!, {r3, r4, r5, r6, r7} \n\t" \ + "push {r2} \n\t" \ + \ + "ldr r2, [r1], #4 \n\t" \ + "umull r8, r9, r3, r2 \n\t" \ + "str r8, [r0], #4 \n\t" \ + "mov r10, #0 \n\t" \ + "umaal r9, r10, r4, r2 \n\t" \ + "mov r11, #0 \n\t" \ + "umaal r10, r11, r5, r2 \n\t" \ + "mov r12, #0 \n\t" \ + "umaal r11, r12, r6, r2 \n\t" \ + "mov r14, #0 \n\t" \ + "umaal r12, r14, r7, r2 \n\t" \ + \ + "ldr r2, [r1], #4 \n\t" \ + "mov r8, #0 \n\t" \ + "umaal r8, r9, r3, r2 \n\t" \ + "str r8, [r0], #4 \n\t" \ + "umaal r9, r10, r4, r2 \n\t" \ + "umaal r10, r11, r5, r2 \n\t" \ + "umaal r11, r12, r6, r2 \n\t" \ + "umaal r12, r14, r7, r2 \n\t" \ + \ + "ldr r2, [r1], #4 \n\t" \ + "mov r8, #0 \n\t" \ + "umaal r8, r9, r3, r2 \n\t" \ + "str r8, [r0], #4 \n\t" \ + "umaal r9, r10, r4, r2 \n\t" \ + "umaal r10, r11, r5, r2 \n\t" \ + "umaal r11, r12, r6, r2 \n\t" \ + "umaal r12, r14, r7, r2 \n\t" \ + \ + "ldr r2, [r1], #4 \n\t" \ + "mov r8, #0 \n\t" \ + "umaal r8, r9, r3, r2 \n\t" \ + "str r8, [r0], #4 \n\t" \ + "umaal r9, r10, r4, r2 \n\t" \ + "umaal r10, r11, r5, r2 \n\t" \ + "umaal r11, r12, r6, r2 \n\t" \ + "umaal r12, r14, r7, r2 \n\t" \ + \ + "ldr r2, [r1], #4 \n\t" \ + "mov r8, #0 \n\t" \ + "umaal r8, r9, r3, r2 \n\t" \ + "str r8, [r0], #4 \n\t" \ + "umaal r9, r10, r4, r2 \n\t" \ + "umaal r10, r11, r5, r2 \n\t" \ + "umaal r11, r12, r6, r2 \n\t" \ + "umaal r12, r14, r7, r2 \n\t" \ + \ + "str r9, [r0], #4 \n\t" \ + "str r10, [r0], #4 \n\t" \ + "str r11, [r0], #4 \n\t" \ + "str r12, [r0], #4 \n\t" \ + "str r14, [r0], #4 \n\t" \ + \ + "pop {r2, r3} \n\t" + +#define FAST_MULT_ASM_5_TO_6 \ + "cmp r3, #5 \n\t" \ + "beq 1f \n\t" \ + \ + /* r4 = left high */ \ + "ldr r4, [r1] \n\t" \ + \ + "sub r0, #20 \n\t" \ + "sub r1, #20 \n\t" \ + "sub r2, #20 \n\t" \ + \ + /* Do right side */ \ + "ldr r14, [r2], #4 \n\t" \ + "mov r5, #0 \n\t" \ + "ldr r6, [r0], #4 \n\t" \ + "umaal r5, r6, r4, r14 \n\t" \ + "ldr r14, [r2], #4 \n\t" \ + "ldr r7, [r0], #4 \n\t" \ + "umaal r6, r7, r4, r14 \n\t" \ + "ldr r14, [r2], #4 \n\t" \ + "ldr r8, [r0], #4 \n\t" \ + "umaal r7, r8, r4, r14 \n\t" \ + "ldr r14, [r2], #4 \n\t" \ + "ldr r9, [r0], #4 \n\t" \ + "umaal r8, r9, r4, r14 \n\t" \ + "ldr r14, [r2], #4 \n\t" \ + "ldr r10, [r0], #4 \n\t" \ + "umaal r9, r10, r4, r14 \n\t" \ + "sub r0, #20 \n\t" \ + \ + /* r4 = right high */ \ + "ldr r4, [r2], #4 \n\t" \ + \ + /* Do left side */ \ + "ldr r14, [r1], #4 \n\t" \ + "mov r12, #0 \n\t" \ + "umaal r12, r5, r4, r14 \n\t" \ + "str r12, [r0], #4 \n\t" \ + "ldr r14, [r1], #4 \n\t" \ + "umaal r5, r6, r4, r14 \n\t" \ + "str r5, [r0], #4 \n\t" \ + "ldr r14, [r1], #4 \n\t" \ + "umaal r6, r7, r4, r14 \n\t" \ + "str r6, [r0], #4 \n\t" \ + "ldr r14, [r1], #4 \n\t" \ + "umaal r7, r8, r4, r14 \n\t" \ + "str r7, [r0], #4 \n\t" \ + "ldr r14, [r1], #4 \n\t" \ + "umaal r8, r9, r4, r14 \n\t" \ + "str r8, [r0], #4 \n\t" \ + \ + "ldr r14, [r1], #4 \n\t" \ + "umaal r9, r10, r4, r14 \n\t" \ + "stmia r0!, {r9, r10} \n\t" + +#define FAST_MULT_ASM_6 \ + "ldmia r2!, {r4, r5, r6} \n\t" \ + \ + "ldr r14, [r1], #4 \n\t" \ + "umull r8, r9, r4, r14 \n\t" \ + "str r8, [r0], #4 \n\t" \ + "mov r10, #0 \n\t" \ + "umaal r9, r10, r5, r14 \n\t" \ + "mov r11, #0 \n\t" \ + "umaal r10, r11, r6, r14 \n\t" \ + \ + "ldr r14, [r1], #4 \n\t" \ + "mov r8, #0 \n\t" \ + "umaal r8, r9, r4, r14 \n\t" \ + "str r8, [r0], #4 \n\t" \ + "umaal r9, r10, r5, r14 \n\t" \ + "umaal r10, r11, r6, r14 \n\t" \ + \ + "ldr r14, [r1], #4 \n\t" \ + "mov r8, #0 \n\t" \ + "umaal r8, r9, r4, r14 \n\t" \ + "str r8, [r0], #4 \n\t" \ + "umaal r9, r10, r5, r14 \n\t" \ + "umaal r10, r11, r6, r14 \n\t" \ + \ + "ldr r14, [r1], #4 \n\t" \ + "mov r8, #0 \n\t" \ + "umaal r8, r9, r4, r14 \n\t" \ + "str r8, [r0], #4 \n\t" \ + "umaal r9, r10, r5, r14 \n\t" \ + "umaal r10, r11, r6, r14 \n\t" \ + \ + "ldr r14, [r1], #4 \n\t" \ + "mov r8, #0 \n\t" \ + "umaal r8, r9, r4, r14 \n\t" \ + "str r8, [r0], #4 \n\t" \ + "umaal r9, r10, r5, r14 \n\t" \ + "umaal r10, r11, r6, r14 \n\t" \ + \ + "ldr r14, [r1], #4 \n\t" \ + "mov r8, #0 \n\t" \ + "umaal r8, r9, r4, r14 \n\t" \ + "str r8, [r0], #4 \n\t" \ + "umaal r9, r10, r5, r14 \n\t" \ + "umaal r10, r11, r6, r14 \n\t" \ + \ + "str r9, [r0], #4 \n\t" \ + "str r10, [r0], #4 \n\t" \ + "str r11, [r0], #4 \n\t" \ + \ + "sub r0, #24 \n\t" \ + "sub r1, #24 \n\t" \ + "ldmia r2!, {r4, r5, r6} \n\t" \ + \ + "ldr r14, [r1], #4 \n\t" \ + "ldr r8, [r0] \n\t" \ + "mov r9, #0 \n\t" \ + "umaal r8, r9, r4, r14 \n\t" \ + "str r8, [r0], #4 \n\t" \ + "mov r10, #0 \n\t" \ + "umaal r9, r10, r5, r14 \n\t" \ + "mov r11, #0 \n\t" \ + "umaal r10, r11, r6, r14 \n\t" \ + \ + "ldr r14, [r1], #4 \n\t" \ + "ldr r8, [r0] \n\t" \ + "umaal r8, r9, r4, r14 \n\t" \ + "str r8, [r0], #4 \n\t" \ + "umaal r9, r10, r5, r14 \n\t" \ + "umaal r10, r11, r6, r14 \n\t" \ + \ + "ldr r14, [r1], #4 \n\t" \ + "ldr r8, [r0] \n\t" \ + "umaal r8, r9, r4, r14 \n\t" \ + "str r8, [r0], #4 \n\t" \ + "umaal r9, r10, r5, r14 \n\t" \ + "umaal r10, r11, r6, r14 \n\t" \ + \ + "ldr r14, [r1], #4 \n\t" \ + "ldr r8, [r0] \n\t" \ + "umaal r8, r9, r4, r14 \n\t" \ + "str r8, [r0], #4 \n\t" \ + "umaal r9, r10, r5, r14 \n\t" \ + "umaal r10, r11, r6, r14 \n\t" \ + \ + "ldr r14, [r1], #4 \n\t" \ + "ldr r8, [r0] \n\t" \ + "umaal r8, r9, r4, r14 \n\t" \ + "str r8, [r0], #4 \n\t" \ + "umaal r9, r10, r5, r14 \n\t" \ + "umaal r10, r11, r6, r14 \n\t" \ + \ + "ldr r14, [r1], #4 \n\t" \ + "ldr r8, [r0] \n\t" \ + "umaal r8, r9, r4, r14 \n\t" \ + "str r8, [r0], #4 \n\t" \ + "umaal r9, r10, r5, r14 \n\t" \ + "umaal r10, r11, r6, r14 \n\t" \ + \ + "str r9, [r0], #4 \n\t" \ + "str r10, [r0], #4 \n\t" \ + "str r11, [r0], #4 \n\t" + +#define FAST_MULT_ASM_6_TO_7 \ + "cmp r3, #6 \n\t" \ + "beq 1f \n\t" \ + \ + /* r4 = left high */ \ + "ldr r4, [r1] \n\t" \ + \ + "sub r0, #24 \n\t" \ + "sub r1, #24 \n\t" \ + "sub r2, #24 \n\t" \ + \ + /* Do right side */ \ + "ldr r14, [r2], #4 \n\t" \ + "mov r5, #0 \n\t" \ + "ldr r6, [r0], #4 \n\t" \ + "umaal r5, r6, r4, r14 \n\t" \ + "ldr r14, [r2], #4 \n\t" \ + "ldr r7, [r0], #4 \n\t" \ + "umaal r6, r7, r4, r14 \n\t" \ + "ldr r14, [r2], #4 \n\t" \ + "ldr r8, [r0], #4 \n\t" \ + "umaal r7, r8, r4, r14 \n\t" \ + "ldr r14, [r2], #4 \n\t" \ + "ldr r9, [r0], #4 \n\t" \ + "umaal r8, r9, r4, r14 \n\t" \ + "ldr r14, [r2], #4 \n\t" \ + "ldr r10, [r0], #4 \n\t" \ + "umaal r9, r10, r4, r14 \n\t" \ + "ldr r14, [r2], #4 \n\t" \ + "ldr r11, [r0], #4 \n\t" \ + "umaal r10, r11, r4, r14 \n\t" \ + "sub r0, #24 \n\t" \ + \ + /* r4 = right high */ \ + "ldr r4, [r2], #4 \n\t" \ + \ + /* Do left side */ \ + "ldr r14, [r1], #4 \n\t" \ + "mov r12, #0 \n\t" \ + "umaal r12, r5, r4, r14 \n\t" \ + "str r12, [r0], #4 \n\t" \ + "ldr r14, [r1], #4 \n\t" \ + "umaal r5, r6, r4, r14 \n\t" \ + "str r5, [r0], #4 \n\t" \ + "ldr r14, [r1], #4 \n\t" \ + "umaal r6, r7, r4, r14 \n\t" \ + "str r6, [r0], #4 \n\t" \ + "ldr r14, [r1], #4 \n\t" \ + "umaal r7, r8, r4, r14 \n\t" \ + "str r7, [r0], #4 \n\t" \ + "ldr r14, [r1], #4 \n\t" \ + "umaal r8, r9, r4, r14 \n\t" \ + "str r8, [r0], #4 \n\t" \ + "ldr r14, [r1], #4 \n\t" \ + "umaal r9, r10, r4, r14 \n\t" \ + "str r9, [r0], #4 \n\t" \ + \ + "ldr r14, [r1], #4 \n\t" \ + "umaal r10, r11, r4, r14 \n\t" \ + "stmia r0!, {r10, r11} \n\t" + +#define FAST_MULT_ASM_7 \ + "ldmia r2!, {r4, r5, r6, r7} \n\t" \ + \ + "ldr r14, [r1], #4 \n\t" \ + "umull r8, r9, r4, r14 \n\t" \ + "str r8, [r0], #4 \n\t" \ + "mov r10, #0 \n\t" \ + "umaal r9, r10, r5, r14 \n\t" \ + "mov r11, #0 \n\t" \ + "umaal r10, r11, r6, r14 \n\t" \ + "mov r12, #0 \n\t" \ + "umaal r11, r12, r7, r14 \n\t" \ + \ + "ldr r14, [r1], #4 \n\t" \ + "mov r8, #0 \n\t" \ + "umaal r8, r9, r4, r14 \n\t" \ + "str r8, [r0], #4 \n\t" \ + "umaal r9, r10, r5, r14 \n\t" \ + "umaal r10, r11, r6, r14 \n\t" \ + "umaal r11, r12, r7, r14 \n\t" \ + \ + "ldr r14, [r1], #4 \n\t" \ + "mov r8, #0 \n\t" \ + "umaal r8, r9, r4, r14 \n\t" \ + "str r8, [r0], #4 \n\t" \ + "umaal r9, r10, r5, r14 \n\t" \ + "umaal r10, r11, r6, r14 \n\t" \ + "umaal r11, r12, r7, r14 \n\t" \ + \ + "ldr r14, [r1], #4 \n\t" \ + "mov r8, #0 \n\t" \ + "umaal r8, r9, r4, r14 \n\t" \ + "str r8, [r0], #4 \n\t" \ + "umaal r9, r10, r5, r14 \n\t" \ + "umaal r10, r11, r6, r14 \n\t" \ + "umaal r11, r12, r7, r14 \n\t" \ + \ + "ldr r14, [r1], #4 \n\t" \ + "mov r8, #0 \n\t" \ + "umaal r8, r9, r4, r14 \n\t" \ + "str r8, [r0], #4 \n\t" \ + "umaal r9, r10, r5, r14 \n\t" \ + "umaal r10, r11, r6, r14 \n\t" \ + "umaal r11, r12, r7, r14 \n\t" \ + \ + "ldr r14, [r1], #4 \n\t" \ + "mov r8, #0 \n\t" \ + "umaal r8, r9, r4, r14 \n\t" \ + "str r8, [r0], #4 \n\t" \ + "umaal r9, r10, r5, r14 \n\t" \ + "umaal r10, r11, r6, r14 \n\t" \ + "umaal r11, r12, r7, r14 \n\t" \ + \ + "ldr r14, [r1], #4 \n\t" \ + "mov r8, #0 \n\t" \ + "umaal r8, r9, r4, r14 \n\t" \ + "str r8, [r0], #4 \n\t" \ + "umaal r9, r10, r5, r14 \n\t" \ + "umaal r10, r11, r6, r14 \n\t" \ + "umaal r11, r12, r7, r14 \n\t" \ + \ + "str r9, [r0], #4 \n\t" \ + "str r10, [r0], #4 \n\t" \ + "str r11, [r0], #4 \n\t" \ + "str r12, [r0], #4 \n\t" \ + \ + "sub r0, #28 \n\t" \ + "sub r1, #28 \n\t" \ + "ldmia r2!, {r4, r5, r6} \n\t" \ + \ + "ldr r14, [r1], #4 \n\t" \ + "ldr r8, [r0] \n\t" \ + "mov r9, #0 \n\t" \ + "umaal r8, r9, r4, r14 \n\t" \ + "str r8, [r0], #4 \n\t" \ + "mov r10, #0 \n\t" \ + "umaal r9, r10, r5, r14 \n\t" \ + "mov r11, #0 \n\t" \ + "umaal r10, r11, r6, r14 \n\t" \ + \ + "ldr r14, [r1], #4 \n\t" \ + "ldr r8, [r0] \n\t" \ + "umaal r8, r9, r4, r14 \n\t" \ + "str r8, [r0], #4 \n\t" \ + "umaal r9, r10, r5, r14 \n\t" \ + "umaal r10, r11, r6, r14 \n\t" \ + \ + "ldr r14, [r1], #4 \n\t" \ + "ldr r8, [r0] \n\t" \ + "umaal r8, r9, r4, r14 \n\t" \ + "str r8, [r0], #4 \n\t" \ + "umaal r9, r10, r5, r14 \n\t" \ + "umaal r10, r11, r6, r14 \n\t" \ + \ + "ldr r14, [r1], #4 \n\t" \ + "ldr r8, [r0] \n\t" \ + "umaal r8, r9, r4, r14 \n\t" \ + "str r8, [r0], #4 \n\t" \ + "umaal r9, r10, r5, r14 \n\t" \ + "umaal r10, r11, r6, r14 \n\t" \ + \ + "ldr r14, [r1], #4 \n\t" \ + "ldr r8, [r0] \n\t" \ + "umaal r8, r9, r4, r14 \n\t" \ + "str r8, [r0], #4 \n\t" \ + "umaal r9, r10, r5, r14 \n\t" \ + "umaal r10, r11, r6, r14 \n\t" \ + \ + "ldr r14, [r1], #4 \n\t" \ + "ldr r8, [r0] \n\t" \ + "umaal r8, r9, r4, r14 \n\t" \ + "str r8, [r0], #4 \n\t" \ + "umaal r9, r10, r5, r14 \n\t" \ + "umaal r10, r11, r6, r14 \n\t" \ + \ + "ldr r14, [r1], #4 \n\t" \ + "ldr r8, [r0] \n\t" \ + "umaal r8, r9, r4, r14 \n\t" \ + "str r8, [r0], #4 \n\t" \ + "umaal r9, r10, r5, r14 \n\t" \ + "umaal r10, r11, r6, r14 \n\t" \ + \ + "str r9, [r0], #4 \n\t" \ + "str r10, [r0], #4 \n\t" \ + "str r11, [r0], #4 \n\t" + +#define FAST_MULT_ASM_7_TO_8 \ + "cmp r3, #7 \n\t" \ + "beq 1f \n\t" \ + "push {r3} \n\t" \ + \ + /* r4 = left high */ \ + "ldr r4, [r1] \n\t" \ + \ + "sub r0, #28 \n\t" \ + "sub r1, #28 \n\t" \ + "sub r2, #28 \n\t" \ + \ + /* Do right side */ \ + "ldr r14, [r2], #4 \n\t" \ + "mov r5, #0 \n\t" \ + "ldr r6, [r0], #4 \n\t" \ + "umaal r5, r6, r4, r14 \n\t" \ + "ldr r14, [r2], #4 \n\t" \ + "ldr r7, [r0], #4 \n\t" \ + "umaal r6, r7, r4, r14 \n\t" \ + "ldr r14, [r2], #4 \n\t" \ + "ldr r8, [r0], #4 \n\t" \ + "umaal r7, r8, r4, r14 \n\t" \ + "ldr r14, [r2], #4 \n\t" \ + "ldr r9, [r0], #4 \n\t" \ + "umaal r8, r9, r4, r14 \n\t" \ + "ldr r14, [r2], #4 \n\t" \ + "ldr r10, [r0], #4 \n\t" \ + "umaal r9, r10, r4, r14 \n\t" \ + "ldr r14, [r2], #4 \n\t" \ + "ldr r11, [r0], #4 \n\t" \ + "umaal r10, r11, r4, r14 \n\t" \ + "ldr r14, [r2], #4 \n\t" \ + "ldr r12, [r0], #4 \n\t" \ + "umaal r11, r12, r4, r14 \n\t" \ + "sub r0, #28 \n\t" \ + \ + /* r4 = right high */ \ + "ldr r4, [r2], #4 \n\t" \ + \ + /* Do left side */ \ + "ldr r14, [r1], #4 \n\t" \ + "mov r3, #0 \n\t" \ + "umaal r3, r5, r4, r14 \n\t" \ + "str r3, [r0], #4 \n\t" \ + "ldr r14, [r1], #4 \n\t" \ + "umaal r5, r6, r4, r14 \n\t" \ + "str r5, [r0], #4 \n\t" \ + "ldr r14, [r1], #4 \n\t" \ + "umaal r6, r7, r4, r14 \n\t" \ + "str r6, [r0], #4 \n\t" \ + "ldr r14, [r1], #4 \n\t" \ + "umaal r7, r8, r4, r14 \n\t" \ + "str r7, [r0], #4 \n\t" \ + "ldr r14, [r1], #4 \n\t" \ + "umaal r8, r9, r4, r14 \n\t" \ + "str r8, [r0], #4 \n\t" \ + "ldr r14, [r1], #4 \n\t" \ + "umaal r9, r10, r4, r14 \n\t" \ + "str r9, [r0], #4 \n\t" \ + "ldr r14, [r1], #4 \n\t" \ + "umaal r10, r11, r4, r14 \n\t" \ + "str r10, [r0], #4 \n\t" \ + \ + "ldr r14, [r1], #4 \n\t" \ + "umaal r11, r12, r4, r14 \n\t" \ + "stmia r0!, {r11, r12} \n\t" \ + "pop {r3} \n\t" + +#define FAST_MULT_ASM_8 \ + "ldmia r2!, {r4, r5, r6, r7} \n\t" \ + \ + "ldr r14, [r1], #4 \n\t" \ + "umull r8, r9, r4, r14 \n\t" \ + "str r8, [r0], #4 \n\t" \ + "mov r10, #0 \n\t" \ + "umaal r9, r10, r5, r14 \n\t" \ + "mov r11, #0 \n\t" \ + "umaal r10, r11, r6, r14 \n\t" \ + "mov r12, #0 \n\t" \ + "umaal r11, r12, r7, r14 \n\t" \ + \ + "ldr r14, [r1], #4 \n\t" \ + "mov r8, #0 \n\t" \ + "umaal r8, r9, r4, r14 \n\t" \ + "str r8, [r0], #4 \n\t" \ + "umaal r9, r10, r5, r14 \n\t" \ + "umaal r10, r11, r6, r14 \n\t" \ + "umaal r11, r12, r7, r14 \n\t" \ + \ + "ldr r14, [r1], #4 \n\t" \ + "mov r8, #0 \n\t" \ + "umaal r8, r9, r4, r14 \n\t" \ + "str r8, [r0], #4 \n\t" \ + "umaal r9, r10, r5, r14 \n\t" \ + "umaal r10, r11, r6, r14 \n\t" \ + "umaal r11, r12, r7, r14 \n\t" \ + \ + "ldr r14, [r1], #4 \n\t" \ + "mov r8, #0 \n\t" \ + "umaal r8, r9, r4, r14 \n\t" \ + "str r8, [r0], #4 \n\t" \ + "umaal r9, r10, r5, r14 \n\t" \ + "umaal r10, r11, r6, r14 \n\t" \ + "umaal r11, r12, r7, r14 \n\t" \ + \ + "ldr r14, [r1], #4 \n\t" \ + "mov r8, #0 \n\t" \ + "umaal r8, r9, r4, r14 \n\t" \ + "str r8, [r0], #4 \n\t" \ + "umaal r9, r10, r5, r14 \n\t" \ + "umaal r10, r11, r6, r14 \n\t" \ + "umaal r11, r12, r7, r14 \n\t" \ + \ + "ldr r14, [r1], #4 \n\t" \ + "mov r8, #0 \n\t" \ + "umaal r8, r9, r4, r14 \n\t" \ + "str r8, [r0], #4 \n\t" \ + "umaal r9, r10, r5, r14 \n\t" \ + "umaal r10, r11, r6, r14 \n\t" \ + "umaal r11, r12, r7, r14 \n\t" \ + \ + "ldr r14, [r1], #4 \n\t" \ + "mov r8, #0 \n\t" \ + "umaal r8, r9, r4, r14 \n\t" \ + "str r8, [r0], #4 \n\t" \ + "umaal r9, r10, r5, r14 \n\t" \ + "umaal r10, r11, r6, r14 \n\t" \ + "umaal r11, r12, r7, r14 \n\t" \ + \ + "ldr r14, [r1], #4 \n\t" \ + "mov r8, #0 \n\t" \ + "umaal r8, r9, r4, r14 \n\t" \ + "str r8, [r0], #4 \n\t" \ + "umaal r9, r10, r5, r14 \n\t" \ + "umaal r10, r11, r6, r14 \n\t" \ + "umaal r11, r12, r7, r14 \n\t" \ + \ + "str r9, [r0], #4 \n\t" \ + "str r10, [r0], #4 \n\t" \ + "str r11, [r0], #4 \n\t" \ + "str r12, [r0], #4 \n\t" \ + \ + "sub r0, #32 \n\t" \ + "sub r1, #32 \n\t" \ + "ldmia r2!, {r4, r5, r6, r7} \n\t" \ + \ + "ldr r14, [r1], #4 \n\t" \ + "ldr r8, [r0] \n\t" \ + "mov r9, #0 \n\t" \ + "umaal r8, r9, r4, r14 \n\t" \ + "str r8, [r0], #4 \n\t" \ + "mov r10, #0 \n\t" \ + "umaal r9, r10, r5, r14 \n\t" \ + "mov r11, #0 \n\t" \ + "umaal r10, r11, r6, r14 \n\t" \ + "mov r12, #0 \n\t" \ + "umaal r11, r12, r7, r14 \n\t" \ + \ + "ldr r14, [r1], #4 \n\t" \ + "ldr r8, [r0] \n\t" \ + "umaal r8, r9, r4, r14 \n\t" \ + "str r8, [r0], #4 \n\t" \ + "umaal r9, r10, r5, r14 \n\t" \ + "umaal r10, r11, r6, r14 \n\t" \ + "umaal r11, r12, r7, r14 \n\t" \ + \ + "ldr r14, [r1], #4 \n\t" \ + "ldr r8, [r0] \n\t" \ + "umaal r8, r9, r4, r14 \n\t" \ + "str r8, [r0], #4 \n\t" \ + "umaal r9, r10, r5, r14 \n\t" \ + "umaal r10, r11, r6, r14 \n\t" \ + "umaal r11, r12, r7, r14 \n\t" \ + \ + "ldr r14, [r1], #4 \n\t" \ + "ldr r8, [r0] \n\t" \ + "umaal r8, r9, r4, r14 \n\t" \ + "str r8, [r0], #4 \n\t" \ + "umaal r9, r10, r5, r14 \n\t" \ + "umaal r10, r11, r6, r14 \n\t" \ + "umaal r11, r12, r7, r14 \n\t" \ + \ + "ldr r14, [r1], #4 \n\t" \ + "ldr r8, [r0] \n\t" \ + "umaal r8, r9, r4, r14 \n\t" \ + "str r8, [r0], #4 \n\t" \ + "umaal r9, r10, r5, r14 \n\t" \ + "umaal r10, r11, r6, r14 \n\t" \ + "umaal r11, r12, r7, r14 \n\t" \ + \ + "ldr r14, [r1], #4 \n\t" \ + "ldr r8, [r0] \n\t" \ + "umaal r8, r9, r4, r14 \n\t" \ + "str r8, [r0], #4 \n\t" \ + "umaal r9, r10, r5, r14 \n\t" \ + "umaal r10, r11, r6, r14 \n\t" \ + "umaal r11, r12, r7, r14 \n\t" \ + \ + "ldr r14, [r1], #4 \n\t" \ + "ldr r8, [r0] \n\t" \ + "umaal r8, r9, r4, r14 \n\t" \ + "str r8, [r0], #4 \n\t" \ + "umaal r9, r10, r5, r14 \n\t" \ + "umaal r10, r11, r6, r14 \n\t" \ + "umaal r11, r12, r7, r14 \n\t" \ + \ + "ldr r14, [r1], #4 \n\t" \ + "ldr r8, [r0] \n\t" \ + "umaal r8, r9, r4, r14 \n\t" \ + "str r8, [r0], #4 \n\t" \ + "umaal r9, r10, r5, r14 \n\t" \ + "umaal r10, r11, r6, r14 \n\t" \ + "umaal r11, r12, r7, r14 \n\t" \ + \ + "str r9, [r0], #4 \n\t" \ + "str r10, [r0], #4 \n\t" \ + "str r11, [r0], #4 \n\t" \ + "str r12, [r0], #4 \n\t" + +#define FAST_SQUARE_ASM_5 \ + "ldmia r1!, {r9,r10,r11,r12,r14} \n\t" \ + "push {r1, r2} \n\t" \ + \ + "umull r1, r2, r10, r9 \n\t" \ + "mov r3, #0 \n\t" \ + "umaal r2, r3, r11, r9 \n\t" \ + "mov r4, #0 \n\t" \ + "umaal r3, r4, r12, r9 \n\t" \ + "mov r5, #0 \n\t" \ + "umaal r4, r5, r14, r9 \n\t" \ + \ + "mov r6, #0 \n\t" \ + "umaal r6, r3, r11, r10 \n\t" \ + "umaal r3, r4, r12, r10 \n\t" \ + "adds r1, r1, r1 \n\t" \ + "adcs r2, r2, r2 \n\t" \ + "adcs r6, r6, r6 \n\t" \ + "adcs r3, r3, r3 \n\t" \ + \ + "umull r7, r8, r9, r9 \n\t" \ + /* Store carry in r9 */ \ + "mov r9, #0 \n\t" \ + "adc r9, r9, #0 \n\t" \ + "adds r8, r8, r1 \n\t" \ + "stmia r0!, {r7,r8} \n\t" \ + \ + "umull r7, r8, r10, r10 \n\t" \ + "adcs r7, r7, r2 \n\t" \ + "adcs r8, r8, r6 \n\t" \ + "stmia r0!, {r7,r8} \n\t" \ + \ + "umaal r4, r5, r14, r10 \n\t" \ + /* Store carry in r10 */ \ + "mov r10, #0 \n\t" \ + "adc r10, r10, #0 \n\t" \ + \ + "mov r1, #0 \n\t" \ + "umaal r1, r4, r12, r11 \n\t" \ + "umaal r4, r5, r14, r11 \n\t" \ + \ + "mov r2, #0 \n\t" \ + "umaal r2, r5, r14, r12 \n\t" \ + /* Load carry from r9 */ \ + "lsrs r9, #1 \n\t" \ + "adcs r1, r1, r1 \n\t" \ + "adcs r4, r4, r4 \n\t" \ + "adcs r2, r2, r2 \n\t" \ + "adcs r5, r5, r5 \n\t" \ + /* r9 is 0 now */ \ + "adc r9, r9, #0 \n\t" \ + \ + /* Use carry from r10 */ \ + "umaal r3, r10, r11, r11 \n\t" \ + "adds r10, r10, r1 \n\t" \ + "stmia r0!, {r3,r10} \n\t" \ + \ + "umull r6, r10, r12, r12 \n\t" \ + "adcs r6, r6, r4 \n\t" \ + "adcs r10, r10, r2 \n\t" \ + "stmia r0!, {r6,r10} \n\t" \ + \ + "umull r6, r10, r14, r14 \n\t" \ + "adcs r6, r6, r5 \n\t" \ + "adcs r10, r10, r9 \n\t" \ + "stmia r0!, {r6,r10} \n\t" \ + "pop {r1, r2} \n\t" + +#define FAST_SQUARE_ASM_5_TO_6 \ + "cmp r2, #5 \n\t" \ + "beq 1f \n\t" \ + \ + "sub r0, #20 \n\t" \ + "sub r1, #20 \n\t" \ + \ + /* Do off-center multiplication */ \ + "ldmia r1!, {r5,r6,r7,r8,r9,r14} \n\t" \ + "umull r3, r4, r5, r14 \n\t" \ + "mov r5, #0 \n\t" \ + "umaal r4, r5, r6, r14 \n\t" \ + "mov r6, #0 \n\t" \ + "umaal r5, r6, r7, r14 \n\t" \ + "mov r7, #0 \n\t" \ + "umaal r6, r7, r8, r14 \n\t" \ + "mov r8, #0 \n\t" \ + "umaal r7, r8, r9, r14 \n\t" \ + \ + /* Multiply by 2 */ \ + "mov r9, #0 \n\t" \ + "adds r3, r3, r3 \n\t" \ + "adcs r4, r4, r4 \n\t" \ + "adcs r5, r5, r5 \n\t" \ + "adcs r6, r6, r6 \n\t" \ + "adcs r7, r7, r7 \n\t" \ + "adcs r8, r8, r8 \n\t" \ + "adcs r9, r9, #0 \n\t" \ + \ + /* Add into previous */ \ + "ldr r12, [r0], #4 \n\t" \ + "adds r3, r3, r12 \n\t" \ + "ldr r12, [r0], #4 \n\t" \ + "adcs r4, r4, r12 \n\t" \ + "ldr r12, [r0], #4 \n\t" \ + "adcs r5, r5, r12 \n\t" \ + "ldr r12, [r0], #4 \n\t" \ + "adcs r6, r6, r12 \n\t" \ + "ldr r12, [r0], #4 \n\t" \ + "adcs r7, r7, r12 \n\t" \ + "adcs r8, r8, #0 \n\t" \ + "adcs r9, r9, #0 \n\t" \ + "sub r0, #20 \n\t" \ + \ + /* Perform center multiplication */ \ + "umlal r8, r9, r14, r14 \n\t" \ + "stmia r0!, {r3,r4,r5,r6,r7,r8,r9} \n\t" + +#define FAST_SQUARE_ASM_6 \ + "ldmia r1!, {r8,r9,r10,r11,r12,r14} \n\t" \ + "push {r1, r2} \n\t" \ + \ + "umull r1, r2, r9, r8 \n\t" \ + "mov r3, #0 \n\t" \ + "umaal r2, r3, r10, r8 \n\t" \ + "mov r4, #0 \n\t" \ + "umaal r3, r4, r11, r8 \n\t" \ + "mov r5, #0 \n\t" \ + "umaal r4, r5, r12, r8 \n\t" \ + "mov r6, #0 \n\t" \ + "umaal r5, r6, r14, r8 \n\t" \ + \ + "mov r7, #0 \n\t" \ + "umaal r7, r3, r10, r9 \n\t" \ + "umaal r3, r4, r11, r9 \n\t" \ + "umaal r4, r5, r12, r9 \n\t" \ + "push {r4, r5} \n\t" \ + "adds r1, r1, r1 \n\t" \ + "adcs r2, r2, r2 \n\t" \ + "adcs r7, r7, r7 \n\t" \ + "adcs r3, r3, r3 \n\t" \ + \ + "umull r4, r5, r8, r8 \n\t" \ + /* Store carry in r8 */ \ + "mov r8, #0 \n\t" \ + "adc r8, r8, #0 \n\t" \ + "adds r5, r5, r1 \n\t" \ + "stmia r0!, {r4,r5} \n\t" \ + \ + "umull r4, r5, r9, r9 \n\t" \ + "adcs r4, r4, r2 \n\t" \ + "adcs r5, r5, r7 \n\t" \ + "stmia r0!, {r4,r5} \n\t" \ + \ + "pop {r4, r5} \n\t" \ + "umaal r5, r6, r14, r9 \n\t" \ + /* Store carry in r9 */ \ + "mov r9, #0 \n\t" \ + "adc r9, r9, #0 \n\t" \ + \ + "mov r1, #0 \n\t" \ + "umaal r1, r4, r11, r10 \n\t" \ + "umaal r4, r5, r12, r10 \n\t" \ + "umaal r5, r6, r14, r10 \n\t" \ + \ + "mov r2, #0 \n\t" \ + "umaal r2, r5, r12, r11 \n\t" \ + "umaal r5, r6, r14, r11 \n\t" \ + \ + "mov r7, #0 \n\t" \ + "umaal r7, r6, r14, r12 \n\t" \ + \ + /* Load carry from r8 */ \ + "lsrs r8, #1 \n\t" \ + "adcs r1, r1, r1 \n\t" \ + "adcs r4, r4, r4 \n\t" \ + "adcs r2, r2, r2 \n\t" \ + "adcs r5, r5, r5 \n\t" \ + "adcs r7, r7, r7 \n\t" \ + "adcs r6, r6, r6 \n\t" \ + "adc r8, r8, #0 \n\t" \ + \ + /* Use carry from r9 */ \ + "umaal r3, r9, r10, r10 \n\t" \ + "adds r9, r9, r1 \n\t" \ + "stmia r0!, {r3,r9} \n\t" \ + \ + "umull r9, r10, r11, r11 \n\t" \ + "adcs r9, r9, r4 \n\t" \ + "adcs r10, r10, r2 \n\t" \ + "stmia r0!, {r9,r10} \n\t" \ + \ + "umull r9, r10, r12, r12 \n\t" \ + "adcs r9, r9, r5 \n\t" \ + "adcs r10, r10, r7 \n\t" \ + "stmia r0!, {r9,r10} \n\t" \ + \ + "umull r9, r10, r14, r14 \n\t" \ + "adcs r9, r9, r6 \n\t" \ + "adcs r10, r10, r8 \n\t" \ + "stmia r0!, {r9,r10} \n\t" \ + "pop {r1, r2} \n\t" + +#define FAST_SQUARE_ASM_6_TO_7 \ + "cmp r2, #6 \n\t" \ + "beq 1f \n\t" \ + \ + "sub r0, #24 \n\t" \ + "sub r1, #24 \n\t" \ + \ + /* Do off-center multiplication */ \ + "ldmia r1!, {r5,r6,r7,r8,r9,r10,r14} \n\t" \ + "umull r3, r4, r5, r14 \n\t" \ + "mov r5, #0 \n\t" \ + "umaal r4, r5, r6, r14 \n\t" \ + "mov r6, #0 \n\t" \ + "umaal r5, r6, r7, r14 \n\t" \ + "mov r7, #0 \n\t" \ + "umaal r6, r7, r8, r14 \n\t" \ + "mov r8, #0 \n\t" \ + "umaal r7, r8, r9, r14 \n\t" \ + "mov r9, #0 \n\t" \ + "umaal r8, r9, r10, r14 \n\t" \ + \ + /* Multiply by 2 */ \ + "mov r10, #0 \n\t" \ + "adds r3, r3, r3 \n\t" \ + "adcs r4, r4, r4 \n\t" \ + "adcs r5, r5, r5 \n\t" \ + "adcs r6, r6, r6 \n\t" \ + "adcs r7, r7, r7 \n\t" \ + "adcs r8, r8, r8 \n\t" \ + "adcs r9, r9, r9 \n\t" \ + "adcs r10, r10, #0 \n\t" \ + \ + /* Add into previous */ \ + "ldr r12, [r0], #4 \n\t" \ + "adds r3, r3, r12 \n\t" \ + "ldr r12, [r0], #4 \n\t" \ + "adcs r4, r4, r12 \n\t" \ + "ldr r12, [r0], #4 \n\t" \ + "adcs r5, r5, r12 \n\t" \ + "ldr r12, [r0], #4 \n\t" \ + "adcs r6, r6, r12 \n\t" \ + "ldr r12, [r0], #4 \n\t" \ + "adcs r7, r7, r12 \n\t" \ + "ldr r12, [r0], #4 \n\t" \ + "adcs r8, r8, r12 \n\t" \ + "adcs r9, r9, #0 \n\t" \ + "adcs r10, r10, #0 \n\t" \ + "sub r0, #24 \n\t" \ + \ + /* Perform center multiplication */ \ + "umlal r9, r10, r14, r14 \n\t" \ + "stmia r0!, {r3,r4,r5,r6,r7,r8,r9,r10} \n\t" + +#define FAST_SQUARE_ASM_7 \ + "ldmia r1!, {r9,r10,r11,r12} \n\t" \ + "push {r2} \n\t" \ + \ + "umull r14, r2, r10, r9 \n\t" \ + "mov r3, #0 \n\t" \ + "umaal r2, r3, r11, r9 \n\t" \ + "mov r4, #0 \n\t" \ + "umaal r3, r4, r12, r9 \n\t" \ + \ + "mov r5, #0 \n\t" \ + "umaal r5, r3, r11, r10 \n\t" \ + "adds r14, r14, r14 \n\t" \ + "adcs r2, r2, r2 \n\t" \ + "adcs r5, r5, r5 \n\t" \ + /* Store carry in r7 */ \ + "mov r7, #0 \n\t" \ + "adc r7, r7, #0 \n\t" \ + \ + "umull r6, r8, r9, r9 \n\t" \ + "adds r8, r8, r14 \n\t" \ + "stmia r0!, {r6,r8} \n\t" \ + \ + "umull r6, r8, r10, r10 \n\t" \ + "adcs r6, r6, r2 \n\t" \ + "adcs r8, r8, r5 \n\t" \ + "stmia r0!, {r6,r8} \n\t" \ + /* Store carry in r8 */ \ + "mov r8, #0 \n\t" \ + "adc r8, r8, #0 \n\t" \ + \ + "ldmia r1!, {r2, r6, r14} \n\t" \ + "push {r1} \n\t" \ + "umaal r3, r4, r2, r9 \n\t" \ + "mov r5, #0 \n\t" \ + "umaal r4, r5, r6, r9 \n\t" \ + "mov r1, #0 \n\t" \ + "umaal r5, r1, r14, r9 \n\t" \ + \ + "mov r9, #0 \n\t" \ + "umaal r3, r9, r12, r10 \n\t" \ + "umaal r9, r4, r2, r10 \n\t" \ + "umaal r4, r5, r6, r10 \n\t" \ + "umaal r5, r1, r14, r10 \n\t" \ + \ + "mov r10, #0 \n\t" \ + "umaal r10, r9, r12, r11 \n\t" \ + "umaal r9, r4, r2, r11 \n\t" \ + "umaal r4, r5, r6, r11 \n\t" \ + "umaal r5, r1, r14, r11 \n\t" \ + \ + /* Load carry from r7 */ \ + "lsrs r7, #1 \n\t" \ + "adcs r3, r3, r3 \n\t" \ + "adcs r10, r10, r10 \n\t" \ + "adcs r9, r9, r9 \n\t" \ + /* Store carry back in r7 */ \ + "adc r7, r7, #0 \n\t" \ + \ + /* Use carry from r8 */ \ + "umaal r3, r8, r11, r11 \n\t" \ + "adds r8, r8, r10 \n\t" \ + "stmia r0!, {r3,r8} \n\t" \ + /* Store carry back in r8 */ \ + "mov r8, #0 \n\t" \ + "adc r8, r8, #0 \n\t" \ + \ + "mov r3, #0 \n\t" \ + "umaal r3, r4, r2, r12 \n\t" \ + "umaal r4, r5, r6, r12 \n\t" \ + "umaal r5, r1, r14, r12 \n\t" \ + \ + "mov r10, #0 \n\t" \ + "umaal r10, r5, r6, r2 \n\t" \ + "umaal r5, r1, r14, r2 \n\t" \ + \ + "mov r11, #0 \n\t" \ + "umaal r11, r1, r14, r6 \n\t" \ + \ + /* Load carry from r7 */ \ + "lsrs r7, #1 \n\t" \ + "adcs r3, r3, r3 \n\t" \ + "adcs r4, r4, r4 \n\t" \ + "adcs r10, r10, r10 \n\t" \ + "adcs r5, r5, r5 \n\t" \ + "adcs r11, r11, r11 \n\t" \ + "adcs r1, r1, r1 \n\t" \ + "adc r7, r7, #0 \n\t" \ + \ + /* Use carry from r8 */ \ + "umaal r8, r9, r12, r12 \n\t" \ + "adds r9, r9, r3 \n\t" \ + "stmia r0!, {r8,r9} \n\t" \ + \ + "umull r8, r9, r2, r2 \n\t" \ + "adcs r8, r8, r4 \n\t" \ + "adcs r9, r9, r10 \n\t" \ + "stmia r0!, {r8,r9} \n\t" \ + \ + "umull r8, r9, r6, r6 \n\t" \ + "adcs r8, r8, r5 \n\t" \ + "adcs r9, r9, r11 \n\t" \ + "stmia r0!, {r8,r9} \n\t" \ + \ + "umull r8, r9, r14, r14 \n\t" \ + "adcs r8, r8, r1 \n\t" \ + "adcs r9, r9, r7 \n\t" \ + "stmia r0!, {r8,r9} \n\t" \ + "pop {r1, r2} \n\t" + +#define FAST_SQUARE_ASM_7_TO_8 \ + "cmp r2, #7 \n\t" \ + "beq 1f \n\t" \ + \ + "sub r0, #28 \n\t" \ + "sub r1, #28 \n\t" \ + \ + /* Do off-center multiplication */ \ + "ldmia r1!, {r5,r6,r7,r8,r9,r10,r11,r14} \n\t" \ + "umull r3, r4, r5, r14 \n\t" \ + "mov r5, #0 \n\t" \ + "umaal r4, r5, r6, r14 \n\t" \ + "mov r6, #0 \n\t" \ + "umaal r5, r6, r7, r14 \n\t" \ + "mov r7, #0 \n\t" \ + "umaal r6, r7, r8, r14 \n\t" \ + "mov r8, #0 \n\t" \ + "umaal r7, r8, r9, r14 \n\t" \ + "mov r9, #0 \n\t" \ + "umaal r8, r9, r10, r14 \n\t" \ + "mov r10, #0 \n\t" \ + "umaal r9, r10, r11, r14 \n\t" \ + \ + /* Multiply by 2 */ \ + "mov r11, #0 \n\t" \ + "adds r3, r3, r3 \n\t" \ + "adcs r4, r4, r4 \n\t" \ + "adcs r5, r5, r5 \n\t" \ + "adcs r6, r6, r6 \n\t" \ + "adcs r7, r7, r7 \n\t" \ + "adcs r8, r8, r8 \n\t" \ + "adcs r9, r9, r9 \n\t" \ + "adcs r10, r10, r10 \n\t" \ + "adcs r11, r11, #0 \n\t" \ + \ + /* Add into previous */ \ + "ldr r12, [r0], #4 \n\t" \ + "adds r3, r3, r12 \n\t" \ + "ldr r12, [r0], #4 \n\t" \ + "adcs r4, r4, r12 \n\t" \ + "ldr r12, [r0], #4 \n\t" \ + "adcs r5, r5, r12 \n\t" \ + "ldr r12, [r0], #4 \n\t" \ + "adcs r6, r6, r12 \n\t" \ + "ldr r12, [r0], #4 \n\t" \ + "adcs r7, r7, r12 \n\t" \ + "ldr r12, [r0], #4 \n\t" \ + "adcs r8, r8, r12 \n\t" \ + "ldr r12, [r0], #4 \n\t" \ + "adcs r9, r9, r12 \n\t" \ + "adcs r10, r10, #0 \n\t" \ + "adcs r11, r11, #0 \n\t" \ + "sub r0, #28 \n\t" \ + \ + /* Perform center multiplication */ \ + "umlal r10, r11, r14, r14 \n\t" \ + "stmia r0!, {r3,r4,r5,r6,r7,r8,r9,r10,r11} \n\t" + +#define FAST_SQUARE_ASM_8 \ + "ldmia r1!, {r10,r11,r12,r14} \n\t" \ + "push {r2} \n\t" \ + \ + "umull r2, r3, r11, r10 \n\t" \ + "mov r4, #0 \n\t" \ + "umaal r3, r4, r12, r10 \n\t" \ + "mov r5, #0 \n\t" \ + "umaal r4, r5, r14, r10 \n\t" \ + \ + "mov r6, #0 \n\t" \ + "umaal r6, r4, r12, r11 \n\t" \ + "adds r2, r2, r2 \n\t" \ + "adcs r3, r3, r3 \n\t" \ + "adcs r6, r6, r6 \n\t" \ + /* Store carry in r7 */ \ + "mov r7, #0 \n\t" \ + "adc r7, r7, #0 \n\t" \ + \ + "umull r8, r9, r10, r10 \n\t" \ + "adds r9, r9, r2 \n\t" \ + "stmia r0!, {r8,r9} \n\t" \ + \ + "umull r8, r9, r11, r11 \n\t" \ + "adcs r8, r8, r3 \n\t" \ + "adcs r9, r9, r6 \n\t" \ + "stmia r0!, {r8,r9} \n\t" \ + /* Store carry in r8 */ \ + "mov r8, #0 \n\t" \ + "adc r8, r8, #0 \n\t" \ + \ + "ldmia r1!, {r2, r3} \n\t" \ + "push {r1} \n\t" \ + "umaal r4, r5, r2, r10 \n\t" \ + "mov r6, #0 \n\t" \ + "umaal r5, r6, r3, r10 \n\t" \ + \ + "mov r9, #0 \n\t" \ + "umaal r9, r4, r14, r11 \n\t" \ + "umaal r4, r5, r2, r11 \n\t" \ + \ + "mov r1, #0 \n\t" \ + "umaal r1, r4, r14, r12 \n\t" \ + \ + /* Load carry from r7 */ \ + "lsrs r7, #1 \n\t" \ + "adcs r9, r9, r9 \n\t" \ + "adcs r1, r1, r1 \n\t" \ + /* Store carry back in r7 */ \ + "adc r7, r7, #0 \n\t" \ + \ + /* Use carry from r8 */ \ + "umaal r8, r9, r12, r12 \n\t" \ + "adds r9, r9, r1 \n\t" \ + "stmia r0!, {r8,r9} \n\t" \ + /* Store carry back in r8 */ \ + "mov r8, #0 \n\t" \ + "adc r8, r8, #0 \n\t" \ + \ + "pop {r1} \n\t" \ + /* TODO could fix up r1 value on stack here */ \ + /* and leave the value on the stack (rather */ \ + /* than popping) if supporting curves > 256 bits */ \ + "ldr r9, [r1], #4 \n\t" \ + "ldr r1, [r1] \n\t" \ + \ + "push {r7} \n\t" \ + "umaal r5, r6, r9, r10 \n\t" \ + "mov r7, #0 \n\t" \ + "umaal r6, r7, r1, r10 \n\t" \ + /* Carry now stored in r10 */ \ + "pop {r10} \n\t" \ + \ + "umaal r4, r5, r3, r11 \n\t" \ + "umaal r5, r6, r9, r11 \n\t" \ + "umaal r6, r7, r1, r11 \n\t" \ + \ + "mov r11, #0 \n\t" \ + "umaal r11, r4, r2, r12 \n\t" \ + "umaal r4, r5, r3, r12 \n\t" \ + "umaal r5, r6, r9, r12 \n\t" \ + "umaal r6, r7, r1, r12 \n\t" \ + \ + "mov r12, #0 \n\t" \ + "umaal r12, r4, r2, r14 \n\t" \ + "umaal r4, r5, r3, r14 \n\t" \ + "umaal r5, r6, r9, r14 \n\t" \ + "umaal r6, r7, r1, r14 \n\t" \ + \ + /* Load carry from r10 */ \ + "lsrs r10, #1 \n\t" \ + "adcs r11, r11, r11 \n\t" \ + "adcs r12, r12, r12 \n\t" \ + "adc r10, r10, #0 \n\t" \ + \ + /* Use carry from r8 */ \ + "umaal r8, r11, r14, r14 \n\t" \ + "adds r11, r11, r12 \n\t" \ + "stmia r0!, {r8,r11} \n\t" \ + /* Store carry back in r8 */ \ + "mov r8, #0 \n\t" \ + "adc r8, r8, #0 \n\t" \ + \ + "mov r11, #0 \n\t" \ + "umaal r11, r5, r3, r2 \n\t" \ + "umaal r5, r6, r9, r2 \n\t" \ + "umaal r6, r7, r1, r2 \n\t" \ + \ + "mov r12, #0 \n\t" \ + "umaal r12, r6, r9, r3 \n\t" \ + "umaal r6, r7, r1, r3 \n\t" \ + \ + "mov r14, #0 \n\t" \ + "umaal r14, r7, r1, r9 \n\t" \ + \ + /* Load carry from r10 */ \ + "lsrs r10, #1 \n\t" \ + "adcs r4, r4, r4 \n\t" \ + "adcs r11, r11, r11 \n\t" \ + "adcs r5, r5, r5 \n\t" \ + "adcs r12, r12, r12 \n\t" \ + "adcs r6, r6, r6 \n\t" \ + "adcs r14, r14, r14 \n\t" \ + "adcs r7, r7, r7 \n\t" \ + "adc r10, r10, #0 \n\t" \ + \ + /* Use carry from r8 */ \ + "umaal r4, r8, r2, r2 \n\t" \ + "adds r8, r8, r11 \n\t" \ + "stmia r0!, {r4,r8} \n\t" \ + \ + "umull r4, r8, r3, r3 \n\t" \ + "adcs r4, r4, r5 \n\t" \ + "adcs r8, r8, r12 \n\t" \ + "stmia r0!, {r4,r8} \n\t" \ + \ + "umull r4, r8, r9, r9 \n\t" \ + "adcs r4, r4, r6 \n\t" \ + "adcs r8, r8, r14 \n\t" \ + "stmia r0!, {r4,r8} \n\t" \ + \ + "umull r4, r8, r1, r1 \n\t" \ + "adcs r4, r4, r7 \n\t" \ + "adcs r8, r8, r10 \n\t" \ + "stmia r0!, {r4,r8} \n\t" \ + /* TODO pop {r1, r2} if supporting curves > 256 bits */ \ + "pop {r2} \n\t" + +#endif /* _UECC_ASM_ARM_MULT_SQUARE_H_ */ diff --git a/deps/picoquic/picotls/deps/micro-ecc/asm_avr.inc b/deps/picoquic/picotls/deps/micro-ecc/asm_avr.inc new file mode 100644 index 0000000..c988040 --- /dev/null +++ b/deps/picoquic/picotls/deps/micro-ecc/asm_avr.inc @@ -0,0 +1,1089 @@ +/* Copyright 2015, Kenneth MacKay. Licensed under the BSD 2-clause license. */ + +#ifndef _UECC_ASM_AVR_H_ +#define _UECC_ASM_AVR_H_ + +#if (uECC_SUPPORTS_secp256r1 || uECC_SUPPORTS_secp256k1) + #define uECC_MIN_WORDS 32 +#endif +#if uECC_SUPPORTS_secp224r1 + #undef uECC_MIN_WORDS + #define uECC_MIN_WORDS 28 +#endif +#if uECC_SUPPORTS_secp192r1 + #undef uECC_MIN_WORDS + #define uECC_MIN_WORDS 24 +#endif +#if uECC_SUPPORTS_secp160r1 + #undef uECC_MIN_WORDS + #define uECC_MIN_WORDS 20 +#endif + +#if __AVR_HAVE_EIJMP_EICALL__ + #define IJMP "eijmp \n\t" +#else + #define IJMP "ijmp \n\t" +#endif + +#if (uECC_OPTIMIZATION_LEVEL >= 2) + +uECC_VLI_API void uECC_vli_clear(uECC_word_t *vli, wordcount_t num_words) { + volatile uECC_word_t *v = vli; + __asm__ volatile ( + #if (uECC_MAX_WORDS != uECC_MIN_WORDS) + "ldi r30, pm_lo8(1f) \n\t" + "ldi r31, pm_hi8(1f) \n\t" + "sub r30, %[num] \n\t" + "sbc r31, __zero_reg__ \n\t" + IJMP + #endif + + REPEAT(uECC_MAX_WORDS, "st x+, __zero_reg__ \n\t") + "1: \n\t" + : "+x" (v) + : [num] "r" (num_words) + : + #if (uECC_MAX_WORDS != uECC_MIN_WORDS) + "r30", "r31", "cc" + #endif + ); +} +#define asm_clear 1 + +uECC_VLI_API void uECC_vli_set(uECC_word_t *dest, const uECC_word_t *src, wordcount_t num_words) { + volatile uECC_word_t *d = dest; + __asm__ volatile ( + #if (uECC_MAX_WORDS != uECC_MIN_WORDS) + "ldi r30, pm_lo8(1f) \n\t" + "ldi r31, pm_hi8(1f) \n\t" + "sub r30, %[num] \n\t" + "sbc r31, __zero_reg__ \n\t" + IJMP + #endif + + REPEAT(uECC_MAX_WORDS, + "ld r0, y+ \n\t" + "st x+, r0 \n\t") + "1: \n\t" + : "+x" (d), "+y" (src) + : [num] "r" ((uint8_t)(num_words * 2)) + : "r0", + #if (uECC_MAX_WORDS != uECC_MIN_WORDS) + "r30", "r31", "cc" + #endif + ); +} +#define asm_set 1 + +uECC_VLI_API void uECC_vli_rshift1(uECC_word_t *vli, wordcount_t num_words) { + volatile uECC_word_t *v = vli; + __asm__ volatile ( + #if (uECC_MAX_WORDS != uECC_MIN_WORDS) + "ldi r30, pm_lo8(1f) \n\t" + "ldi r31, pm_hi8(1f) \n\t" + "sub r30, %[jump] \n\t" + "sbc r31, __zero_reg__ \n\t" + #endif + + "add r26, %[num] \n\t" + "adc r27, __zero_reg__ \n\t" + "ld r0, -x \n\t" + "lsr r0 \n\t" + "st x, r0 \n\t" + #if (uECC_MAX_WORDS != uECC_MIN_WORDS) + IJMP + #endif + + REPEAT(DEC(uECC_MAX_WORDS), + "ld r0, -x \n\t" + "ror r0 \n\t" + "st x, r0 \n\t") + "1: \n\t" + : "+x" (v) + #if (uECC_MAX_WORDS != uECC_MIN_WORDS) + : [num] "r" (num_words), [jump] "r" ((uint8_t)(3 * (num_words - 1))) + : "r0", "r30", "r31", "cc" + #else + : [num] "r" (num_words) + : "r0", "cc" + #endif + ); +} +#define asm_rshift1 1 + +#define ADD_RJPM_TABLE(N) \ + "movw r30, %A[result] \n\t" \ + "rjmp add_%=_" #N " \n\t" + +#define ADD_RJPM_DEST(N) \ + "add_%=_" #N ":" \ + "ld %[clb], x+ \n\t" \ + "ld %[rb], y+ \n\t" \ + "adc %[clb], %[rb] \n\t" \ + "st z+, %[clb] \n\t" + +uECC_VLI_API uECC_word_t uECC_vli_add(uECC_word_t *result, + const uECC_word_t *left, + const uECC_word_t *right, + wordcount_t num_words) { + volatile uECC_word_t *r = result; + uint8_t carry; + uint8_t right_byte; + + __asm__ volatile ( + #if (uECC_MAX_WORDS != uECC_MIN_WORDS) + "ldi r30, pm_lo8(add_%=_" STR(uECC_MAX_WORDS) ") \n\t" + "ldi r31, pm_hi8(add_%=_" STR(uECC_MAX_WORDS) ") \n\t" + "sub r30, %[num] \n\t" + "sbc r31, __zero_reg__ \n\t" + #endif + + "clc \n\t" + #if (uECC_MAX_WORDS != uECC_MIN_WORDS) + IJMP + REPEATM(uECC_MAX_WORDS, ADD_RJPM_TABLE) + #endif + + REPEATM(uECC_MAX_WORDS, ADD_RJPM_DEST) + + "mov %[clb], __zero_reg__ \n\t" + "adc %[clb], %[clb] \n\t" /* Store carry bit. */ + + : "+x" (left), "+y" (right), + [clb] "=&r" (carry), [rb] "=&r" (right_byte) + : [result] "r" (r), [num] "r" ((uint8_t)(num_words * 2)) + : "r30", "r31", "cc" + ); + return carry; +} +#define asm_add 1 + +#define SUB_RJPM_TABLE(N) \ + "movw r30, %A[result] \n\t" \ + "rjmp sub_%=_" #N " \n\t" + +#define SUB_RJPM_DEST(N) \ + "sub_%=_" #N ":" \ + "ld %[clb], x+ \n\t" \ + "ld %[rb], y+ \n\t" \ + "sbc %[clb], %[rb] \n\t" \ + "st z+, %[clb] \n\t" + +uECC_VLI_API uECC_word_t uECC_vli_sub(uECC_word_t *result, + const uECC_word_t *left, + const uECC_word_t *right, + wordcount_t num_words) { + volatile uECC_word_t *r = result; + uint8_t carry; + uint8_t right_byte; + + __asm__ volatile ( + #if (uECC_MAX_WORDS != uECC_MIN_WORDS) + "ldi r30, pm_lo8(sub_%=_" STR(uECC_MAX_WORDS) ") \n\t" + "ldi r31, pm_hi8(sub_%=_" STR(uECC_MAX_WORDS) ") \n\t" + "sub r30, %[num] \n\t" + "sbc r31, __zero_reg__ \n\t" + #endif + + "clc \n\t" + #if (uECC_MAX_WORDS != uECC_MIN_WORDS) + IJMP + REPEATM(uECC_MAX_WORDS, SUB_RJPM_TABLE) + #endif + + REPEATM(uECC_MAX_WORDS, SUB_RJPM_DEST) + + "mov %[clb], __zero_reg__ \n\t" + "adc %[clb], %[clb] \n\t" /* Store carry bit. */ + + : "+x" (left), "+y" (right), + [clb] "=&r" (carry), [rb] "=&r" (right_byte) + : [result] "r" (r), [num] "r" ((uint8_t)(num_words * 2)) + : "r30", "r31", "cc" + ); + return carry; +} +#define asm_sub 1 + +#if (uECC_OPTIMIZATION_LEVEL >= 3) + +#include "asm_avr_mult_square.inc" + +__attribute((noinline)) +uECC_VLI_API void uECC_vli_mult(uECC_word_t *result, + const uECC_word_t *left, + const uECC_word_t *right, + wordcount_t num_words) { + /* num_words should already be in r18. */ + register wordcount_t r18 __asm__("r18") = num_words; + + __asm__ volatile ( + "push r18 \n\t" +#if (uECC_MIN_WORDS == 20) + FAST_MULT_ASM_20 + "pop r18 \n\t" + #if (uECC_MAX_WORDS > 20) + FAST_MULT_ASM_20_TO_24 + #endif + #if (uECC_MAX_WORDS > 24) + FAST_MULT_ASM_24_TO_28 + #endif + #if (uECC_MAX_WORDS > 28) + FAST_MULT_ASM_28_TO_32 + #endif +#elif (uECC_MIN_WORDS == 24) + FAST_MULT_ASM_24 + "pop r18 \n\t" + #if (uECC_MAX_WORDS > 24) + FAST_MULT_ASM_24_TO_28 + #endif + #if (uECC_MAX_WORDS > 28) + FAST_MULT_ASM_28_TO_32 + #endif +#elif (uECC_MIN_WORDS == 28) + FAST_MULT_ASM_28 + "pop r18 \n\t" + #if (uECC_MAX_WORDS > 28) + FAST_MULT_ASM_28_TO_32 + #endif +#elif (uECC_MIN_WORDS == 32) + FAST_MULT_ASM_32 + "pop r18 \n\t" +#endif + "2: \n\t" + "eor r1, r1 \n\t" + : "+x" (left), "+y" (right), "+z" (result) + : "r" (r18) + : "r0", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", + "r11", "r12", "r13", "r14", "r15", "r16", "r17", "r19", "r20", + "r21", "r22", "r23", "r24", "r25", "cc" + ); +} +#define asm_mult 1 + +#if uECC_SQUARE_FUNC +__attribute((noinline)) +uECC_VLI_API void uECC_vli_square(uECC_word_t *result, + const uECC_word_t *left, + wordcount_t num_words) { + /* num_words should already be in r20. */ + register wordcount_t r20 __asm__("r20") = num_words; + + __asm__ volatile ( + "push r20 \n\t" +#if (uECC_MIN_WORDS == 20) + FAST_SQUARE_ASM_20 + "pop r20 \n\t" + #if (uECC_MAX_WORDS > 20) + FAST_SQUARE_ASM_20_TO_24 + #endif + #if (uECC_MAX_WORDS > 24) + FAST_SQUARE_ASM_24_TO_28 + #endif + #if (uECC_MAX_WORDS > 28) + FAST_SQUARE_ASM_28_TO_32 + #endif +#elif (uECC_MIN_WORDS == 24) + FAST_SQUARE_ASM_24 + "pop r20 \n\t" + #if (uECC_MAX_WORDS > 24) + FAST_SQUARE_ASM_24_TO_28 + #endif + #if (uECC_MAX_WORDS > 28) + FAST_SQUARE_ASM_28_TO_32 + #endif +#elif (uECC_MIN_WORDS == 28) + FAST_SQUARE_ASM_28 + "pop r20 \n\t" + #if (uECC_MAX_WORDS > 28) + FAST_SQUARE_ASM_28_TO_32 + #endif +#elif (uECC_MIN_WORDS == 32) + FAST_SQUARE_ASM_32 + "pop r20 \n\t" +#endif + "2: \n\t" + "eor r1, r1 \n\t" + : "+x" (left), "+z" (result) + : "r" (r20) + : "r0", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", + "r11", "r12", "r13", "r14", "r15", "r16", "r17", "r18", "r19", + "r21", "r22", "r23", "r24", "r25", "r28", "r29", "cc" + ); +} +#define asm_square 1 +#endif /* uECC_SQUARE_FUNC */ + +#endif /* (uECC_OPTIMIZATION_LEVEL >= 3) */ + +#if uECC_SUPPORTS_secp160r1 +static const struct uECC_Curve_t curve_secp160r1; +static void vli_mmod_fast_secp160r1(uECC_word_t *result, uECC_word_t *product) { + uint8_t carry = 0; + __asm__ volatile ( + "in r30, __SP_L__ \n\t" + "in r31, __SP_H__ \n\t" + "sbiw r30, 24 \n\t" + "in r0, __SREG__ \n\t" + "cli \n\t" + "out __SP_H__, r31 \n\t" + "out __SREG__, r0 \n\t" + "out __SP_L__, r30 \n\t" + + "adiw r30, 25 \n\t" /* we are shifting by 31 bits, so shift over 4 bytes + (+ 1 since z initially points below the stack) */ + "adiw r26, 40 \n\t" /* end of product */ + "ld r18, -x \n\t" /* Load word. */ + "lsr r18 \n\t" /* Shift. */ + "st -z, r18 \n\t" /* Store the first result word. */ + + /* Now we just do the remaining words with the carry bit (using ROR) */ + REPEAT(19, + "ld r18, -x \n\t" + "ror r18 \n\t" + "st -z, r18 \n\t") + + "eor r18, r18 \n\t" /* r18 = 0 */ + "ror r18 \n\t" /* get last bit */ + "st -z, r18 \n\t" /* store it */ + + "sbiw r30, 3 \n\t" /* move z back to point at tmp */ + /* now we add right */ + "ld r18, x+ \n\t" + "st z+, r18 \n\t" /* the first 3 bytes do not need to be added */ + "ld r18, x+ \n\t" + "st z+, r18 \n\t" + "ld r18, x+ \n\t" + "st z+, r18 \n\t" + + "ld r18, x+ \n\t" + "ld r19, z \n\t" + "add r18, r19 \n\t" + "st z+, r18 \n\t" + + /* Now we just do the remaining words with the carry bit (using ADC) */ + REPEAT(16, + "ld r18, x+ \n\t" + "ld r19, z \n\t" + "adc r18, r19 \n\t" + "st z+, r18 \n\t") + + /* Propagate over the remaining bytes of result */ + "ld r18, z \n\t" + "adc r18, r1 \n\t" + "st z+, r18 \n\t" + + "ld r18, z \n\t" + "adc r18, r1 \n\t" + "st z+, r18 \n\t" + + "ld r18, z \n\t" + "adc r18, r1 \n\t" + "st z+, r18 \n\t" + + "ld r18, z \n\t" + "adc r18, r1 \n\t" + "st z+, r18 \n\t" + + "sbiw r30, 24 \n\t" /* move z back to point at tmp */ + "sbiw r26, 40 \n\t" /* move x back to point at product */ + + /* add low bytes of tmp to product, storing in result */ + "ld r18, z+ \n\t" + "ld r19, x+ \n\t" + "add r18, r19 \n\t" + "st y+, r18 \n\t" + REPEAT(19, + "ld r18, z+ \n\t" + "ld r19, x+ \n\t" + "adc r18, r19 \n\t" + "st y+, r18 \n\t") + "adc %[carry], __zero_reg__ \n\t" /* Store carry bit (carry flag is cleared). */ + /* at this point x is at the end of product, y is at the end of result, + z is 20 bytes into tmp */ + "sbiw r28, 20 \n\t" /* move y back to point at result */ + "adiw r30, 4 \n\t" /* move z to point to the end of tmp */ + + /* do omega_mult again with the 4 relevant bytes */ + /* z points to the end of tmp, x points to the end of product */ + "ld r18, -z \n\t" /* Load word. */ + "lsr r18 \n\t" /* Shift. */ + "st -x, r18 \n\t" /* Store the first result word. */ + + "ld r18, -z \n\t" + "ror r18 \n\t" + "st -x, r18 \n\t" + "ld r18, -z \n\t" + "ror r18 \n\t" + "st -x, r18 \n\t" + "ld r18, -z \n\t" + "ror r18 \n\t" + "st -x, r18 \n\t" + + "eor r18, r18 \n\t" /* r18 = 0 */ + "ror r18 \n\t" /* get last bit */ + "st -x, r18 \n\t" /* store it */ + + "sbiw r26, 3 \n\t" /* move x back to point at beginning */ + /* now we add a copy of the 4 bytes */ + "ld r18, z+ \n\t" + "st x+, r18 \n\t" /* the first 3 bytes do not need to be added */ + "ld r18, z+ \n\t" + "st x+, r18 \n\t" + "ld r18, z+ \n\t" + "st x+, r18 \n\t" + + "ld r18, z+ \n\t" + "ld r19, x \n\t" + "add r18, r19 \n\t" + "st x+, r18 \n\t" + + /* Propagate over the remaining bytes */ + "ld r18, x \n\t" + "adc r18, r1 \n\t" + "st x+, r18 \n\t" + + "ld r18, x \n\t" + "adc r18, r1 \n\t" + "st x+, r18 \n\t" + + "ld r18, x \n\t" + "adc r18, r1 \n\t" + "st x+, r18 \n\t" + + "ld r18, x \n\t" + "adc r18, r1 \n\t" + "st x+, r18 \n\t" + + /* now z points to the end of tmp, x points to the end of product + (y still points at result) */ + "sbiw r26, 8 \n\t" /* move x back to point at beginning of actual data */ + /* add into result */ + "ld r18, x+ \n\t" + "ld r19, y \n\t" + "add r18, r19 \n\t" + "st y+, r18 \n\t" + REPEAT(7, + "ld r18, x+ \n\t" + "ld r19, y \n\t" + "adc r18, r19 \n\t" + "st y+, r18 \n\t") + + /* Done adding, now propagate carry bit */ + REPEAT(12, + "ld r18, y \n\t" + "adc r18, __zero_reg__ \n\t" + "st y+, r18 \n\t") + + "adc %[carry], __zero_reg__ \n\t" /* Store carry bit (carry flag is cleared). */ + "sbiw r28, 20 \n\t" /* move y back to point at result */ + + "sbiw r30, 1 \n\t" /* fix stack pointer */ + "in r0, __SREG__ \n\t" + "cli \n\t" + "out __SP_H__, r31 \n\t" + "out __SREG__, r0 \n\t" + "out __SP_L__, r30 \n\t" + + : "+x" (product), [carry] "+r" (carry) + : "y" (result) + : "r0", "r18", "r19", "r30", "r31", "cc" + ); + + if (carry > 0) { + --carry; + uECC_vli_sub(result, result, curve_secp160r1.p, 20); + } + if (carry > 0) { + uECC_vli_sub(result, result, curve_secp160r1.p, 20); + } + if (uECC_vli_cmp_unsafe(result, curve_secp160r1.p, 20) > 0) { + uECC_vli_sub(result, result, curve_secp160r1.p, 20); + } +} +#define asm_mmod_fast_secp160r1 1 +#endif /* uECC_SUPPORTS_secp160r1 */ + +#if uECC_SUPPORTS_secp256k1 +static const struct uECC_Curve_t curve_secp256k1; +static void vli_mmod_fast_secp256k1(uECC_word_t *result, uECC_word_t *product) { + uint8_t carry = 0; + __asm__ volatile ( + "in r30, __SP_L__ \n\t" + "in r31, __SP_H__ \n\t" + "sbiw r30, 37 \n\t" + "in r0, __SREG__ \n\t" + "cli \n\t" + "out __SP_H__, r31 \n\t" + "out __SREG__, r0 \n\t" + "out __SP_L__, r30 \n\t" + + "adiw r30, 1 \n\t" /* add 1 since z initially points below the stack */ + "adiw r26, 32 \n\t" /* product + uECC_WORDS */ + "ldi r25, 0x03 \n\t" + "ldi r24, 0xD1 \n\t" + "ld r18, x+ \n\t" + "ld r19, x+ \n\t" + "ld r20, x+ \n\t" + "ld r21, x+ \n\t" + + "mul r24, r18 \n\t" + "st z+, r0 \n\t" + "mov r22, r1 \n\t" + "ldi r23, 0 \n\t" + + "mul r24, r19 \n\t" + "add r22, r0 \n\t" + "adc r23, r1 \n\t" /* can't overflow */ + "mul r25, r18 \n\t" + "add r22, r0 \n\t" + "adc r23, r1 \n\t" /* can't overflow */ + "st z+, r22 \n\t" + "ldi r22, 0 \n\t" + + "mul r24, r20 \n\t" + "add r23, r0 \n\t" + "adc r22, r1 \n\t" + "mul r25, r19 \n\t" + "add r23, r0 \n\t" + "adc r22, r1 \n\t" + "st z+, r23 \n\t" + "ldi r23, 0 \n\t" + + "mul r24, r21 \n\t" + "add r22, r0 \n\t" + "adc r23, r1 \n\t" + "mul r25, r20 \n\t" + "add r22, r0 \n\t" + "adc r23, r1 \n\t" + "st z+, r22 \n\t" + "ldi r22, 0 \n\t" + + /* now we start adding the 2^32 part as well */ + "add r23, r18 \n\t" // 28 + "adc r22, r22 \n\t" + "ld r18, x+ \n\t" + "mul r24, r18 \n\t" + "add r23, r0 \n\t" + "adc r22, r1 \n\t" + "mul r25, r21 \n\t" + "add r23, r0 \n\t" + "adc r22, r1 \n\t" + "st z+, r23 \n\t" + "ldi r23, 0 \n\t" + + "add r22, r19 \n\t" // 27 + "adc r23, r23 \n\t" + "ld r19, x+ \n\t" + "mul r24, r19 \n\t" + "add r22, r0 \n\t" + "adc r23, r1 \n\t" + "mul r25, r18 \n\t" + "add r22, r0 \n\t" + "adc r23, r1 \n\t" + "st z+, r22 \n\t" + "ldi r22, 0 \n\t" + + REPEAT(6, // 26 - 3 + "add r23, r20 \n\t" + "adc r22, r22 \n\t" + "ld r20, x+ \n\t" + "mul r24, r20 \n\t" + "add r23, r0 \n\t" + "adc r22, r1 \n\t" + "mul r25, r19 \n\t" + "add r23, r0 \n\t" + "adc r22, r1 \n\t" + "st z+, r23 \n\t" + "ldi r23, 0 \n\t" + + "add r22, r21 \n\t" + "adc r23, r23 \n\t" + "ld r21, x+ \n\t" + "mul r24, r21 \n\t" + "add r22, r0 \n\t" + "adc r23, r1 \n\t" + "mul r25, r20 \n\t" + "add r22, r0 \n\t" + "adc r23, r1 \n\t" + "st z+, r22 \n\t" + "ldi r22, 0 \n\t" + + "add r23, r18 \n\t" + "adc r22, r22 \n\t" + "ld r18, x+ \n\t" + "mul r24, r18 \n\t" + "add r23, r0 \n\t" + "adc r22, r1 \n\t" + "mul r25, r21 \n\t" + "add r23, r0 \n\t" + "adc r22, r1 \n\t" + "st z+, r23 \n\t" + "ldi r23, 0 \n\t" + + "add r22, r19 \n\t" + "adc r23, r23 \n\t" + "ld r19, x+ \n\t" + "mul r24, r19 \n\t" + "add r22, r0 \n\t" + "adc r23, r1 \n\t" + "mul r25, r18 \n\t" + "add r22, r0 \n\t" + "adc r23, r1 \n\t" + "st z+, r22 \n\t" + "ldi r22, 0 \n\t") + + "add r23, r20 \n\t" // 2 + "adc r22, r22 \n\t" + "ld r20, x+ \n\t" + "mul r24, r20 \n\t" + "add r23, r0 \n\t" + "adc r22, r1 \n\t" + "mul r25, r19 \n\t" + "add r23, r0 \n\t" + "adc r22, r1 \n\t" + "st z+, r23 \n\t" + "ldi r23, 0 \n\t" + + "add r22, r21 \n\t" // 1 + "adc r23, r23 \n\t" + "ld r21, x+ \n\t" + "mul r24, r21 \n\t" + "add r22, r0 \n\t" + "adc r23, r1 \n\t" + "mul r25, r20 \n\t" + "add r22, r0 \n\t" + "adc r23, r1 \n\t" + "st z+, r22 \n\t" + "ldi r22, 0 \n\t" + + /* Now finish the carries etc */ + "add r23, r18 \n\t" + "adc r22, r22 \n\t" + "mul r25, r21 \n\t" + "add r23, r0 \n\t" + "adc r22, r1 \n\t" + "st z+, r23 \n\t" + "ldi r23, 0 \n\t" + + "add r22, r19 \n\t" + "adc r23, r23 \n\t" + "st z+, r22 \n\t" + "ldi r22, 0 \n\t" + + "add r23, r20 \n\t" + "adc r22, r22 \n\t" + "st z+, r23 \n\t" + "ldi r23, 0 \n\t" + + "add r22, r21 \n\t" + "adc r23, r23 \n\t" + "st z+, r22 \n\t" + "st z+, r23 \n\t" + "eor r1, r1 \n\t" /* make r1 be 0 again */ + + "sbiw r30, 37 \n\t" /* move z back to point at tmp */ + "subi r26, 64 \n\t" /* move x back to point at product */ + "sbc r27, __zero_reg__ \n\t" + + /* add low bytes of tmp to product, storing in result */ + "ld r18, z+ \n\t" + "ld r19, x+ \n\t" + "add r18, r19 \n\t" + "st y+, r18 \n\t" + REPEAT(31, + "ld r18, z+ \n\t" + "ld r19, x+ \n\t" + "adc r18, r19 \n\t" + "st y+, r18 \n\t") + + "adc %[carry], __zero_reg__ \n\t" /* Store carry bit (carry flag is cleared). */ + /* at this point x is at the end of product, y is at the end of result, + z is 32 bytes into tmp */ + "sbiw r28, 32 \n\t" /* move y back to point at result */ + + /* do omega_mult again with the 5 relevant bytes */ + /* z points to tmp + uECC_WORDS, x points to the end of product */ + "sbiw r26, 32 \n\t" /* shift x back to point into the product buffer + (we can overwrite it now) */ + "ld r18, z+ \n\t" + "ld r19, z+ \n\t" + "ld r20, z+ \n\t" + "ld r21, z+ \n\t" + + "mul r24, r18 \n\t" + "st x+, r0 \n\t" + "mov r22, r1 \n\t" + "ldi r23, 0 \n\t" + + "mul r24, r19 \n\t" + "add r22, r0 \n\t" + "adc r23, r1 \n\t" /* can't overflow */ + "mul r25, r18 \n\t" + "add r22, r0 \n\t" + "adc r23, r1 \n\t" /* can't overflow */ + "st x+, r22 \n\t" + "ldi r22, 0 \n\t" + + "mul r24, r20 \n\t" + "add r23, r0 \n\t" + "adc r22, r1 \n\t" + "mul r25, r19 \n\t" + "add r23, r0 \n\t" + "adc r22, r1 \n\t" + "st x+, r23 \n\t" + "ldi r23, 0 \n\t" + + "mul r24, r21 \n\t" + "add r22, r0 \n\t" + "adc r23, r1 \n\t" + "mul r25, r20 \n\t" + "add r22, r0 \n\t" + "adc r23, r1 \n\t" + "st x+, r22 \n\t" + "ldi r22, 0 \n\t" + + "add r23, r18 \n\t" + "adc r22, r22 \n\t" + "ld r18, z+ \n\t" + "mul r24, r18 \n\t" + "add r23, r0 \n\t" + "adc r22, r1 \n\t" + "mul r25, r21 \n\t" + "add r23, r0 \n\t" + "adc r22, r1 \n\t" + "st x+, r23 \n\t" + "ldi r23, 0 \n\t" + + /* Now finish the carries etc */ + "add r22, r19 \n\t" + "adc r23, r23 \n\t" + "mul r25, r18 \n\t" + "add r22, r0 \n\t" + "adc r23, r1 \n\t" + "st x+, r22 \n\t" + "ldi r22, 0 \n\t" + + "add r23, r20 \n\t" + "adc r22, r22 \n\t" + "st x+, r23 \n\t" + "ldi r23, 0 \n\t" + + "add r22, r21 \n\t" + "adc r23, r23 \n\t" + "st x+, r22 \n\t" + "ldi r22, 0 \n\t" + + "add r23, r18 \n\t" + "adc r22, r22 \n\t" + "st x+, r23 \n\t" + "st x+, r22 \n\t" + "eor r1, r1 \n\t" /* make r1 be 0 again */ + + /* now z points to the end of tmp, x points to the end of product + (y still points at result) */ + "sbiw r26, 10 \n\t" /* move x back to point at beginning of actual data */ + /* add into result */ + "ld r18, x+ \n\t" + "ld r19, y \n\t" + "add r18, r19 \n\t" + "st y+, r18 \n\t" + REPEAT(9, + "ld r18, x+ \n\t" + "ld r19, y \n\t" + "adc r18, r19 \n\t" + "st y+, r18 \n\t") + + /* Done adding, now propagate carry bit */ + REPEAT(22, + "ld r18, y \n\t" + "adc r18, __zero_reg__ \n\t" + "st y+, r18 \n\t") + + "adc %[carry], __zero_reg__ \n\t" /* Store carry bit (carry flag is cleared). */ + "sbiw r28, 32 \n\t" /* move y back to point at result */ + + "sbiw r30, 1 \n\t" /* fix stack pointer */ + "in r0, __SREG__ \n\t" + "cli \n\t" + "out __SP_H__, r31 \n\t" + "out __SREG__, r0 \n\t" + "out __SP_L__, r30 \n\t" + + : "+x" (product), [carry] "+r" (carry) + : "y" (result) + : "r0", "r18", "r19", "r20", "r21", "r22", "r23", "r24", "r25", "r30", "r31", "cc" + ); + + if (carry > 0) { + --carry; + uECC_vli_sub(result, result, curve_secp256k1.p, 32); + } + if (carry > 0) { + uECC_vli_sub(result, result, curve_secp256k1.p, 32); + } + if (uECC_vli_cmp_unsafe(result, curve_secp256k1.p, 32) > 0) { + uECC_vli_sub(result, result, curve_secp256k1.p, 32); + } +} +#define asm_mmod_fast_secp256k1 1 +#endif /* uECC_SUPPORTS_secp256k1 */ + +#endif /* (uECC_OPTIMIZATION_LEVEL >= 2) */ + +/* ---- "Small" implementations ---- */ + +#if !asm_add +uECC_VLI_API uECC_word_t uECC_vli_add(uECC_word_t *result, + const uECC_word_t *left, + const uECC_word_t *right, + wordcount_t num_words) { + volatile uECC_word_t *r = result; + uint8_t carry = 0; + uint8_t left_byte; + uint8_t right_byte; + + __asm__ volatile ( + "clc \n\t" + + "1: \n\t" + "ld %[left], x+ \n\t" /* Load left byte. */ + "ld %[right], y+ \n\t" /* Load right byte. */ + "adc %[left], %[right] \n\t" /* Add. */ + "st z+, %[left] \n\t" /* Store the result. */ + "dec %[i] \n\t" + "brne 1b \n\t" + + "adc %[carry], %[carry] \n\t" /* Store carry bit. */ + + : "+z" (r), "+x" (left), "+y" (right), [i] "+r" (num_words), + [carry] "+r" (carry), [left] "=&r" (left_byte), [right] "=&r" (right_byte) + : + : "cc" + ); + return carry; +} +#define asm_add 1 +#endif + +#if !asm_sub +uECC_VLI_API uECC_word_t uECC_vli_sub(uECC_word_t *result, + const uECC_word_t *left, + const uECC_word_t *right, + wordcount_t num_words) { + volatile uECC_word_t *r = result; + uint8_t borrow = 0; + uint8_t left_byte; + uint8_t right_byte; + + __asm__ volatile ( + "clc \n\t" + + "1: \n\t" + "ld %[left], x+ \n\t" /* Load left byte. */ + "ld %[right], y+ \n\t" /* Load right byte. */ + "sbc %[left], %[right] \n\t" /* Subtract. */ + "st z+, %[left] \n\t" /* Store the result. */ + "dec %[i] \n\t" + "brne 1b \n\t" + + "adc %[borrow], %[borrow] \n\t" /* Store carry bit in borrow. */ + + : "+z" (r), "+x" (left), "+y" (right), [i] "+r" (i), + [borrow] "+r" (borrow), [left] "=&r" (left_byte), [right] "=&r" (right_byte) + : + : "cc" + ); + return borrow; +} +#define asm_sub 1 +#endif + +#if !asm_mult +__attribute((noinline)) +uECC_VLI_API void uECC_vli_mult(uECC_word_t *result, + const uECC_word_t *left, + const uECC_word_t *right, + wordcount_t num_words) { + volatile uECC_word_t *r = result; + uint8_t r0 = 0; + uint8_t r1 = 0; + uint8_t r2 = 0; + uint8_t zero = 0; + uint8_t k, i; + + __asm__ volatile ( + "ldi %[k], 1 \n\t" /* k = 1; k < num_words; ++k */ + + "1: \n\t" + "ldi %[i], 0 \n\t" /* i = 0; i < k; ++i */ + + "add r28, %[k] \n\t" /* pre-add right ptr */ + "adc r29, %[zero] \n\t" + + "2: \n\t" + "ld r0, x+ \n\t" + "ld r1, -y \n\t" + "mul r0, r1 \n\t" + + "add %[r0], r0 \n\t" + "adc %[r1], r1 \n\t" + "adc %[r2], %[zero] \n\t" + + "inc %[i] \n\t" + "cp %[i], %[k] \n\t" + "brlo 2b \n\t" /* loop if i < k */ + + "sub r26, %[k] \n\t" /* fix up left ptr */ + "sbc r27, %[zero] \n\t" + + "st z+, %[r0] \n\t" /* Store the result. */ + "mov %[r0], %[r1] \n\t" + "mov %[r1], %[r2] \n\t" + "mov %[r2], %[zero] \n\t" + + "inc %[k] \n\t" + "cp %[k], %[num] \n\t" + "brlo 1b \n\t" /* loop if k < num_words */ + + /* second half */ + "mov %[k], %[num] \n\t" /* k = num_words; k > 0; --k */ + "add r28, %[num] \n\t" /* move right ptr to point at the end of right */ + "adc r29, %[zero] \n\t" + + "1: \n\t" + "ldi %[i], 0 \n\t" /* i = 0; i < k; ++i */ + + "2: \n\t" + "ld r0, x+ \n\t" + "ld r1, -y \n\t" + "mul r0, r1 \n\t" + + "add %[r0], r0 \n\t" + "adc %[r1], r1 \n\t" + "adc %[r2], %[zero] \n\t" + + "inc %[i] \n\t" + "cp %[i], %[k] \n\t" + "brlo 2b \n\t" /* loop if i < k */ + + "add r28, %[k] \n\t" /* fix up right ptr */ + "adc r29, %[zero] \n\t" + + "st z+, %[r0] \n\t" /* Store the result. */ + "mov %[r0], %[r1] \n\t" + "mov %[r1], %[r2] \n\t" + "mov %[r2], %[zero] \n\t" + + "dec %[k] \n\t" + "sub r26, %[k] \n\t" /* fix up left ptr (after k is decremented, so next time + we start 1 higher) */ + "sbc r27, %[zero] \n\t" + + "cp %[k], %[zero] \n\t" + "brne 1b \n\t" /* loop if k > 0 */ + + "st z+, %[r0] \n\t" /* Store last result byte. */ + "eor r1, r1 \n\t" /* fix r1 to be 0 again */ + + : "+z" (result), "+x" (left), "+y" (right), + [r0] "+r" (r0), [r1] "+r" (r1), [r2] "+r" (r2), + [zero] "+r" (zero), [num] "+r" (num_words), + [k] "=&r" (k), [i] "=&r" (i) + : + : "r0", "cc" + ); +} +#define asm_mult 1 +#endif + +#if (uECC_SQUARE_FUNC && !asm_square) +uECC_VLI_API void uECC_vli_square(uECC_word_t *result, + const uECC_word_t *left, + wordcount_t num_words) { + volatile uECC_word_t *r = result; + uint8_t r0 = 0; + uint8_t r1 = 0; + uint8_t r2 = 0; + uint8_t zero = 0; + uint8_t k; + + __asm__ volatile ( + "ldi %[k], 1 \n\t" /* k = 1; k < num_words * 2; ++k */ + + "1: \n\t" + + "movw r26, %[orig] \n\t" /* copy orig ptr to 'left' ptr */ + "movw r30, %[orig] \n\t" /* copy orig ptr to 'right' ptr */ + "cp %[k], %[num] \n\t" + "brlo 2f \n\t" + "breq 2f \n\t" + + /* when k > num_words, we start from (k - num_words) on the 'left' ptr */ + "add r26, %[k] \n\t" + "adc r27, %[zero] \n\t" + "sub r26, %[num] \n\t" + "sbc r27, %[zero] \n\t" + "add r30, %[num] \n\t" /* move right ptr to point at the end */ + "adc r31, %[zero] \n\t" + "rjmp 3f \n\t" + + "2: \n\t" /* when k <= num_words, we add k to the 'right' ptr */ + "add r30, %[k] \n\t" /* pre-add 'right' ptr */ + "adc r31, %[zero] \n\t" + + "3: \n\t" + "ld r0, x+ \n\t" + "cp r26, r30 \n\t" /* if left == right here, then we are done after this mult + (and we don't need to double) */ + "breq 4f \n\t" + "ld r1, -z \n\t" + "mul r0, r1 \n\t" + + /* add twice since it costs the same as doubling */ + "add %[r0], r0 \n\t" + "adc %[r1], r1 \n\t" + "adc %[r2], %[zero] \n\t" + "add %[r0], r0 \n\t" + "adc %[r1], r1 \n\t" + "adc %[r2], %[zero] \n\t" + + "cpse r26, r30 \n\t" /* if left == right here, then we are done */ + "rjmp 3b \n\t" + "rjmp 5f \n\t" /* skip code for non-doubled mult */ + + "4: \n\t" + "ld r1, -z \n\t" + "mul r0, r1 \n\t" + "add %[r0], r0 \n\t" + "adc %[r1], r1 \n\t" + "adc %[r2], %[zero] \n\t" + + "5: \n\t" + "movw r30, %[result] \n\t" /* make z point to result */ + "st z+, %[r0] \n\t" /* Store the result. */ + "movw %[result], r30 \n\t" /* update result ptr*/ + "mov %[r0], %[r1] \n\t" + "mov %[r1], %[r2] \n\t" + "mov %[r2], %[zero] \n\t" + + "inc %[k] \n\t" + "cp %[k], %[max] \n\t" + "brlo 1b \n\t" /* loop if k < num_words * 2 */ + + "movw r30, %[result] \n\t" /* make z point to result */ + "st z+, %[r0] \n\t" /* Store last result byte. */ + "eor r1, r1 \n\t" /* fix r1 to be 0 again */ + + : [result] "+r" (r), + [r0] "+r" (r0), [r1] "+r" (r1), [r2] "+r" (r2), [zero] "+r" (zero), + [k] "=&a" (k) + : [orig] "r" (left), [max] "r" ((uint8_t)(2 * num_words)), + [num] "r" (num_words) + : "r0", "r26", "r27", "r30", "r31", "cc" + ); +} +#define asm_square 1 +#endif /* uECC_SQUARE_FUNC && !asm_square */ + +#endif /* _UECC_ASM_AVR_H_ */ diff --git a/deps/picoquic/picotls/deps/micro-ecc/asm_avr_mult_square.inc b/deps/picoquic/picotls/deps/micro-ecc/asm_avr_mult_square.inc new file mode 100644 index 0000000..7ae08bc --- /dev/null +++ b/deps/picoquic/picotls/deps/micro-ecc/asm_avr_mult_square.inc @@ -0,0 +1,26311 @@ +/* Copyright 2015, Kenneth MacKay. Licensed under the BSD 2-clause license. */ + +#ifndef _UECC_ASM_AVR_MULT_SQUARE_H_ +#define _UECC_ASM_AVR_MULT_SQUARE_H_ + +#define FAST_MULT_ASM_20 \ + "adiw r30, 10 \n\t" \ + "adiw r28, 10 \n\t" \ + "ld r2, x+ \n\t" \ + "ld r3, x+ \n\t" \ + "ld r4, x+ \n\t" \ + "ld r5, x+ \n\t" \ + "ld r6, x+ \n\t" \ + "ld r7, x+ \n\t" \ + "ld r8, x+ \n\t" \ + "ld r9, x+ \n\t" \ + "ld r10, x+ \n\t" \ + "ld r11, x+ \n\t" \ + "ld r12, y+ \n\t" \ + "ld r13, y+ \n\t" \ + "ld r14, y+ \n\t" \ + "ld r15, y+ \n\t" \ + "ld r16, y+ \n\t" \ + "ld r17, y+ \n\t" \ + "ld r18, y+ \n\t" \ + "ld r19, y+ \n\t" \ + "ld r20, y+ \n\t" \ + "ld r21, y+ \n\t" \ + "ldi r25, 0 \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r2, r12 \n\t" \ + "st z+, r0 \n\t" \ + "mov r22, r1 \n\t" \ + \ + "ldi r24, 0 \n\t" \ + "mul r2, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "mul r3, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r2, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r2, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ldi r24, 0 \n\t" \ + "mul r2, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r2, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r2, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ldi r24, 0 \n\t" \ + "mul r2, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r2, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r2, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r10, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ldi r24, 0 \n\t" \ + "mul r3, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r10, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r4, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r5, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r10, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ldi r24, 0 \n\t" \ + "mul r6, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r10, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r7, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r8, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r10, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ldi r24, 0 \n\t" \ + "mul r9, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r10, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r10, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "mul r11, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "st z+, r24 \n\t" \ + "st z+, r22 \n\t" \ + \ + "sbiw r30, 30 \n\t" \ + "sbiw r28, 20 \n\t" \ + "ld r12, y+ \n\t" \ + "ld r13, y+ \n\t" \ + "ld r14, y+ \n\t" \ + "ld r15, y+ \n\t" \ + "ld r16, y+ \n\t" \ + "ld r17, y+ \n\t" \ + "ld r18, y+ \n\t" \ + "ld r19, y+ \n\t" \ + "ld r20, y+ \n\t" \ + "ld r21, y+ \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r2, r12 \n\t" \ + "st z+, r0 \n\t" \ + "mov r22, r1 \n\t" \ + \ + "ldi r24, 0 \n\t" \ + "mul r2, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "mul r3, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r2, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r2, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ldi r24, 0 \n\t" \ + "mul r2, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r2, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r2, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ldi r24, 0 \n\t" \ + "mul r2, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r2, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r2, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r10, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ld r2, x+ \n\t" \ + "ldi r24, 0 \n\t" \ + "mul r3, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r10, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r2, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r25 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ld r3, x+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r4, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r4, x+ \n\t" \ + "ldi r23, 0 \n\t" \ + "mul r5, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r10, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r2, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r25 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ld r5, x+ \n\t" \ + "ldi r24, 0 \n\t" \ + "mul r6, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r10, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r2, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r25 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ld r6, x+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r7, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r7, x+ \n\t" \ + "ldi r23, 0 \n\t" \ + "mul r8, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r10, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r2, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r25 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ld r8, x+ \n\t" \ + "ldi r24, 0 \n\t" \ + "mul r9, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r10, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r2, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r25 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ld r9, x+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r10, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r10, x+ \n\t" \ + "ldi r23, 0 \n\t" \ + "mul r11, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r2, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r10, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r25 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ld r11, x+ \n\t" \ + "ldi r24, 0 \n\t" \ + "mul r2, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r10, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r25 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ld r12, y+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r2, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r13, y+ \n\t" \ + "ldi r23, 0 \n\t" \ + "mul r2, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r10, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r25 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ld r14, y+ \n\t" \ + "ldi r24, 0 \n\t" \ + "mul r2, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r10, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r25 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ld r15, y+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r2, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r16, y+ \n\t" \ + "ldi r23, 0 \n\t" \ + "mul r2, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r10, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r25 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ld r17, y+ \n\t" \ + "ldi r24, 0 \n\t" \ + "mul r2, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r10, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r25 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ld r18, y+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r2, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r19, y+ \n\t" \ + "ldi r23, 0 \n\t" \ + "mul r2, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r10, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r25 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ld r20, y+ \n\t" \ + "ldi r24, 0 \n\t" \ + "mul r2, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r10, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r25 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ld r21, y+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r2, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r3, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r10, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ldi r24, 0 \n\t" \ + "mul r4, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r10, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r5, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r6, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r10, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ldi r24, 0 \n\t" \ + "mul r7, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r10, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r8, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r9, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r10, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ldi r24, 0 \n\t" \ + "mul r10, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "mul r11, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "st z+, r23 \n\t" \ + "st z+, r24 \n\t" + +#define FAST_MULT_ASM_20_TO_24 \ + "cpi r18, 20 \n\t" \ + "brne 1f \n\t" \ + "jmp 2f \n\t" \ + "1: \n\t" \ + "ld r2, x+ \n\t" \ + "ld r6, y+ \n\t" \ + "ld r3, x+ \n\t" \ + "ld r7, y+ \n\t" \ + "ld r4, x+ \n\t" \ + "ld r8, y+ \n\t" \ + "ld r5, x+ \n\t" \ + "ld r9, y+ \n\t" \ + "sbiw r26, 24 \n\t" \ + "sbiw r28, 24 \n\t" \ + "sbiw r30, 20 \n\t" \ + "ld r10, x+ \n\t" \ + "ld r14, y+ \n\t" \ + "ld r11, x+ \n\t" \ + "ld r15, y+ \n\t" \ + "ld r12, x+ \n\t" \ + "ld r16, y+ \n\t" \ + "ld r13, x+ \n\t" \ + "ld r17, y+ \n\t" \ + \ + "mul r2, r14 \n\t" \ + "mov r19, r0 \n\t" \ + "mov r20, r1 \n\t" \ + "ldi r21, 0 \n\t" \ + "ld r0, z \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r6, r10 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "st z+, r19 \n\t" \ + \ + "ldi r19, 0 \n\t" \ + "ld r0, z \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r2, r15 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r6, r11 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r3, r14 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r7, r10 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "st z+, r20 \n\t" \ + \ + "ldi r20, 0 \n\t" \ + "ld r0, z \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r2, r16 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r6, r12 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r3, r15 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r7, r11 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r4, r14 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r8, r10 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "st z+, r21 \n\t" \ + \ + "ldi r21, 0 \n\t" \ + "ld r0, z \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r2, r17 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r6, r13 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r3, r16 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r7, r12 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r4, r15 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r8, r11 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r5, r14 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r9, r10 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "st z+, r19 \n\t" \ + \ + "ldi r19, 0 \n\t" \ + "ld r0, z \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r25 \n\t" \ + "ld r10, x+ \n\t" \ + "ld r14, y+ \n\t" \ + "mul r2, r14 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r6, r10 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r3, r17 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r7, r13 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r4, r16 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r8, r12 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r5, r15 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r9, r11 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "st z+, r20 \n\t" \ + \ + "ldi r20, 0 \n\t" \ + "ld r0, z \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r25 \n\t" \ + "ld r11, x+ \n\t" \ + "ld r15, y+ \n\t" \ + "mul r2, r15 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r6, r11 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r3, r14 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r7, r10 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r4, r17 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r8, r13 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r5, r16 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r9, r12 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "st z+, r21 \n\t" \ + \ + "ldi r21, 0 \n\t" \ + "ld r0, z \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r25 \n\t" \ + "ld r12, x+ \n\t" \ + "ld r16, y+ \n\t" \ + "mul r2, r16 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r6, r12 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r3, r15 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r7, r11 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r4, r14 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r8, r10 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r5, r17 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r9, r13 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "st z+, r19 \n\t" \ + \ + "ldi r19, 0 \n\t" \ + "ld r0, z \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r25 \n\t" \ + "ld r13, x+ \n\t" \ + "ld r17, y+ \n\t" \ + "mul r2, r17 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r6, r13 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r3, r16 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r7, r12 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r4, r15 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r8, r11 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r5, r14 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r9, r10 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "st z+, r20 \n\t" \ + \ + "ldi r20, 0 \n\t" \ + "ld r0, z \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r25 \n\t" \ + "ld r10, x+ \n\t" \ + "ld r14, y+ \n\t" \ + "mul r2, r14 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r6, r10 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r3, r17 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r7, r13 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r4, r16 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r8, r12 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r5, r15 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r9, r11 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "st z+, r21 \n\t" \ + \ + "ldi r21, 0 \n\t" \ + "ld r0, z \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r25 \n\t" \ + "ld r11, x+ \n\t" \ + "ld r15, y+ \n\t" \ + "mul r2, r15 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r6, r11 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r3, r14 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r7, r10 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r4, r17 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r8, r13 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r5, r16 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r9, r12 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "st z+, r19 \n\t" \ + \ + "ldi r19, 0 \n\t" \ + "ld r0, z \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r25 \n\t" \ + "ld r12, x+ \n\t" \ + "ld r16, y+ \n\t" \ + "mul r2, r16 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r6, r12 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r3, r15 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r7, r11 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r4, r14 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r8, r10 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r5, r17 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r9, r13 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "st z+, r20 \n\t" \ + \ + "ldi r20, 0 \n\t" \ + "ld r0, z \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r25 \n\t" \ + "ld r13, x+ \n\t" \ + "ld r17, y+ \n\t" \ + "mul r2, r17 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r6, r13 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r3, r16 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r7, r12 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r4, r15 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r8, r11 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r5, r14 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r9, r10 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "st z+, r21 \n\t" \ + \ + "ldi r21, 0 \n\t" \ + "ld r0, z \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r25 \n\t" \ + "ld r10, x+ \n\t" \ + "ld r14, y+ \n\t" \ + "mul r2, r14 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r6, r10 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r3, r17 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r7, r13 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r4, r16 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r8, r12 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r5, r15 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r9, r11 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "st z+, r19 \n\t" \ + \ + "ldi r19, 0 \n\t" \ + "ld r0, z \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r25 \n\t" \ + "ld r11, x+ \n\t" \ + "ld r15, y+ \n\t" \ + "mul r2, r15 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r6, r11 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r3, r14 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r7, r10 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r4, r17 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r8, r13 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r5, r16 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r9, r12 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "st z+, r20 \n\t" \ + \ + "ldi r20, 0 \n\t" \ + "ld r0, z \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r25 \n\t" \ + "ld r12, x+ \n\t" \ + "ld r16, y+ \n\t" \ + "mul r2, r16 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r6, r12 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r3, r15 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r7, r11 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r4, r14 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r8, r10 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r5, r17 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r9, r13 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "st z+, r21 \n\t" \ + \ + "ldi r21, 0 \n\t" \ + "ld r0, z \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r25 \n\t" \ + "ld r13, x+ \n\t" \ + "ld r17, y+ \n\t" \ + "mul r2, r17 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r6, r13 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r3, r16 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r7, r12 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r4, r15 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r8, r11 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r5, r14 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r9, r10 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "st z+, r19 \n\t" \ + \ + "ldi r19, 0 \n\t" \ + "ld r0, z \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r25 \n\t" \ + "ld r10, x+ \n\t" \ + "ld r14, y+ \n\t" \ + "mul r2, r14 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r6, r10 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r3, r17 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r7, r13 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r4, r16 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r8, r12 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r5, r15 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r9, r11 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "st z+, r20 \n\t" \ + \ + "ldi r20, 0 \n\t" \ + "ld r0, z \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r25 \n\t" \ + "ld r11, x+ \n\t" \ + "ld r15, y+ \n\t" \ + "mul r2, r15 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r6, r11 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r3, r14 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r7, r10 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r4, r17 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r8, r13 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r5, r16 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r9, r12 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "st z+, r21 \n\t" \ + \ + "ldi r21, 0 \n\t" \ + "ld r0, z \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r25 \n\t" \ + "ld r12, x+ \n\t" \ + "ld r16, y+ \n\t" \ + "mul r2, r16 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r6, r12 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r3, r15 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r7, r11 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r4, r14 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r8, r10 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r5, r17 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r9, r13 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "st z+, r19 \n\t" \ + \ + "ldi r19, 0 \n\t" \ + "ld r0, z \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r25 \n\t" \ + "ld r13, x+ \n\t" \ + "ld r17, y+ \n\t" \ + "mul r2, r17 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r6, r13 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r3, r16 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r7, r12 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r4, r15 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r8, r11 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r5, r14 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r9, r10 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "st z+, r20 \n\t" \ + \ + "ldi r20, 0 \n\t" \ + "mul r11, r9 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r12, r8 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r13, r7 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r2, r6 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r3, r17 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r4, r16 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r5, r15 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "st z+, r21 \n\t" \ + \ + "ldi r21, 0 \n\t" \ + "mul r12, r9 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r13, r8 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r2, r7 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r3, r6 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r4, r17 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r5, r16 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "st z+, r19 \n\t" \ + \ + "ldi r19, 0 \n\t" \ + "mul r13, r9 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r2, r8 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r3, r7 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r4, r6 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r5, r17 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "st z+, r20 \n\t" \ + \ + "ldi r20, 0 \n\t" \ + "mul r2, r9 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r3, r8 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r4, r7 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r5, r6 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "st z+, r21 \n\t" \ + \ + "ldi r21, 0 \n\t" \ + "mul r3, r9 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r4, r8 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r5, r7 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "st z+, r19 \n\t" \ + \ + "ldi r19, 0 \n\t" \ + "mul r4, r9 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r5, r8 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "st z+, r20 \n\t" \ + \ + "mul r5, r9 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "st z+, r21 \n\t" \ + "st z+, r19 \n\t" \ + "adiw r26, 4 \n\t" \ + "adiw r28, 4 \n\t" + +#define FAST_MULT_ASM_24 \ + "adiw r30, 20 \n\t" \ + "adiw r28, 20 \n\t" \ + "ld r2, x+ \n\t" \ + "ld r3, x+ \n\t" \ + "ld r4, x+ \n\t" \ + "ld r5, x+ \n\t" \ + "ld r12, y+ \n\t" \ + "ld r13, y+ \n\t" \ + "ld r14, y+ \n\t" \ + "ld r15, y+ \n\t" \ + "ldi r25, 0 \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r2, r12 \n\t" \ + "st z+, r0 \n\t" \ + "mov r22, r1 \n\t" \ + \ + "ldi r24, 0 \n\t" \ + "mul r2, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "mul r3, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r2, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r2, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ldi r24, 0 \n\t" \ + "mul r3, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r4, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "mul r5, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "st z+, r24 \n\t" \ + "st z+, r22 \n\t" \ + \ + "sbiw r30, 18 \n\t" \ + "sbiw r28, 14 \n\t" \ + "ld r12, y+ \n\t" \ + "ld r13, y+ \n\t" \ + "ld r14, y+ \n\t" \ + "ld r15, y+ \n\t" \ + "ld r16, y+ \n\t" \ + "ld r17, y+ \n\t" \ + "ld r18, y+ \n\t" \ + "ld r19, y+ \n\t" \ + "ld r20, y+ \n\t" \ + "ld r21, y+ \n\t" \ + "ld r6, x+ \n\t" \ + "ld r7, x+ \n\t" \ + "ld r8, x+ \n\t" \ + "ld r9, x+ \n\t" \ + "ld r10, x+ \n\t" \ + "ld r11, x+ \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r2, r12 \n\t" \ + "st z+, r0 \n\t" \ + "mov r22, r1 \n\t" \ + \ + "ldi r24, 0 \n\t" \ + "mul r2, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "mul r3, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r2, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r2, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ldi r24, 0 \n\t" \ + "mul r2, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r2, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r2, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ldi r24, 0 \n\t" \ + "mul r2, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r2, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r2, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r10, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ld r2, x+ \n\t" \ + "ldi r24, 0 \n\t" \ + "mul r3, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r10, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r2, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r25 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ld r3, x+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r4, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r4, x+ \n\t" \ + "ldi r23, 0 \n\t" \ + "mul r5, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r10, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r2, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r25 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ld r5, x+ \n\t" \ + "ldi r24, 0 \n\t" \ + "mul r6, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r10, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r2, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r25 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ld r12, y+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r6, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r13, y+ \n\t" \ + "ldi r23, 0 \n\t" \ + "mul r6, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r10, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r2, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r25 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ld r14, y+ \n\t" \ + "ldi r24, 0 \n\t" \ + "mul r6, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r10, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r2, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r25 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ld r15, y+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r6, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r7, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r10, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r2, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ldi r24, 0 \n\t" \ + "mul r8, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r10, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r2, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r9, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r10, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r2, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ldi r24, 0 \n\t" \ + "mul r11, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r2, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r2, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r3, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ldi r24, 0 \n\t" \ + "mul r4, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "mul r5, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "st z+, r23 \n\t" \ + "st z+, r24 \n\t" \ + \ + "sbiw r30, 38 \n\t" \ + "sbiw r28, 24 \n\t" \ + "sbiw r26, 14 \n\t" \ + "ld r2, x+ \n\t" \ + "ld r12, y+ \n\t" \ + "ld r3, x+ \n\t" \ + "ld r13, y+ \n\t" \ + "ld r4, x+ \n\t" \ + "ld r14, y+ \n\t" \ + "ld r5, x+ \n\t" \ + "ld r15, y+ \n\t" \ + "ld r6, x+ \n\t" \ + "ld r16, y+ \n\t" \ + "ld r7, x+ \n\t" \ + "ld r17, y+ \n\t" \ + "ld r8, x+ \n\t" \ + "ld r18, y+ \n\t" \ + "ld r9, x+ \n\t" \ + "ld r19, y+ \n\t" \ + "ld r10, x+ \n\t" \ + "ld r20, y+ \n\t" \ + "ld r11, x+ \n\t" \ + "ld r21, y+ \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r2, r12 \n\t" \ + "st z+, r0 \n\t" \ + "mov r22, r1 \n\t" \ + \ + "ldi r24, 0 \n\t" \ + "mul r2, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "mul r3, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r2, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r2, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ldi r24, 0 \n\t" \ + "mul r2, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r2, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r2, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ldi r24, 0 \n\t" \ + "mul r2, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r2, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r2, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r10, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ld r2, x+ \n\t" \ + "ldi r24, 0 \n\t" \ + "mul r3, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r10, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r2, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r25 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ld r3, x+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r4, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r4, x+ \n\t" \ + "ldi r23, 0 \n\t" \ + "mul r5, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r10, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r2, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r25 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ld r5, x+ \n\t" \ + "ldi r24, 0 \n\t" \ + "mul r6, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r10, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r2, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r25 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ld r6, x+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r7, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r7, x+ \n\t" \ + "ldi r23, 0 \n\t" \ + "mul r8, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r10, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r2, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r25 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ld r8, x+ \n\t" \ + "ldi r24, 0 \n\t" \ + "mul r9, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r10, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r2, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r25 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ld r9, x+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r10, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r10, x+ \n\t" \ + "ldi r23, 0 \n\t" \ + "mul r11, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r2, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r10, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r25 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ld r11, x+ \n\t" \ + "ldi r24, 0 \n\t" \ + "mul r2, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r10, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r25 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ld r2, x+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r3, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r3, x+ \n\t" \ + "ldi r23, 0 \n\t" \ + "mul r4, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r10, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r2, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r25 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ld r4, x+ \n\t" \ + "ldi r24, 0 \n\t" \ + "mul r5, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r10, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r2, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r25 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ld r5, x+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r6, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r12, y+ \n\t" \ + "ldi r23, 0 \n\t" \ + "mul r6, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r10, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r2, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r25 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ld r13, y+ \n\t" \ + "ldi r24, 0 \n\t" \ + "mul r6, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r10, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r2, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r25 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ld r14, y+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r6, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r15, y+ \n\t" \ + "ldi r23, 0 \n\t" \ + "mul r6, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r10, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r2, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r25 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ld r16, y+ \n\t" \ + "ldi r24, 0 \n\t" \ + "mul r6, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r10, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r2, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r25 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ld r17, y+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r6, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r18, y+ \n\t" \ + "ldi r23, 0 \n\t" \ + "mul r6, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r10, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r2, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r25 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ld r19, y+ \n\t" \ + "ldi r24, 0 \n\t" \ + "mul r6, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r10, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r2, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r25 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ld r20, y+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r6, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r21, y+ \n\t" \ + "ldi r23, 0 \n\t" \ + "mul r6, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r10, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r2, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r25 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ld r12, y+ \n\t" \ + "ldi r24, 0 \n\t" \ + "mul r6, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r10, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r2, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r25 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ld r13, y+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r6, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r14, y+ \n\t" \ + "ldi r23, 0 \n\t" \ + "mul r6, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r10, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r2, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r25 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ld r15, y+ \n\t" \ + "ldi r24, 0 \n\t" \ + "mul r6, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r10, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r2, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r25 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r7, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r8, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r10, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r2, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ldi r24, 0 \n\t" \ + "mul r9, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r10, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r2, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r10, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r11, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r2, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ldi r24, 0 \n\t" \ + "mul r2, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r3, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r4, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "mul r5, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "st z+, r22 \n\t" \ + "st z+, r23 \n\t" + +#define FAST_MULT_ASM_24_TO_28 \ + "cpi r18, 24 \n\t" \ + "brne 1f \n\t" \ + "jmp 2f \n\t" \ + "1: \n\t" \ + "ld r2, x+ \n\t" \ + "ld r6, y+ \n\t" \ + "ld r3, x+ \n\t" \ + "ld r7, y+ \n\t" \ + "ld r4, x+ \n\t" \ + "ld r8, y+ \n\t" \ + "ld r5, x+ \n\t" \ + "ld r9, y+ \n\t" \ + "sbiw r26, 28 \n\t" \ + "sbiw r28, 28 \n\t" \ + "sbiw r30, 24 \n\t" \ + "ld r10, x+ \n\t" \ + "ld r14, y+ \n\t" \ + "ld r11, x+ \n\t" \ + "ld r15, y+ \n\t" \ + "ld r12, x+ \n\t" \ + "ld r16, y+ \n\t" \ + "ld r13, x+ \n\t" \ + "ld r17, y+ \n\t" \ + \ + "mul r2, r14 \n\t" \ + "mov r19, r0 \n\t" \ + "mov r20, r1 \n\t" \ + "ldi r21, 0 \n\t" \ + "ld r0, z \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r6, r10 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "st z+, r19 \n\t" \ + \ + "ldi r19, 0 \n\t" \ + "ld r0, z \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r2, r15 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r6, r11 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r3, r14 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r7, r10 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "st z+, r20 \n\t" \ + \ + "ldi r20, 0 \n\t" \ + "ld r0, z \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r2, r16 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r6, r12 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r3, r15 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r7, r11 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r4, r14 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r8, r10 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "st z+, r21 \n\t" \ + \ + "ldi r21, 0 \n\t" \ + "ld r0, z \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r2, r17 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r6, r13 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r3, r16 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r7, r12 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r4, r15 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r8, r11 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r5, r14 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r9, r10 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "st z+, r19 \n\t" \ + \ + "ldi r19, 0 \n\t" \ + "ld r0, z \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r25 \n\t" \ + "ld r10, x+ \n\t" \ + "ld r14, y+ \n\t" \ + "mul r2, r14 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r6, r10 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r3, r17 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r7, r13 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r4, r16 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r8, r12 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r5, r15 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r9, r11 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "st z+, r20 \n\t" \ + \ + "ldi r20, 0 \n\t" \ + "ld r0, z \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r25 \n\t" \ + "ld r11, x+ \n\t" \ + "ld r15, y+ \n\t" \ + "mul r2, r15 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r6, r11 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r3, r14 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r7, r10 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r4, r17 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r8, r13 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r5, r16 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r9, r12 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "st z+, r21 \n\t" \ + \ + "ldi r21, 0 \n\t" \ + "ld r0, z \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r25 \n\t" \ + "ld r12, x+ \n\t" \ + "ld r16, y+ \n\t" \ + "mul r2, r16 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r6, r12 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r3, r15 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r7, r11 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r4, r14 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r8, r10 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r5, r17 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r9, r13 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "st z+, r19 \n\t" \ + \ + "ldi r19, 0 \n\t" \ + "ld r0, z \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r25 \n\t" \ + "ld r13, x+ \n\t" \ + "ld r17, y+ \n\t" \ + "mul r2, r17 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r6, r13 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r3, r16 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r7, r12 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r4, r15 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r8, r11 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r5, r14 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r9, r10 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "st z+, r20 \n\t" \ + \ + "ldi r20, 0 \n\t" \ + "ld r0, z \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r25 \n\t" \ + "ld r10, x+ \n\t" \ + "ld r14, y+ \n\t" \ + "mul r2, r14 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r6, r10 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r3, r17 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r7, r13 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r4, r16 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r8, r12 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r5, r15 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r9, r11 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "st z+, r21 \n\t" \ + \ + "ldi r21, 0 \n\t" \ + "ld r0, z \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r25 \n\t" \ + "ld r11, x+ \n\t" \ + "ld r15, y+ \n\t" \ + "mul r2, r15 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r6, r11 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r3, r14 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r7, r10 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r4, r17 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r8, r13 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r5, r16 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r9, r12 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "st z+, r19 \n\t" \ + \ + "ldi r19, 0 \n\t" \ + "ld r0, z \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r25 \n\t" \ + "ld r12, x+ \n\t" \ + "ld r16, y+ \n\t" \ + "mul r2, r16 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r6, r12 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r3, r15 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r7, r11 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r4, r14 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r8, r10 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r5, r17 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r9, r13 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "st z+, r20 \n\t" \ + \ + "ldi r20, 0 \n\t" \ + "ld r0, z \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r25 \n\t" \ + "ld r13, x+ \n\t" \ + "ld r17, y+ \n\t" \ + "mul r2, r17 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r6, r13 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r3, r16 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r7, r12 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r4, r15 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r8, r11 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r5, r14 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r9, r10 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "st z+, r21 \n\t" \ + \ + "ldi r21, 0 \n\t" \ + "ld r0, z \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r25 \n\t" \ + "ld r10, x+ \n\t" \ + "ld r14, y+ \n\t" \ + "mul r2, r14 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r6, r10 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r3, r17 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r7, r13 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r4, r16 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r8, r12 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r5, r15 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r9, r11 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "st z+, r19 \n\t" \ + \ + "ldi r19, 0 \n\t" \ + "ld r0, z \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r25 \n\t" \ + "ld r11, x+ \n\t" \ + "ld r15, y+ \n\t" \ + "mul r2, r15 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r6, r11 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r3, r14 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r7, r10 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r4, r17 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r8, r13 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r5, r16 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r9, r12 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "st z+, r20 \n\t" \ + \ + "ldi r20, 0 \n\t" \ + "ld r0, z \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r25 \n\t" \ + "ld r12, x+ \n\t" \ + "ld r16, y+ \n\t" \ + "mul r2, r16 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r6, r12 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r3, r15 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r7, r11 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r4, r14 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r8, r10 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r5, r17 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r9, r13 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "st z+, r21 \n\t" \ + \ + "ldi r21, 0 \n\t" \ + "ld r0, z \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r25 \n\t" \ + "ld r13, x+ \n\t" \ + "ld r17, y+ \n\t" \ + "mul r2, r17 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r6, r13 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r3, r16 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r7, r12 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r4, r15 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r8, r11 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r5, r14 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r9, r10 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "st z+, r19 \n\t" \ + \ + "ldi r19, 0 \n\t" \ + "ld r0, z \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r25 \n\t" \ + "ld r10, x+ \n\t" \ + "ld r14, y+ \n\t" \ + "mul r2, r14 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r6, r10 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r3, r17 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r7, r13 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r4, r16 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r8, r12 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r5, r15 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r9, r11 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "st z+, r20 \n\t" \ + \ + "ldi r20, 0 \n\t" \ + "ld r0, z \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r25 \n\t" \ + "ld r11, x+ \n\t" \ + "ld r15, y+ \n\t" \ + "mul r2, r15 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r6, r11 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r3, r14 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r7, r10 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r4, r17 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r8, r13 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r5, r16 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r9, r12 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "st z+, r21 \n\t" \ + \ + "ldi r21, 0 \n\t" \ + "ld r0, z \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r25 \n\t" \ + "ld r12, x+ \n\t" \ + "ld r16, y+ \n\t" \ + "mul r2, r16 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r6, r12 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r3, r15 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r7, r11 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r4, r14 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r8, r10 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r5, r17 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r9, r13 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "st z+, r19 \n\t" \ + \ + "ldi r19, 0 \n\t" \ + "ld r0, z \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r25 \n\t" \ + "ld r13, x+ \n\t" \ + "ld r17, y+ \n\t" \ + "mul r2, r17 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r6, r13 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r3, r16 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r7, r12 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r4, r15 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r8, r11 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r5, r14 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r9, r10 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "st z+, r20 \n\t" \ + \ + "ldi r20, 0 \n\t" \ + "ld r0, z \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r25 \n\t" \ + "ld r10, x+ \n\t" \ + "ld r14, y+ \n\t" \ + "mul r2, r14 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r6, r10 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r3, r17 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r7, r13 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r4, r16 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r8, r12 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r5, r15 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r9, r11 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "st z+, r21 \n\t" \ + \ + "ldi r21, 0 \n\t" \ + "ld r0, z \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r25 \n\t" \ + "ld r11, x+ \n\t" \ + "ld r15, y+ \n\t" \ + "mul r2, r15 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r6, r11 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r3, r14 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r7, r10 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r4, r17 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r8, r13 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r5, r16 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r9, r12 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "st z+, r19 \n\t" \ + \ + "ldi r19, 0 \n\t" \ + "ld r0, z \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r25 \n\t" \ + "ld r12, x+ \n\t" \ + "ld r16, y+ \n\t" \ + "mul r2, r16 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r6, r12 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r3, r15 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r7, r11 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r4, r14 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r8, r10 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r5, r17 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r9, r13 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "st z+, r20 \n\t" \ + \ + "ldi r20, 0 \n\t" \ + "ld r0, z \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r25 \n\t" \ + "ld r13, x+ \n\t" \ + "ld r17, y+ \n\t" \ + "mul r2, r17 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r6, r13 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r3, r16 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r7, r12 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r4, r15 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r8, r11 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r5, r14 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r9, r10 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "st z+, r21 \n\t" \ + \ + "ldi r21, 0 \n\t" \ + "mul r11, r9 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r12, r8 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r13, r7 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r2, r6 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r3, r17 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r4, r16 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r5, r15 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "st z+, r19 \n\t" \ + \ + "ldi r19, 0 \n\t" \ + "mul r12, r9 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r13, r8 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r2, r7 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r3, r6 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r4, r17 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r5, r16 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "st z+, r20 \n\t" \ + \ + "ldi r20, 0 \n\t" \ + "mul r13, r9 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r2, r8 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r3, r7 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r4, r6 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r5, r17 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "st z+, r21 \n\t" \ + \ + "ldi r21, 0 \n\t" \ + "mul r2, r9 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r3, r8 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r4, r7 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r5, r6 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "st z+, r19 \n\t" \ + \ + "ldi r19, 0 \n\t" \ + "mul r3, r9 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r4, r8 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r5, r7 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "st z+, r20 \n\t" \ + \ + "ldi r20, 0 \n\t" \ + "mul r4, r9 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r5, r8 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "st z+, r21 \n\t" \ + \ + "mul r5, r9 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "st z+, r19 \n\t" \ + "st z+, r20 \n\t" \ + "adiw r26, 4 \n\t" \ + "adiw r28, 4 \n\t" + +#define FAST_MULT_ASM_28 \ + "adiw r30, 20 \n\t" \ + "adiw r28, 20 \n\t" \ + "ld r2, x+ \n\t" \ + "ld r3, x+ \n\t" \ + "ld r4, x+ \n\t" \ + "ld r5, x+ \n\t" \ + "ld r6, x+ \n\t" \ + "ld r7, x+ \n\t" \ + "ld r8, x+ \n\t" \ + "ld r9, x+ \n\t" \ + "ld r12, y+ \n\t" \ + "ld r13, y+ \n\t" \ + "ld r14, y+ \n\t" \ + "ld r15, y+ \n\t" \ + "ld r16, y+ \n\t" \ + "ld r17, y+ \n\t" \ + "ld r18, y+ \n\t" \ + "ld r19, y+ \n\t" \ + "ldi r25, 0 \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r2, r12 \n\t" \ + "st z+, r0 \n\t" \ + "mov r22, r1 \n\t" \ + \ + "ldi r24, 0 \n\t" \ + "mul r2, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "mul r3, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r2, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r2, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ldi r24, 0 \n\t" \ + "mul r2, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r2, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r2, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ldi r24, 0 \n\t" \ + "mul r2, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r3, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r4, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ldi r24, 0 \n\t" \ + "mul r5, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r6, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r7, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ldi r24, 0 \n\t" \ + "mul r8, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "mul r9, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "st z+, r23 \n\t" \ + "st z+, r24 \n\t" \ + \ + "sbiw r30, 26 \n\t" \ + "sbiw r28, 18 \n\t" \ + "ld r12, y+ \n\t" \ + "ld r13, y+ \n\t" \ + "ld r14, y+ \n\t" \ + "ld r15, y+ \n\t" \ + "ld r16, y+ \n\t" \ + "ld r17, y+ \n\t" \ + "ld r18, y+ \n\t" \ + "ld r19, y+ \n\t" \ + "ld r20, y+ \n\t" \ + "ld r21, y+ \n\t" \ + "ld r10, x+ \n\t" \ + "ld r11, x+ \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r2, r12 \n\t" \ + "st z+, r0 \n\t" \ + "mov r22, r1 \n\t" \ + \ + "ldi r24, 0 \n\t" \ + "mul r2, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "mul r3, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r2, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r2, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ldi r24, 0 \n\t" \ + "mul r2, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r2, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r2, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ldi r24, 0 \n\t" \ + "mul r2, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r2, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r2, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r10, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ld r2, x+ \n\t" \ + "ldi r24, 0 \n\t" \ + "mul r3, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r10, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r2, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r25 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ld r3, x+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r4, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r4, x+ \n\t" \ + "ldi r23, 0 \n\t" \ + "mul r5, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r10, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r2, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r25 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ld r5, x+ \n\t" \ + "ldi r24, 0 \n\t" \ + "mul r6, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r10, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r2, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r25 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ld r6, x+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r7, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r7, x+ \n\t" \ + "ldi r23, 0 \n\t" \ + "mul r8, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r10, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r2, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r25 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ld r8, x+ \n\t" \ + "ldi r24, 0 \n\t" \ + "mul r9, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r10, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r2, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r25 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ld r9, x+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r10, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r12, y+ \n\t" \ + "ldi r23, 0 \n\t" \ + "mul r10, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r2, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r25 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ld r13, y+ \n\t" \ + "ldi r24, 0 \n\t" \ + "mul r10, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r2, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r25 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ld r14, y+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r10, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r15, y+ \n\t" \ + "ldi r23, 0 \n\t" \ + "mul r10, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r2, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r25 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ld r16, y+ \n\t" \ + "ldi r24, 0 \n\t" \ + "mul r10, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r2, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r25 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ld r17, y+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r10, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r18, y+ \n\t" \ + "ldi r23, 0 \n\t" \ + "mul r10, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r2, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r25 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ld r19, y+ \n\t" \ + "ldi r24, 0 \n\t" \ + "mul r10, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r2, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r25 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r11, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r2, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ldi r24, 0 \n\t" \ + "mul r3, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r4, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r5, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ldi r24, 0 \n\t" \ + "mul r6, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r7, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r8, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "mul r9, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "st z+, r22 \n\t" \ + "st z+, r23 \n\t" \ + \ + "sbiw r30, 46 \n\t" \ + "sbiw r28, 28 \n\t" \ + "sbiw r26, 18 \n\t" \ + "ld r2, x+ \n\t" \ + "ld r12, y+ \n\t" \ + "ld r3, x+ \n\t" \ + "ld r13, y+ \n\t" \ + "ld r4, x+ \n\t" \ + "ld r14, y+ \n\t" \ + "ld r5, x+ \n\t" \ + "ld r15, y+ \n\t" \ + "ld r6, x+ \n\t" \ + "ld r16, y+ \n\t" \ + "ld r7, x+ \n\t" \ + "ld r17, y+ \n\t" \ + "ld r8, x+ \n\t" \ + "ld r18, y+ \n\t" \ + "ld r9, x+ \n\t" \ + "ld r19, y+ \n\t" \ + "ld r10, x+ \n\t" \ + "ld r20, y+ \n\t" \ + "ld r11, x+ \n\t" \ + "ld r21, y+ \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r2, r12 \n\t" \ + "st z+, r0 \n\t" \ + "mov r22, r1 \n\t" \ + \ + "ldi r24, 0 \n\t" \ + "mul r2, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "mul r3, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r2, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r2, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ldi r24, 0 \n\t" \ + "mul r2, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r2, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r2, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ldi r24, 0 \n\t" \ + "mul r2, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r2, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r2, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r10, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ld r2, x+ \n\t" \ + "ldi r24, 0 \n\t" \ + "mul r3, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r10, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r2, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r25 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ld r3, x+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r4, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r4, x+ \n\t" \ + "ldi r23, 0 \n\t" \ + "mul r5, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r10, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r2, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r25 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ld r5, x+ \n\t" \ + "ldi r24, 0 \n\t" \ + "mul r6, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r10, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r2, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r25 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ld r6, x+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r7, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r7, x+ \n\t" \ + "ldi r23, 0 \n\t" \ + "mul r8, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r10, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r2, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r25 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ld r8, x+ \n\t" \ + "ldi r24, 0 \n\t" \ + "mul r9, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r10, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r2, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r25 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ld r9, x+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r10, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r10, x+ \n\t" \ + "ldi r23, 0 \n\t" \ + "mul r11, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r2, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r10, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r25 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ld r11, x+ \n\t" \ + "ldi r24, 0 \n\t" \ + "mul r2, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r10, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r25 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ld r2, x+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r3, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r3, x+ \n\t" \ + "ldi r23, 0 \n\t" \ + "mul r4, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r10, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r2, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r25 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ld r4, x+ \n\t" \ + "ldi r24, 0 \n\t" \ + "mul r5, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r10, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r2, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r25 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ld r5, x+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r6, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r6, x+ \n\t" \ + "ldi r23, 0 \n\t" \ + "mul r7, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r10, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r2, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r25 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ld r7, x+ \n\t" \ + "ldi r24, 0 \n\t" \ + "mul r8, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r10, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r2, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r25 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ld r8, x+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r9, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r9, x+ \n\t" \ + "ldi r23, 0 \n\t" \ + "mul r10, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r2, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r25 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ld r12, y+ \n\t" \ + "ldi r24, 0 \n\t" \ + "mul r10, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r2, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r25 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ld r13, y+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r10, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r14, y+ \n\t" \ + "ldi r23, 0 \n\t" \ + "mul r10, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r2, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r25 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ld r15, y+ \n\t" \ + "ldi r24, 0 \n\t" \ + "mul r10, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r2, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r25 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ld r16, y+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r10, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r17, y+ \n\t" \ + "ldi r23, 0 \n\t" \ + "mul r10, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r2, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r25 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ld r18, y+ \n\t" \ + "ldi r24, 0 \n\t" \ + "mul r10, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r2, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r25 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ld r19, y+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r10, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r20, y+ \n\t" \ + "ldi r23, 0 \n\t" \ + "mul r10, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r2, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r25 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ld r21, y+ \n\t" \ + "ldi r24, 0 \n\t" \ + "mul r10, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r2, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r25 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ld r12, y+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r10, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r13, y+ \n\t" \ + "ldi r23, 0 \n\t" \ + "mul r10, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r2, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r25 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ld r14, y+ \n\t" \ + "ldi r24, 0 \n\t" \ + "mul r10, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r2, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r25 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ld r15, y+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r10, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r16, y+ \n\t" \ + "ldi r23, 0 \n\t" \ + "mul r10, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r2, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r25 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ld r17, y+ \n\t" \ + "ldi r24, 0 \n\t" \ + "mul r10, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r2, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r25 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ld r18, y+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r10, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r19, y+ \n\t" \ + "ldi r23, 0 \n\t" \ + "mul r10, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r2, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r25 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ldi r24, 0 \n\t" \ + "mul r11, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r2, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r2, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r3, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ldi r24, 0 \n\t" \ + "mul r4, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r5, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r6, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ldi r24, 0 \n\t" \ + "mul r7, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r8, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "mul r9, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "st z+, r24 \n\t" \ + "st z+, r22 \n\t" + +#define FAST_MULT_ASM_28_TO_32 \ + "cpi r18, 28 \n\t" \ + "brne 1f \n\t" \ + "jmp 2f \n\t" \ + "1: \n\t" \ + "ld r2, x+ \n\t" \ + "ld r6, y+ \n\t" \ + "ld r3, x+ \n\t" \ + "ld r7, y+ \n\t" \ + "ld r4, x+ \n\t" \ + "ld r8, y+ \n\t" \ + "ld r5, x+ \n\t" \ + "ld r9, y+ \n\t" \ + "sbiw r26, 32 \n\t" \ + "sbiw r28, 32 \n\t" \ + "sbiw r30, 28 \n\t" \ + "ld r10, x+ \n\t" \ + "ld r14, y+ \n\t" \ + "ld r11, x+ \n\t" \ + "ld r15, y+ \n\t" \ + "ld r12, x+ \n\t" \ + "ld r16, y+ \n\t" \ + "ld r13, x+ \n\t" \ + "ld r17, y+ \n\t" \ + \ + "mul r2, r14 \n\t" \ + "mov r19, r0 \n\t" \ + "mov r20, r1 \n\t" \ + "ldi r21, 0 \n\t" \ + "ld r0, z \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r6, r10 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "st z+, r19 \n\t" \ + \ + "ldi r19, 0 \n\t" \ + "ld r0, z \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r2, r15 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r6, r11 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r3, r14 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r7, r10 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "st z+, r20 \n\t" \ + \ + "ldi r20, 0 \n\t" \ + "ld r0, z \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r2, r16 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r6, r12 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r3, r15 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r7, r11 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r4, r14 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r8, r10 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "st z+, r21 \n\t" \ + \ + "ldi r21, 0 \n\t" \ + "ld r0, z \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r2, r17 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r6, r13 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r3, r16 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r7, r12 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r4, r15 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r8, r11 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r5, r14 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r9, r10 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "st z+, r19 \n\t" \ + \ + "ldi r19, 0 \n\t" \ + "ld r0, z \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r25 \n\t" \ + "ld r10, x+ \n\t" \ + "ld r14, y+ \n\t" \ + "mul r2, r14 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r6, r10 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r3, r17 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r7, r13 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r4, r16 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r8, r12 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r5, r15 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r9, r11 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "st z+, r20 \n\t" \ + \ + "ldi r20, 0 \n\t" \ + "ld r0, z \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r25 \n\t" \ + "ld r11, x+ \n\t" \ + "ld r15, y+ \n\t" \ + "mul r2, r15 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r6, r11 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r3, r14 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r7, r10 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r4, r17 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r8, r13 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r5, r16 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r9, r12 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "st z+, r21 \n\t" \ + \ + "ldi r21, 0 \n\t" \ + "ld r0, z \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r25 \n\t" \ + "ld r12, x+ \n\t" \ + "ld r16, y+ \n\t" \ + "mul r2, r16 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r6, r12 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r3, r15 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r7, r11 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r4, r14 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r8, r10 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r5, r17 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r9, r13 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "st z+, r19 \n\t" \ + \ + "ldi r19, 0 \n\t" \ + "ld r0, z \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r25 \n\t" \ + "ld r13, x+ \n\t" \ + "ld r17, y+ \n\t" \ + "mul r2, r17 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r6, r13 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r3, r16 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r7, r12 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r4, r15 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r8, r11 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r5, r14 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r9, r10 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "st z+, r20 \n\t" \ + \ + "ldi r20, 0 \n\t" \ + "ld r0, z \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r25 \n\t" \ + "ld r10, x+ \n\t" \ + "ld r14, y+ \n\t" \ + "mul r2, r14 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r6, r10 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r3, r17 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r7, r13 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r4, r16 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r8, r12 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r5, r15 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r9, r11 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "st z+, r21 \n\t" \ + \ + "ldi r21, 0 \n\t" \ + "ld r0, z \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r25 \n\t" \ + "ld r11, x+ \n\t" \ + "ld r15, y+ \n\t" \ + "mul r2, r15 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r6, r11 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r3, r14 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r7, r10 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r4, r17 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r8, r13 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r5, r16 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r9, r12 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "st z+, r19 \n\t" \ + \ + "ldi r19, 0 \n\t" \ + "ld r0, z \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r25 \n\t" \ + "ld r12, x+ \n\t" \ + "ld r16, y+ \n\t" \ + "mul r2, r16 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r6, r12 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r3, r15 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r7, r11 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r4, r14 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r8, r10 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r5, r17 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r9, r13 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "st z+, r20 \n\t" \ + \ + "ldi r20, 0 \n\t" \ + "ld r0, z \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r25 \n\t" \ + "ld r13, x+ \n\t" \ + "ld r17, y+ \n\t" \ + "mul r2, r17 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r6, r13 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r3, r16 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r7, r12 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r4, r15 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r8, r11 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r5, r14 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r9, r10 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "st z+, r21 \n\t" \ + \ + "ldi r21, 0 \n\t" \ + "ld r0, z \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r25 \n\t" \ + "ld r10, x+ \n\t" \ + "ld r14, y+ \n\t" \ + "mul r2, r14 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r6, r10 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r3, r17 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r7, r13 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r4, r16 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r8, r12 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r5, r15 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r9, r11 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "st z+, r19 \n\t" \ + \ + "ldi r19, 0 \n\t" \ + "ld r0, z \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r25 \n\t" \ + "ld r11, x+ \n\t" \ + "ld r15, y+ \n\t" \ + "mul r2, r15 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r6, r11 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r3, r14 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r7, r10 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r4, r17 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r8, r13 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r5, r16 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r9, r12 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "st z+, r20 \n\t" \ + \ + "ldi r20, 0 \n\t" \ + "ld r0, z \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r25 \n\t" \ + "ld r12, x+ \n\t" \ + "ld r16, y+ \n\t" \ + "mul r2, r16 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r6, r12 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r3, r15 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r7, r11 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r4, r14 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r8, r10 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r5, r17 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r9, r13 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "st z+, r21 \n\t" \ + \ + "ldi r21, 0 \n\t" \ + "ld r0, z \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r25 \n\t" \ + "ld r13, x+ \n\t" \ + "ld r17, y+ \n\t" \ + "mul r2, r17 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r6, r13 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r3, r16 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r7, r12 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r4, r15 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r8, r11 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r5, r14 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r9, r10 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "st z+, r19 \n\t" \ + \ + "ldi r19, 0 \n\t" \ + "ld r0, z \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r25 \n\t" \ + "ld r10, x+ \n\t" \ + "ld r14, y+ \n\t" \ + "mul r2, r14 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r6, r10 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r3, r17 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r7, r13 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r4, r16 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r8, r12 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r5, r15 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r9, r11 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "st z+, r20 \n\t" \ + \ + "ldi r20, 0 \n\t" \ + "ld r0, z \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r25 \n\t" \ + "ld r11, x+ \n\t" \ + "ld r15, y+ \n\t" \ + "mul r2, r15 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r6, r11 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r3, r14 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r7, r10 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r4, r17 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r8, r13 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r5, r16 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r9, r12 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "st z+, r21 \n\t" \ + \ + "ldi r21, 0 \n\t" \ + "ld r0, z \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r25 \n\t" \ + "ld r12, x+ \n\t" \ + "ld r16, y+ \n\t" \ + "mul r2, r16 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r6, r12 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r3, r15 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r7, r11 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r4, r14 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r8, r10 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r5, r17 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r9, r13 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "st z+, r19 \n\t" \ + \ + "ldi r19, 0 \n\t" \ + "ld r0, z \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r25 \n\t" \ + "ld r13, x+ \n\t" \ + "ld r17, y+ \n\t" \ + "mul r2, r17 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r6, r13 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r3, r16 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r7, r12 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r4, r15 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r8, r11 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r5, r14 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r9, r10 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "st z+, r20 \n\t" \ + \ + "ldi r20, 0 \n\t" \ + "ld r0, z \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r25 \n\t" \ + "ld r10, x+ \n\t" \ + "ld r14, y+ \n\t" \ + "mul r2, r14 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r6, r10 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r3, r17 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r7, r13 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r4, r16 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r8, r12 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r5, r15 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r9, r11 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "st z+, r21 \n\t" \ + \ + "ldi r21, 0 \n\t" \ + "ld r0, z \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r25 \n\t" \ + "ld r11, x+ \n\t" \ + "ld r15, y+ \n\t" \ + "mul r2, r15 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r6, r11 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r3, r14 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r7, r10 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r4, r17 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r8, r13 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r5, r16 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r9, r12 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "st z+, r19 \n\t" \ + \ + "ldi r19, 0 \n\t" \ + "ld r0, z \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r25 \n\t" \ + "ld r12, x+ \n\t" \ + "ld r16, y+ \n\t" \ + "mul r2, r16 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r6, r12 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r3, r15 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r7, r11 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r4, r14 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r8, r10 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r5, r17 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r9, r13 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "st z+, r20 \n\t" \ + \ + "ldi r20, 0 \n\t" \ + "ld r0, z \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r25 \n\t" \ + "ld r13, x+ \n\t" \ + "ld r17, y+ \n\t" \ + "mul r2, r17 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r6, r13 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r3, r16 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r7, r12 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r4, r15 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r8, r11 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r5, r14 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r9, r10 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "st z+, r21 \n\t" \ + \ + "ldi r21, 0 \n\t" \ + "ld r0, z \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r25 \n\t" \ + "ld r10, x+ \n\t" \ + "ld r14, y+ \n\t" \ + "mul r2, r14 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r6, r10 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r3, r17 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r7, r13 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r4, r16 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r8, r12 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r5, r15 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r9, r11 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "st z+, r19 \n\t" \ + \ + "ldi r19, 0 \n\t" \ + "ld r0, z \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r25 \n\t" \ + "ld r11, x+ \n\t" \ + "ld r15, y+ \n\t" \ + "mul r2, r15 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r6, r11 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r3, r14 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r7, r10 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r4, r17 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r8, r13 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r5, r16 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r9, r12 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "st z+, r20 \n\t" \ + \ + "ldi r20, 0 \n\t" \ + "ld r0, z \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r25 \n\t" \ + "ld r12, x+ \n\t" \ + "ld r16, y+ \n\t" \ + "mul r2, r16 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r6, r12 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r3, r15 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r7, r11 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r4, r14 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r8, r10 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r5, r17 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r9, r13 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "st z+, r21 \n\t" \ + \ + "ldi r21, 0 \n\t" \ + "ld r0, z \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r25 \n\t" \ + "ld r13, x+ \n\t" \ + "ld r17, y+ \n\t" \ + "mul r2, r17 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r6, r13 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r3, r16 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r7, r12 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r4, r15 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r8, r11 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r5, r14 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r9, r10 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "st z+, r19 \n\t" \ + \ + "ldi r19, 0 \n\t" \ + "mul r11, r9 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r12, r8 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r13, r7 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r2, r6 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r3, r17 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r4, r16 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r5, r15 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "st z+, r20 \n\t" \ + \ + "ldi r20, 0 \n\t" \ + "mul r12, r9 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r13, r8 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r2, r7 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r3, r6 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r4, r17 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r5, r16 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "st z+, r21 \n\t" \ + \ + "ldi r21, 0 \n\t" \ + "mul r13, r9 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r2, r8 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r3, r7 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r4, r6 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r5, r17 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "st z+, r19 \n\t" \ + \ + "ldi r19, 0 \n\t" \ + "mul r2, r9 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r3, r8 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r4, r7 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r5, r6 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "st z+, r20 \n\t" \ + \ + "ldi r20, 0 \n\t" \ + "mul r3, r9 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r4, r8 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "mul r5, r7 \n\t" \ + "add r21, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r20, r25 \n\t" \ + "st z+, r21 \n\t" \ + \ + "ldi r21, 0 \n\t" \ + "mul r4, r9 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r5, r8 \n\t" \ + "add r19, r0 \n\t" \ + "adc r20, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "st z+, r19 \n\t" \ + \ + "mul r5, r9 \n\t" \ + "add r20, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "st z+, r20 \n\t" \ + "st z+, r21 \n\t" + /* Not necessary to move ptrs since we don't support sizes > 32 */ + +#define FAST_MULT_ASM_32 \ + "adiw r30, 30 \n\t" \ + "adiw r28, 30 \n\t" \ + "ld r2, x+ \n\t" \ + "ld r3, x+ \n\t" \ + "ld r12, y+ \n\t" \ + "ld r13, y+ \n\t" \ + "ldi r25, 0 \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r2, r12 \n\t" \ + "st z+, r0 \n\t" \ + "mov r22, r1 \n\t" \ + \ + "ldi r24, 0 \n\t" \ + "mul r2, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "mul r3, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "mul r3, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "st z+, r23 \n\t" \ + "st z+, r24 \n\t" \ + \ + "sbiw r30, 14 \n\t" \ + "sbiw r28, 12 \n\t" \ + "ld r12, y+ \n\t" \ + "ld r13, y+ \n\t" \ + "ld r14, y+ \n\t" \ + "ld r15, y+ \n\t" \ + "ld r16, y+ \n\t" \ + "ld r17, y+ \n\t" \ + "ld r18, y+ \n\t" \ + "ld r19, y+ \n\t" \ + "ld r20, y+ \n\t" \ + "ld r21, y+ \n\t" \ + "ld r4, x+ \n\t" \ + "ld r5, x+ \n\t" \ + "ld r6, x+ \n\t" \ + "ld r7, x+ \n\t" \ + "ld r8, x+ \n\t" \ + "ld r9, x+ \n\t" \ + "ld r10, x+ \n\t" \ + "ld r11, x+ \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r2, r12 \n\t" \ + "st z+, r0 \n\t" \ + "mov r22, r1 \n\t" \ + \ + "ldi r24, 0 \n\t" \ + "mul r2, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "mul r3, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r2, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r2, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ldi r24, 0 \n\t" \ + "mul r2, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r2, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r2, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ldi r24, 0 \n\t" \ + "mul r2, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r2, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r2, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r10, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ld r2, x+ \n\t" \ + "ldi r24, 0 \n\t" \ + "mul r3, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r10, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r2, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r25 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ld r3, x+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r4, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r12, y+ \n\t" \ + "ldi r23, 0 \n\t" \ + "mul r4, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r10, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r2, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r25 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ld r13, y+ \n\t" \ + "ldi r24, 0 \n\t" \ + "mul r4, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r10, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r2, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r25 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r5, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r6, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r10, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r2, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ldi r24, 0 \n\t" \ + "mul r7, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r10, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r2, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r8, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r9, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r10, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r2, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ldi r24, 0 \n\t" \ + "mul r10, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r2, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r11, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r2, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "mul r3, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "st z+, r22 \n\t" \ + "st z+, r23 \n\t" \ + \ + "sbiw r30, 34 \n\t" \ + "sbiw r28, 22 \n\t" \ + "sbiw r26, 12 \n\t" \ + "ld r2, x+ \n\t" \ + "ld r12, y+ \n\t" \ + "ld r3, x+ \n\t" \ + "ld r13, y+ \n\t" \ + "ld r4, x+ \n\t" \ + "ld r14, y+ \n\t" \ + "ld r5, x+ \n\t" \ + "ld r15, y+ \n\t" \ + "ld r6, x+ \n\t" \ + "ld r16, y+ \n\t" \ + "ld r7, x+ \n\t" \ + "ld r17, y+ \n\t" \ + "ld r8, x+ \n\t" \ + "ld r18, y+ \n\t" \ + "ld r9, x+ \n\t" \ + "ld r19, y+ \n\t" \ + "ld r10, x+ \n\t" \ + "ld r20, y+ \n\t" \ + "ld r11, x+ \n\t" \ + "ld r21, y+ \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r2, r12 \n\t" \ + "st z+, r0 \n\t" \ + "mov r22, r1 \n\t" \ + \ + "ldi r24, 0 \n\t" \ + "mul r2, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "mul r3, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r2, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r2, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ldi r24, 0 \n\t" \ + "mul r2, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r2, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r2, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ldi r24, 0 \n\t" \ + "mul r2, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r2, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r2, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r10, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ld r2, x+ \n\t" \ + "ldi r24, 0 \n\t" \ + "mul r3, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r10, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r2, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r25 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ld r3, x+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r4, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r4, x+ \n\t" \ + "ldi r23, 0 \n\t" \ + "mul r5, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r10, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r2, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r25 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ld r5, x+ \n\t" \ + "ldi r24, 0 \n\t" \ + "mul r6, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r10, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r2, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r25 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ld r6, x+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r7, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r7, x+ \n\t" \ + "ldi r23, 0 \n\t" \ + "mul r8, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r10, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r2, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r25 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ld r8, x+ \n\t" \ + "ldi r24, 0 \n\t" \ + "mul r9, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r10, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r2, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r25 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ld r9, x+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r10, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r10, x+ \n\t" \ + "ldi r23, 0 \n\t" \ + "mul r11, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r2, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r10, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r25 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ld r11, x+ \n\t" \ + "ldi r24, 0 \n\t" \ + "mul r2, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r10, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r25 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ld r2, x+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r3, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r3, x+ \n\t" \ + "ldi r23, 0 \n\t" \ + "mul r4, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r10, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r2, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r25 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ld r12, y+ \n\t" \ + "ldi r24, 0 \n\t" \ + "mul r4, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r10, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r2, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r25 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ld r13, y+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r4, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r14, y+ \n\t" \ + "ldi r23, 0 \n\t" \ + "mul r4, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r10, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r2, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r25 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ld r15, y+ \n\t" \ + "ldi r24, 0 \n\t" \ + "mul r4, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r10, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r2, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r25 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ld r16, y+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r4, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r17, y+ \n\t" \ + "ldi r23, 0 \n\t" \ + "mul r4, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r10, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r2, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r25 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ld r18, y+ \n\t" \ + "ldi r24, 0 \n\t" \ + "mul r4, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r10, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r2, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r25 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ld r19, y+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r4, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r20, y+ \n\t" \ + "ldi r23, 0 \n\t" \ + "mul r4, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r10, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r2, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r25 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ld r21, y+ \n\t" \ + "ldi r24, 0 \n\t" \ + "mul r4, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r10, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r2, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r25 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ld r12, y+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r4, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r13, y+ \n\t" \ + "ldi r23, 0 \n\t" \ + "mul r4, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r10, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r2, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r25 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ldi r24, 0 \n\t" \ + "mul r5, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r10, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r2, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r6, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r7, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r10, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r2, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ldi r24, 0 \n\t" \ + "mul r8, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r10, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r2, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r9, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r10, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r2, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ldi r24, 0 \n\t" \ + "mul r11, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r2, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r2, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "mul r3, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "st z+, r24 \n\t" \ + "st z+, r22 \n\t" \ + \ + "sbiw r30, 54 \n\t" \ + "sbiw r28, 32 \n\t" \ + "sbiw r26, 22 \n\t" \ + "ld r2, x+ \n\t" \ + "ld r12, y+ \n\t" \ + "ld r3, x+ \n\t" \ + "ld r13, y+ \n\t" \ + "ld r4, x+ \n\t" \ + "ld r14, y+ \n\t" \ + "ld r5, x+ \n\t" \ + "ld r15, y+ \n\t" \ + "ld r6, x+ \n\t" \ + "ld r16, y+ \n\t" \ + "ld r7, x+ \n\t" \ + "ld r17, y+ \n\t" \ + "ld r8, x+ \n\t" \ + "ld r18, y+ \n\t" \ + "ld r9, x+ \n\t" \ + "ld r19, y+ \n\t" \ + "ld r10, x+ \n\t" \ + "ld r20, y+ \n\t" \ + "ld r11, x+ \n\t" \ + "ld r21, y+ \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r2, r12 \n\t" \ + "st z+, r0 \n\t" \ + "mov r22, r1 \n\t" \ + \ + "ldi r24, 0 \n\t" \ + "mul r2, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "mul r3, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r2, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r2, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ldi r24, 0 \n\t" \ + "mul r2, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r2, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r2, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ldi r24, 0 \n\t" \ + "mul r2, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r2, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r2, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r10, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ld r2, x+ \n\t" \ + "ldi r24, 0 \n\t" \ + "mul r3, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r10, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r2, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r25 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ld r3, x+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r4, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r4, x+ \n\t" \ + "ldi r23, 0 \n\t" \ + "mul r5, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r10, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r2, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r25 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ld r5, x+ \n\t" \ + "ldi r24, 0 \n\t" \ + "mul r6, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r10, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r2, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r25 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ld r6, x+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r7, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r7, x+ \n\t" \ + "ldi r23, 0 \n\t" \ + "mul r8, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r10, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r2, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r25 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ld r8, x+ \n\t" \ + "ldi r24, 0 \n\t" \ + "mul r9, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r10, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r2, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r25 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ld r9, x+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r10, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r10, x+ \n\t" \ + "ldi r23, 0 \n\t" \ + "mul r11, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r2, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r10, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r25 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ld r11, x+ \n\t" \ + "ldi r24, 0 \n\t" \ + "mul r2, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r10, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r25 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ld r2, x+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r3, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r3, x+ \n\t" \ + "ldi r23, 0 \n\t" \ + "mul r4, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r10, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r2, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r25 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ld r4, x+ \n\t" \ + "ldi r24, 0 \n\t" \ + "mul r5, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r10, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r2, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r25 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ld r5, x+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r6, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r6, x+ \n\t" \ + "ldi r23, 0 \n\t" \ + "mul r7, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r10, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r2, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r25 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ld r7, x+ \n\t" \ + "ldi r24, 0 \n\t" \ + "mul r8, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r10, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r2, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r25 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ld r8, x+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r9, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r9, x+ \n\t" \ + "ldi r23, 0 \n\t" \ + "mul r10, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r2, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r25 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ld r10, x+ \n\t" \ + "ldi r24, 0 \n\t" \ + "mul r11, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r2, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r10, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r25 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ld r11, x+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r2, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r2, x+ \n\t" \ + "ldi r23, 0 \n\t" \ + "mul r3, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r10, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r2, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r25 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ld r3, x+ \n\t" \ + "ldi r24, 0 \n\t" \ + "mul r4, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r10, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r2, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r25 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ld r12, y+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r4, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r13, y+ \n\t" \ + "ldi r23, 0 \n\t" \ + "mul r4, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r10, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r2, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r25 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ld r14, y+ \n\t" \ + "ldi r24, 0 \n\t" \ + "mul r4, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r10, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r2, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r25 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ld r15, y+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r4, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r16, y+ \n\t" \ + "ldi r23, 0 \n\t" \ + "mul r4, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r10, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r2, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r25 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ld r17, y+ \n\t" \ + "ldi r24, 0 \n\t" \ + "mul r4, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r10, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r2, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r25 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ld r18, y+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r4, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r19, y+ \n\t" \ + "ldi r23, 0 \n\t" \ + "mul r4, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r10, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r2, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r25 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ld r20, y+ \n\t" \ + "ldi r24, 0 \n\t" \ + "mul r4, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r10, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r2, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r25 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ld r21, y+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r4, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r12, y+ \n\t" \ + "ldi r23, 0 \n\t" \ + "mul r4, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r10, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r2, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r25 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ld r13, y+ \n\t" \ + "ldi r24, 0 \n\t" \ + "mul r4, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r10, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r2, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r25 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ld r14, y+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r4, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r15, y+ \n\t" \ + "ldi r23, 0 \n\t" \ + "mul r4, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r10, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r2, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r25 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ld r16, y+ \n\t" \ + "ldi r24, 0 \n\t" \ + "mul r4, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r10, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r2, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r25 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ld r17, y+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r4, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r18, y+ \n\t" \ + "ldi r23, 0 \n\t" \ + "mul r4, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r10, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r2, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r25 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ld r19, y+ \n\t" \ + "ldi r24, 0 \n\t" \ + "mul r4, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r10, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r2, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r25 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ld r20, y+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r4, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r21, y+ \n\t" \ + "ldi r23, 0 \n\t" \ + "mul r4, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r10, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r2, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r25 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ld r12, y+ \n\t" \ + "ldi r24, 0 \n\t" \ + "mul r4, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r10, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r2, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r25 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ld r13, y+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r4, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r5, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r10, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r2, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ldi r24, 0 \n\t" \ + "mul r6, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r8, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r9, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r10, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r18 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r2, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r7, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r8, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r9, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r10, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r11, r20 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r2, r19 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r18 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ldi r24, 0 \n\t" \ + "mul r9, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r10, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r11, r21 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r2, r20 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r19 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r10, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r11, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r2, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r21 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ldi r24, 0 \n\t" \ + "mul r2, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "mul r3, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "st z+, r23 \n\t" \ + "st z+, r24 \n\t" + +#define FAST_SQUARE_ASM_20 \ + "ld r2, x+ \n\t" \ + "ld r3, x+ \n\t" \ + "ld r4, x+ \n\t" \ + "ld r5, x+ \n\t" \ + "ld r6, x+ \n\t" \ + "ld r7, x+ \n\t" \ + "ld r8, x+ \n\t" \ + "ld r9, x+ \n\t" \ + "ld r10, x+ \n\t" \ + "ld r11, x+ \n\t" \ + "ld r12, x+ \n\t" \ + "ld r13, x+ \n\t" \ + "ld r14, x+ \n\t" \ + "ld r15, x+ \n\t" \ + "ld r16, x+ \n\t" \ + "ld r17, x+ \n\t" \ + "ld r18, x+ \n\t" \ + "ld r19, x+ \n\t" \ + "ld r20, x+ \n\t" \ + "ld r21, x+ \n\t" \ + "push r26 \n\t" \ + "push r27 \n\t" \ + "ldi r25, 0 \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r2, r2 \n\t" \ + "st z+, r0 \n\t" \ + "mov r22, r1 \n\t" \ + \ + "ldi r24, 0 \n\t" \ + "mul r2, r3 \n\t" \ + "lsl r0 \n\t" \ + "rol r1 \n\t" \ + "adc r24, r25 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r2, r4 \n\t" \ + "lsl r0 \n\t" \ + "rol r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r3 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r26, 0 \n\t" \ + "mul r2, r5 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r27, r1 \n\t" \ + "mul r3, r4 \n\t" \ + "add r23, r0 \n\t" \ + "adc r27, r1 \n\t" \ + "adc r26, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r27 \n\t" \ + "rol r26 \n\t" \ + "add r23, r24 \n\t" \ + "adc r27, r22 \n\t" \ + "adc r26, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r2, r6 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r3, r5 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r4, r4 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r27 \n\t" \ + "adc r24, r26 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r26, 0 \n\t" \ + "mul r2, r7 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r27, r1 \n\t" \ + "mul r3, r6 \n\t" \ + "add r23, r0 \n\t" \ + "adc r27, r1 \n\t" \ + "adc r26, r25 \n\t" \ + "mul r4, r5 \n\t" \ + "add r23, r0 \n\t" \ + "adc r27, r1 \n\t" \ + "adc r26, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r27 \n\t" \ + "rol r26 \n\t" \ + "add r23, r24 \n\t" \ + "adc r27, r22 \n\t" \ + "adc r26, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r2, r8 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r3, r7 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r6 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r5, r5 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r27 \n\t" \ + "adc r24, r26 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r26, 0 \n\t" \ + "mul r2, r9 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r27, r1 \n\t" \ + "mul r3, r8 \n\t" \ + "add r23, r0 \n\t" \ + "adc r27, r1 \n\t" \ + "adc r26, r25 \n\t" \ + "mul r4, r7 \n\t" \ + "add r23, r0 \n\t" \ + "adc r27, r1 \n\t" \ + "adc r26, r25 \n\t" \ + "mul r5, r6 \n\t" \ + "add r23, r0 \n\t" \ + "adc r27, r1 \n\t" \ + "adc r26, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r27 \n\t" \ + "rol r26 \n\t" \ + "add r23, r24 \n\t" \ + "adc r27, r22 \n\t" \ + "adc r26, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r2, r10 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r3, r9 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r8 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r7 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r6, r6 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r27 \n\t" \ + "adc r24, r26 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r26, 0 \n\t" \ + "mul r2, r11 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r27, r1 \n\t" \ + "mul r3, r10 \n\t" \ + "add r23, r0 \n\t" \ + "adc r27, r1 \n\t" \ + "adc r26, r25 \n\t" \ + "mul r4, r9 \n\t" \ + "add r23, r0 \n\t" \ + "adc r27, r1 \n\t" \ + "adc r26, r25 \n\t" \ + "mul r5, r8 \n\t" \ + "add r23, r0 \n\t" \ + "adc r27, r1 \n\t" \ + "adc r26, r25 \n\t" \ + "mul r6, r7 \n\t" \ + "add r23, r0 \n\t" \ + "adc r27, r1 \n\t" \ + "adc r26, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r27 \n\t" \ + "rol r26 \n\t" \ + "add r23, r24 \n\t" \ + "adc r27, r22 \n\t" \ + "adc r26, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r2, r12 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r3, r11 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r10 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r9 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r8 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r7, r7 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r27 \n\t" \ + "adc r24, r26 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r26, 0 \n\t" \ + "mul r2, r13 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r27, r1 \n\t" \ + "mul r3, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r27, r1 \n\t" \ + "adc r26, r25 \n\t" \ + "mul r4, r11 \n\t" \ + "add r23, r0 \n\t" \ + "adc r27, r1 \n\t" \ + "adc r26, r25 \n\t" \ + "mul r5, r10 \n\t" \ + "add r23, r0 \n\t" \ + "adc r27, r1 \n\t" \ + "adc r26, r25 \n\t" \ + "mul r6, r9 \n\t" \ + "add r23, r0 \n\t" \ + "adc r27, r1 \n\t" \ + "adc r26, r25 \n\t" \ + "mul r7, r8 \n\t" \ + "add r23, r0 \n\t" \ + "adc r27, r1 \n\t" \ + "adc r26, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r27 \n\t" \ + "rol r26 \n\t" \ + "add r23, r24 \n\t" \ + "adc r27, r22 \n\t" \ + "adc r26, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r2, r14 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r3, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r11 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r10 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r9 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r8, r8 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r27 \n\t" \ + "adc r24, r26 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r26, 0 \n\t" \ + "mul r2, r15 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r27, r1 \n\t" \ + "mul r3, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r27, r1 \n\t" \ + "adc r26, r25 \n\t" \ + "mul r4, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r27, r1 \n\t" \ + "adc r26, r25 \n\t" \ + "mul r5, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r27, r1 \n\t" \ + "adc r26, r25 \n\t" \ + "mul r6, r11 \n\t" \ + "add r23, r0 \n\t" \ + "adc r27, r1 \n\t" \ + "adc r26, r25 \n\t" \ + "mul r7, r10 \n\t" \ + "add r23, r0 \n\t" \ + "adc r27, r1 \n\t" \ + "adc r26, r25 \n\t" \ + "mul r8, r9 \n\t" \ + "add r23, r0 \n\t" \ + "adc r27, r1 \n\t" \ + "adc r26, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r27 \n\t" \ + "rol r26 \n\t" \ + "add r23, r24 \n\t" \ + "adc r27, r22 \n\t" \ + "adc r26, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r2, r16 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r3, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r11 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r10 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r9, r9 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r27 \n\t" \ + "adc r24, r26 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r26, 0 \n\t" \ + "mul r2, r17 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r27, r1 \n\t" \ + "mul r3, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r27, r1 \n\t" \ + "adc r26, r25 \n\t" \ + "mul r4, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r27, r1 \n\t" \ + "adc r26, r25 \n\t" \ + "mul r5, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r27, r1 \n\t" \ + "adc r26, r25 \n\t" \ + "mul r6, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r27, r1 \n\t" \ + "adc r26, r25 \n\t" \ + "mul r7, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r27, r1 \n\t" \ + "adc r26, r25 \n\t" \ + "mul r8, r11 \n\t" \ + "add r23, r0 \n\t" \ + "adc r27, r1 \n\t" \ + "adc r26, r25 \n\t" \ + "mul r9, r10 \n\t" \ + "add r23, r0 \n\t" \ + "adc r27, r1 \n\t" \ + "adc r26, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r27 \n\t" \ + "rol r26 \n\t" \ + "add r23, r24 \n\t" \ + "adc r27, r22 \n\t" \ + "adc r26, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r2, r18 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r3, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r11 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r10, r10 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r27 \n\t" \ + "adc r24, r26 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r26, 0 \n\t" \ + "mul r2, r19 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r27, r1 \n\t" \ + "mul r3, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r27, r1 \n\t" \ + "adc r26, r25 \n\t" \ + "mul r4, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r27, r1 \n\t" \ + "adc r26, r25 \n\t" \ + "mul r5, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r27, r1 \n\t" \ + "adc r26, r25 \n\t" \ + "mul r6, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r27, r1 \n\t" \ + "adc r26, r25 \n\t" \ + "mul r7, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r27, r1 \n\t" \ + "adc r26, r25 \n\t" \ + "mul r8, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r27, r1 \n\t" \ + "adc r26, r25 \n\t" \ + "mul r9, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r27, r1 \n\t" \ + "adc r26, r25 \n\t" \ + "mul r10, r11 \n\t" \ + "add r23, r0 \n\t" \ + "adc r27, r1 \n\t" \ + "adc r26, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r27 \n\t" \ + "rol r26 \n\t" \ + "add r23, r24 \n\t" \ + "adc r27, r22 \n\t" \ + "adc r26, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r2, r20 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r3, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r11, r11 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r27 \n\t" \ + "adc r24, r26 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r26, 0 \n\t" \ + "mul r2, r21 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r27, r1 \n\t" \ + "mul r3, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r27, r1 \n\t" \ + "adc r26, r25 \n\t" \ + "mul r4, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r27, r1 \n\t" \ + "adc r26, r25 \n\t" \ + "mul r5, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r27, r1 \n\t" \ + "adc r26, r25 \n\t" \ + "mul r6, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r27, r1 \n\t" \ + "adc r26, r25 \n\t" \ + "mul r7, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r27, r1 \n\t" \ + "adc r26, r25 \n\t" \ + "mul r8, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r27, r1 \n\t" \ + "adc r26, r25 \n\t" \ + "mul r9, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r27, r1 \n\t" \ + "adc r26, r25 \n\t" \ + "mul r10, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r27, r1 \n\t" \ + "adc r26, r25 \n\t" \ + "mul r11, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r27, r1 \n\t" \ + "adc r26, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r27 \n\t" \ + "rol r26 \n\t" \ + "add r23, r24 \n\t" \ + "adc r27, r22 \n\t" \ + "adc r26, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r3, r21 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r4, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r12, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r27 \n\t" \ + "adc r24, r26 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r26, 0 \n\t" \ + "mul r4, r21 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r27, r1 \n\t" \ + "mul r5, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r27, r1 \n\t" \ + "adc r26, r25 \n\t" \ + "mul r6, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r27, r1 \n\t" \ + "adc r26, r25 \n\t" \ + "mul r7, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r27, r1 \n\t" \ + "adc r26, r25 \n\t" \ + "mul r8, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r27, r1 \n\t" \ + "adc r26, r25 \n\t" \ + "mul r9, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r27, r1 \n\t" \ + "adc r26, r25 \n\t" \ + "mul r10, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r27, r1 \n\t" \ + "adc r26, r25 \n\t" \ + "mul r11, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r27, r1 \n\t" \ + "adc r26, r25 \n\t" \ + "mul r12, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r27, r1 \n\t" \ + "adc r26, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r27 \n\t" \ + "rol r26 \n\t" \ + "add r23, r24 \n\t" \ + "adc r27, r22 \n\t" \ + "adc r26, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r5, r21 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r6, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r12, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r13, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r27 \n\t" \ + "adc r24, r26 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r26, 0 \n\t" \ + "mul r6, r21 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r27, r1 \n\t" \ + "mul r7, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r27, r1 \n\t" \ + "adc r26, r25 \n\t" \ + "mul r8, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r27, r1 \n\t" \ + "adc r26, r25 \n\t" \ + "mul r9, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r27, r1 \n\t" \ + "adc r26, r25 \n\t" \ + "mul r10, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r27, r1 \n\t" \ + "adc r26, r25 \n\t" \ + "mul r11, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r27, r1 \n\t" \ + "adc r26, r25 \n\t" \ + "mul r12, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r27, r1 \n\t" \ + "adc r26, r25 \n\t" \ + "mul r13, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r27, r1 \n\t" \ + "adc r26, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r27 \n\t" \ + "rol r26 \n\t" \ + "add r23, r24 \n\t" \ + "adc r27, r22 \n\t" \ + "adc r26, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r7, r21 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r8, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r12, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r13, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r14, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r27 \n\t" \ + "adc r24, r26 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r26, 0 \n\t" \ + "mul r8, r21 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r27, r1 \n\t" \ + "mul r9, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r27, r1 \n\t" \ + "adc r26, r25 \n\t" \ + "mul r10, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r27, r1 \n\t" \ + "adc r26, r25 \n\t" \ + "mul r11, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r27, r1 \n\t" \ + "adc r26, r25 \n\t" \ + "mul r12, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r27, r1 \n\t" \ + "adc r26, r25 \n\t" \ + "mul r13, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r27, r1 \n\t" \ + "adc r26, r25 \n\t" \ + "mul r14, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r27, r1 \n\t" \ + "adc r26, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r27 \n\t" \ + "rol r26 \n\t" \ + "add r23, r24 \n\t" \ + "adc r27, r22 \n\t" \ + "adc r26, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r9, r21 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r10, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r12, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r13, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r14, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r15, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r27 \n\t" \ + "adc r24, r26 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r26, 0 \n\t" \ + "mul r10, r21 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r27, r1 \n\t" \ + "mul r11, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r27, r1 \n\t" \ + "adc r26, r25 \n\t" \ + "mul r12, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r27, r1 \n\t" \ + "adc r26, r25 \n\t" \ + "mul r13, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r27, r1 \n\t" \ + "adc r26, r25 \n\t" \ + "mul r14, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r27, r1 \n\t" \ + "adc r26, r25 \n\t" \ + "mul r15, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r27, r1 \n\t" \ + "adc r26, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r27 \n\t" \ + "rol r26 \n\t" \ + "add r23, r24 \n\t" \ + "adc r27, r22 \n\t" \ + "adc r26, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r11, r21 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r12, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r13, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r14, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r15, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r16, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r27 \n\t" \ + "adc r24, r26 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r26, 0 \n\t" \ + "mul r12, r21 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r27, r1 \n\t" \ + "mul r13, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r27, r1 \n\t" \ + "adc r26, r25 \n\t" \ + "mul r14, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r27, r1 \n\t" \ + "adc r26, r25 \n\t" \ + "mul r15, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r27, r1 \n\t" \ + "adc r26, r25 \n\t" \ + "mul r16, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r27, r1 \n\t" \ + "adc r26, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r27 \n\t" \ + "rol r26 \n\t" \ + "add r23, r24 \n\t" \ + "adc r27, r22 \n\t" \ + "adc r26, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r13, r21 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r14, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r15, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r16, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r17, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r27 \n\t" \ + "adc r24, r26 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r26, 0 \n\t" \ + "mul r14, r21 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r27, r1 \n\t" \ + "mul r15, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r27, r1 \n\t" \ + "adc r26, r25 \n\t" \ + "mul r16, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r27, r1 \n\t" \ + "adc r26, r25 \n\t" \ + "mul r17, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r27, r1 \n\t" \ + "adc r26, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r27 \n\t" \ + "rol r26 \n\t" \ + "add r23, r24 \n\t" \ + "adc r27, r22 \n\t" \ + "adc r26, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r15, r21 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r16, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r17, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r18, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r27 \n\t" \ + "adc r24, r26 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r26, 0 \n\t" \ + "mul r16, r21 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r27, r1 \n\t" \ + "mul r17, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r27, r1 \n\t" \ + "adc r26, r25 \n\t" \ + "mul r18, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r27, r1 \n\t" \ + "adc r26, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r27 \n\t" \ + "rol r26 \n\t" \ + "add r23, r24 \n\t" \ + "adc r27, r22 \n\t" \ + "adc r26, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r17, r21 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r18, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r19, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r27 \n\t" \ + "adc r24, r26 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r26, 0 \n\t" \ + "mul r18, r21 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r27, r1 \n\t" \ + "mul r19, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r27, r1 \n\t" \ + "adc r26, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r27 \n\t" \ + "rol r26 \n\t" \ + "add r23, r24 \n\t" \ + "adc r27, r22 \n\t" \ + "adc r26, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r19, r21 \n\t" \ + "lsl r0 \n\t" \ + "rol r1 \n\t" \ + "adc r23, r25 \n\t" \ + "add r27, r0 \n\t" \ + "adc r26, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r20, r20 \n\t" \ + "add r27, r0 \n\t" \ + "adc r26, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r27 \n\t" \ + \ + "ldi r27, 0 \n\t" \ + "mul r20, r21 \n\t" \ + "lsl r0 \n\t" \ + "rol r1 \n\t" \ + "adc r27, r25 \n\t" \ + "add r26, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r27, r25 \n\t" \ + "st z+, r26 \n\t" \ + \ + "mul r21, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r27, r1 \n\t" \ + "st z+, r23 \n\t" \ + "st z+, r27 \n\t" \ + "pop r27 \n\t" \ + "pop r26 \n\t" + +#define FAST_SQUARE_ASM_20_TO_24 \ + "cpi r20, 20 \n\t" \ + "brne 1f \n\t" \ + "jmp 2f \n\t" \ + "1: \n\t" \ + "ld r2, x+ \n\t" \ + "ld r3, x+ \n\t" \ + "ld r4, x+ \n\t" \ + "ld r5, x+ \n\t" \ + "sbiw r26, 24 \n\t" \ + "sbiw r30, 20 \n\t" \ + "ld r6, x+ \n\t" \ + "ld r7, x+ \n\t" \ + "ld r8, x+ \n\t" \ + "ld r9, x+ \n\t" \ + \ + "mul r2, r6 \n\t" \ + "mov r10, r0 \n\t" \ + "mov r11, r1 \n\t" \ + "mov r12, r25 \n\t" \ + "mov r13, r25 \n\t" \ + "mul r2, r7 \n\t" \ + "add r11, r0 \n\t" \ + "adc r12, r1 \n\t" \ + "adc r13, r25 \n\t" \ + "mul r3, r6 \n\t" \ + "add r11, r0 \n\t" \ + "adc r12, r1 \n\t" \ + "adc r13, r25 \n\t" \ + \ + "mov r14, r25 \n\t" \ + "mul r2, r8 \n\t" \ + "add r12, r0 \n\t" \ + "adc r13, r1 \n\t" \ + "adc r14, r25 \n\t" \ + "mul r3, r7 \n\t" \ + "add r12, r0 \n\t" \ + "adc r13, r1 \n\t" \ + "adc r14, r25 \n\t" \ + "mul r4, r6 \n\t" \ + "add r12, r0 \n\t" \ + "adc r13, r1 \n\t" \ + "adc r14, r25 \n\t" \ + \ + "mov r15, r25 \n\t" \ + "mul r2, r9 \n\t" \ + "add r13, r0 \n\t" \ + "adc r14, r1 \n\t" \ + "adc r15, r25 \n\t" \ + "mul r3, r8 \n\t" \ + "add r13, r0 \n\t" \ + "adc r14, r1 \n\t" \ + "adc r15, r25 \n\t" \ + "mul r4, r7 \n\t" \ + "add r13, r0 \n\t" \ + "adc r14, r1 \n\t" \ + "adc r15, r25 \n\t" \ + "mul r5, r6 \n\t" \ + "add r13, r0 \n\t" \ + "adc r14, r1 \n\t" \ + "adc r15, r25 \n\t" \ + \ + "ld r6, x+ \n\t" \ + "mov r16, r25 \n\t" \ + "mul r2, r6 \n\t" \ + "add r14, r0 \n\t" \ + "adc r15, r1 \n\t" \ + "adc r16, r25 \n\t" \ + "mul r3, r9 \n\t" \ + "add r14, r0 \n\t" \ + "adc r15, r1 \n\t" \ + "adc r16, r25 \n\t" \ + "mul r4, r8 \n\t" \ + "add r14, r0 \n\t" \ + "adc r15, r1 \n\t" \ + "adc r16, r25 \n\t" \ + "mul r5, r7 \n\t" \ + "add r14, r0 \n\t" \ + "adc r15, r1 \n\t" \ + "adc r16, r25 \n\t" \ + \ + "ld r7, x+ \n\t" \ + "mov r17, r25 \n\t" \ + "mul r2, r7 \n\t" \ + "add r15, r0 \n\t" \ + "adc r16, r1 \n\t" \ + "adc r17, r25 \n\t" \ + "mul r3, r6 \n\t" \ + "add r15, r0 \n\t" \ + "adc r16, r1 \n\t" \ + "adc r17, r25 \n\t" \ + "mul r4, r9 \n\t" \ + "add r15, r0 \n\t" \ + "adc r16, r1 \n\t" \ + "adc r17, r25 \n\t" \ + "mul r5, r8 \n\t" \ + "add r15, r0 \n\t" \ + "adc r16, r1 \n\t" \ + "adc r17, r25 \n\t" \ + \ + "ld r8, x+ \n\t" \ + "mov r18, r25 \n\t" \ + "mul r2, r8 \n\t" \ + "add r16, r0 \n\t" \ + "adc r17, r1 \n\t" \ + "adc r18, r25 \n\t" \ + "mul r3, r7 \n\t" \ + "add r16, r0 \n\t" \ + "adc r17, r1 \n\t" \ + "adc r18, r25 \n\t" \ + "mul r4, r6 \n\t" \ + "add r16, r0 \n\t" \ + "adc r17, r1 \n\t" \ + "adc r18, r25 \n\t" \ + "mul r5, r9 \n\t" \ + "add r16, r0 \n\t" \ + "adc r17, r1 \n\t" \ + "adc r18, r25 \n\t" \ + \ + "ld r9, x+ \n\t" \ + "mov r19, r25 \n\t" \ + "mul r2, r9 \n\t" \ + "add r17, r0 \n\t" \ + "adc r18, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r3, r8 \n\t" \ + "add r17, r0 \n\t" \ + "adc r18, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r4, r7 \n\t" \ + "add r17, r0 \n\t" \ + "adc r18, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r5, r6 \n\t" \ + "add r17, r0 \n\t" \ + "adc r18, r1 \n\t" \ + "adc r19, r25 \n\t" \ + \ + "ld r6, x+ \n\t" \ + "mov r21, r25 \n\t" \ + "mul r2, r6 \n\t" \ + "add r18, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r3, r9 \n\t" \ + "add r18, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r4, r8 \n\t" \ + "add r18, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r5, r7 \n\t" \ + "add r18, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r21, r25 \n\t" \ + \ + "ld r7, x+ \n\t" \ + "mov r22, r25 \n\t" \ + "mul r2, r7 \n\t" \ + "add r19, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r6 \n\t" \ + "add r19, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r9 \n\t" \ + "add r19, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r8 \n\t" \ + "add r19, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r22, r25 \n\t" \ + \ + "ld r8, x+ \n\t" \ + "mov r23, r25 \n\t" \ + "mul r2, r8 \n\t" \ + "add r21, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r7 \n\t" \ + "add r21, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r6 \n\t" \ + "add r21, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r9 \n\t" \ + "add r21, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + \ + "ld r9, x+ \n\t" \ + "mov r24, r25 \n\t" \ + "mul r2, r9 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r8 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r7 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r6 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + \ + "ld r6, x+ \n\t" \ + "mov r28, r25 \n\t" \ + "mul r2, r6 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r28, r25 \n\t" \ + "mul r3, r9 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r28, r25 \n\t" \ + "mul r4, r8 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r28, r25 \n\t" \ + "mul r5, r7 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r28, r25 \n\t" \ + \ + "ld r7, x+ \n\t" \ + "mov r29, r25 \n\t" \ + "mul r2, r7 \n\t" \ + "add r24, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r3, r6 \n\t" \ + "add r24, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r4, r9 \n\t" \ + "add r24, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r5, r8 \n\t" \ + "add r24, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + \ + "lsl r10 \n\t" \ + "rol r11 \n\t" \ + "rol r12 \n\t" \ + "rol r13 \n\t" \ + "rol r14 \n\t" \ + "rol r15 \n\t" \ + "rol r16 \n\t" \ + "rol r17 \n\t" \ + "rol r18 \n\t" \ + "rol r19 \n\t" \ + "rol r21 \n\t" \ + "rol r22 \n\t" \ + "rol r23 \n\t" \ + "rol r24 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "ld r0, z \n\t" \ + "add r10, r0 \n\t" \ + "st z+, r10 \n\t" \ + "ld r0, z \n\t" \ + "adc r11, r0 \n\t" \ + "st z+, r11 \n\t" \ + "ld r0, z \n\t" \ + "adc r12, r0 \n\t" \ + "st z+, r12 \n\t" \ + "ld r0, z \n\t" \ + "adc r13, r0 \n\t" \ + "st z+, r13 \n\t" \ + "ld r0, z \n\t" \ + "adc r14, r0 \n\t" \ + "st z+, r14 \n\t" \ + "ld r0, z \n\t" \ + "adc r15, r0 \n\t" \ + "st z+, r15 \n\t" \ + "ld r0, z \n\t" \ + "adc r16, r0 \n\t" \ + "st z+, r16 \n\t" \ + "ld r0, z \n\t" \ + "adc r17, r0 \n\t" \ + "st z+, r17 \n\t" \ + "ld r0, z \n\t" \ + "adc r18, r0 \n\t" \ + "st z+, r18 \n\t" \ + "ld r0, z \n\t" \ + "adc r19, r0 \n\t" \ + "st z+, r19 \n\t" \ + "ld r0, z \n\t" \ + "adc r21, r0 \n\t" \ + "st z+, r21 \n\t" \ + "ld r0, z \n\t" \ + "adc r22, r0 \n\t" \ + "st z+, r22 \n\t" \ + "ld r0, z \n\t" \ + "adc r23, r0 \n\t" \ + "st z+, r23 \n\t" \ + "ld r0, z \n\t" \ + "adc r24, r0 \n\t" \ + "st z+, r24 \n\t" \ + "adc r28, r25 \n\t" \ + "adc r29, r25 \n\t" \ + "bst r28, 0 \n\t" \ + "lsr r29 \n\t" \ + "ror r28 \n\t" \ + \ + "ld r8, x+ \n\t" \ + "mov r10, r25 \n\t" \ + "mul r2, r8 \n\t" \ + "add r28, r0 \n\t" \ + "adc r29, r1 \n\t" \ + "adc r10, r25 \n\t" \ + "mul r3, r7 \n\t" \ + "add r28, r0 \n\t" \ + "adc r29, r1 \n\t" \ + "adc r10, r25 \n\t" \ + "mul r4, r6 \n\t" \ + "add r28, r0 \n\t" \ + "adc r29, r1 \n\t" \ + "adc r10, r25 \n\t" \ + "mul r5, r9 \n\t" \ + "add r28, r0 \n\t" \ + "adc r29, r1 \n\t" \ + "adc r10, r25 \n\t" \ + \ + "ld r9, x+ \n\t" \ + "mov r11, r25 \n\t" \ + "mul r2, r9 \n\t" \ + "add r29, r0 \n\t" \ + "adc r10, r1 \n\t" \ + "adc r11, r25 \n\t" \ + "mul r3, r8 \n\t" \ + "add r29, r0 \n\t" \ + "adc r10, r1 \n\t" \ + "adc r11, r25 \n\t" \ + "mul r4, r7 \n\t" \ + "add r29, r0 \n\t" \ + "adc r10, r1 \n\t" \ + "adc r11, r25 \n\t" \ + "mul r5, r6 \n\t" \ + "add r29, r0 \n\t" \ + "adc r10, r1 \n\t" \ + "adc r11, r25 \n\t" \ + \ + "ld r6, x+ \n\t" \ + "mov r12, r25 \n\t" \ + "mul r2, r6 \n\t" \ + "add r10, r0 \n\t" \ + "adc r11, r1 \n\t" \ + "adc r12, r25 \n\t" \ + "mul r3, r9 \n\t" \ + "add r10, r0 \n\t" \ + "adc r11, r1 \n\t" \ + "adc r12, r25 \n\t" \ + "mul r4, r8 \n\t" \ + "add r10, r0 \n\t" \ + "adc r11, r1 \n\t" \ + "adc r12, r25 \n\t" \ + "mul r5, r7 \n\t" \ + "add r10, r0 \n\t" \ + "adc r11, r1 \n\t" \ + "adc r12, r25 \n\t" \ + \ + "ld r7, x+ \n\t" \ + "mov r13, r25 \n\t" \ + "mul r2, r7 \n\t" \ + "add r11, r0 \n\t" \ + "adc r12, r1 \n\t" \ + "adc r13, r25 \n\t" \ + "mul r3, r6 \n\t" \ + "add r11, r0 \n\t" \ + "adc r12, r1 \n\t" \ + "adc r13, r25 \n\t" \ + "mul r4, r9 \n\t" \ + "add r11, r0 \n\t" \ + "adc r12, r1 \n\t" \ + "adc r13, r25 \n\t" \ + "mul r5, r8 \n\t" \ + "add r11, r0 \n\t" \ + "adc r12, r1 \n\t" \ + "adc r13, r25 \n\t" \ + \ + "ld r8, x+ \n\t" \ + "mov r14, r25 \n\t" \ + "mul r2, r8 \n\t" \ + "add r12, r0 \n\t" \ + "adc r13, r1 \n\t" \ + "adc r14, r25 \n\t" \ + "mul r3, r7 \n\t" \ + "add r12, r0 \n\t" \ + "adc r13, r1 \n\t" \ + "adc r14, r25 \n\t" \ + "mul r4, r6 \n\t" \ + "add r12, r0 \n\t" \ + "adc r13, r1 \n\t" \ + "adc r14, r25 \n\t" \ + "mul r5, r9 \n\t" \ + "add r12, r0 \n\t" \ + "adc r13, r1 \n\t" \ + "adc r14, r25 \n\t" \ + \ + "ld r9, x+ \n\t" \ + "mov r15, r25 \n\t" \ + "mul r2, r9 \n\t" \ + "add r13, r0 \n\t" \ + "adc r14, r1 \n\t" \ + "adc r15, r25 \n\t" \ + "mul r3, r8 \n\t" \ + "add r13, r0 \n\t" \ + "adc r14, r1 \n\t" \ + "adc r15, r25 \n\t" \ + "mul r4, r7 \n\t" \ + "add r13, r0 \n\t" \ + "adc r14, r1 \n\t" \ + "adc r15, r25 \n\t" \ + "mul r5, r6 \n\t" \ + "add r13, r0 \n\t" \ + "adc r14, r1 \n\t" \ + "adc r15, r25 \n\t" \ + \ + "lsl r28 \n\t" \ + "bld r28, 0 \n\t" \ + "rol r29 \n\t" \ + "rol r10 \n\t" \ + "rol r11 \n\t" \ + "rol r12 \n\t" \ + "rol r13 \n\t" \ + "rol r14 \n\t" \ + "rol r15 \n\t" \ + "ld r0, z \n\t" \ + "add r28, r0 \n\t" \ + "st z+, r28 \n\t" \ + "ld r0, z \n\t" \ + "adc r29, r0 \n\t" \ + "st z+, r29 \n\t" \ + "ld r0, z \n\t" \ + "adc r10, r0 \n\t" \ + "st z+, r10 \n\t" \ + "ld r0, z \n\t" \ + "adc r11, r0 \n\t" \ + "st z+, r11 \n\t" \ + "ld r0, z \n\t" \ + "adc r12, r0 \n\t" \ + "st z+, r12 \n\t" \ + "ld r0, z \n\t" \ + "adc r13, r0 \n\t" \ + "st z+, r13 \n\t" \ + "adc r14, r25 \n\t" \ + "adc r15, r25 \n\t" \ + \ + "mul r2, r2 \n\t" \ + "mov r16, r0 \n\t" \ + "mov r17, r1 \n\t" \ + "mul r3, r3 \n\t" \ + "mov r18, r0 \n\t" \ + "mov r19, r1 \n\t" \ + "mul r4, r4 \n\t" \ + "mov r21, r0 \n\t" \ + "mov r22, r1 \n\t" \ + "mul r5, r5 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "add r16, r14 \n\t" \ + "adc r17, r15 \n\t" \ + "adc r18, r25 \n\t" \ + "adc r19, r25 \n\t" \ + \ + "mul r7, r5 \n\t" \ + "mov r14, r0 \n\t" \ + "mov r15, r1 \n\t" \ + "mov r28, r25 \n\t" \ + "mul r8, r4 \n\t" \ + "add r14, r0 \n\t" \ + "adc r15, r1 \n\t" \ + "adc r28, r25 \n\t" \ + "mul r9, r3 \n\t" \ + "add r14, r0 \n\t" \ + "adc r15, r1 \n\t" \ + "adc r28, r25 \n\t" \ + "mov r29, r25 \n\t" \ + "mul r8, r5 \n\t" \ + "add r15, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r9, r4 \n\t" \ + "add r15, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r2, r3 \n\t" \ + "add r15, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mov r10, r25 \n\t" \ + "mul r9, r5 \n\t" \ + "add r28, r0 \n\t" \ + "adc r29, r1 \n\t" \ + "adc r10, r25 \n\t" \ + "mul r2, r4 \n\t" \ + "add r28, r0 \n\t" \ + "adc r29, r1 \n\t" \ + "adc r10, r25 \n\t" \ + "mov r11, r25 \n\t" \ + "mul r2, r5 \n\t" \ + "add r29, r0 \n\t" \ + "adc r10, r1 \n\t" \ + "adc r11, r25 \n\t" \ + "mul r3, r4 \n\t" \ + "add r29, r0 \n\t" \ + "adc r10, r1 \n\t" \ + "adc r11, r25 \n\t" \ + "mov r12, r25 \n\t" \ + "mul r3, r5 \n\t" \ + "add r10, r0 \n\t" \ + "adc r11, r1 \n\t" \ + "adc r12, r25 \n\t" \ + "mul r4, r5 \n\t" \ + "add r11, r0 \n\t" \ + "adc r12, r1 \n\t" \ + \ + "lsl r14 \n\t" \ + "rol r15 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "rol r10 \n\t" \ + "rol r11 \n\t" \ + "rol r12 \n\t" \ + "adc r24, r25 \n\t" \ + "add r16, r14 \n\t" \ + "adc r17, r15 \n\t" \ + "adc r18, r28 \n\t" \ + "adc r19, r29 \n\t" \ + "adc r21, r10 \n\t" \ + "adc r22, r11 \n\t" \ + "adc r23, r12 \n\t" \ + "adc r24, r25 \n\t" \ + \ + "st z+, r16 \n\t" \ + "st z+, r17 \n\t" \ + "st z+, r18 \n\t" \ + "st z+, r19 \n\t" \ + "st z+, r21 \n\t" \ + "st z+, r22 \n\t" \ + "st z+, r23 \n\t" \ + "st z+, r24 \n\t" \ + "adiw r26, 4 \n\t" + +#define FAST_SQUARE_ASM_24 \ + "ldi r25, 0 \n\t" \ + "movw r28, r26 \n\t" \ + "ld r2, x+ \n\t" \ + "ld r3, x+ \n\t" \ + "adiw r28, 20 \n\t" \ + "ld r12, y+ \n\t" \ + "ld r13, y+ \n\t" \ + "adiw r30, 20 \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul 2, 12 \n\t" \ + "st z+, r0 \n\t" \ + "mov r22, r1 \n\t" \ + \ + "ldi r24, 0 \n\t" \ + "mul r2, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ld r12, y+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r2, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r13, y+ \n\t" \ + "ldi r23, 0 \n\t" \ + "mul r2, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ld r2, x+ \n\t" \ + "ldi r24, 0 \n\t" \ + "mul r3, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r2, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ld r3, x+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r2, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "mul r3, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "st z+, r24 \n\t" \ + "st z+, r22 \n\t" \ + \ + "sbiw r26, 4 \n\t" \ + "sbiw r30, 28 \n\t" \ + "ld r2, x+ \n\t" \ + "ld r3, x+ \n\t" \ + "ld r4, x+ \n\t" \ + "ld r5, x+ \n\t" \ + "ld r6, x+ \n\t" \ + "ld r7, x+ \n\t" \ + "ld r8, x+ \n\t" \ + "ld r9, x+ \n\t" \ + "ld r10, x+ \n\t" \ + "ld r11, x+ \n\t" \ + "ld r12, x+ \n\t" \ + "ld r13, x+ \n\t" \ + "ld r14, x+ \n\t" \ + "ld r15, x+ \n\t" \ + "ld r16, x+ \n\t" \ + "ld r17, x+ \n\t" \ + "ld r18, x+ \n\t" \ + "ld r19, x+ \n\t" \ + "ld r20, x+ \n\t" \ + "ld r21, x+ \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r2, r2 \n\t" \ + "st z+, r0 \n\t" \ + "mov r22, r1 \n\t" \ + \ + "ldi r24, 0 \n\t" \ + "mul r2, r3 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r2, r4 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r3 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r2, r5 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r3, r4 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r2, r6 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r3, r5 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r4, r4 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r28 \n\t" \ + "adc r24, r29 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r2, r7 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r3, r6 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r4, r5 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r2, r8 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r3, r7 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r6 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r5, r5 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r28 \n\t" \ + "adc r24, r29 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r2, r9 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r3, r8 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r4, r7 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r5, r6 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r2, r10 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r3, r9 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r8 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r7 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r6, r6 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r28 \n\t" \ + "adc r24, r29 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r2, r11 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r3, r10 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r4, r9 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r5, r8 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r6, r7 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r2, r12 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r3, r11 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r10 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r9 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r8 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r7, r7 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r28 \n\t" \ + "adc r24, r29 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r2, r13 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r3, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r4, r11 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r5, r10 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r6, r9 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r7, r8 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r2, r14 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r3, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r11 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r10 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r9 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r8, r8 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r28 \n\t" \ + "adc r24, r29 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r2, r15 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r3, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r4, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r5, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r6, r11 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r7, r10 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r8, r9 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r2, r16 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r3, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r11 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r10 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r9, r9 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r28 \n\t" \ + "adc r24, r29 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r2, r17 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r3, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r4, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r5, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r6, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r7, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r8, r11 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r9, r10 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r2, r18 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r3, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r11 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r10, r10 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r28 \n\t" \ + "adc r24, r29 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r2, r19 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r3, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r4, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r5, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r6, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r7, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r8, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r9, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r10, r11 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r2, r20 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r3, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r11, r11 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r28 \n\t" \ + "adc r24, r29 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r2, r21 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r3, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r4, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r5, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r6, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r7, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r8, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r9, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r10, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r11, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r2, x+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r3, r21 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r4, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r12, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r28 \n\t" \ + "adc r24, r29 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r3, r2 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r4, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r5, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r6, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r7, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r8, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r9, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r10, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r11, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r12, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r25 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r3, x+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r4, r2 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r5, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r12, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r13, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r28 \n\t" \ + "adc r24, r29 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r4, r3 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r5, r2 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r6, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r7, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r8, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r9, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r10, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r11, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r12, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r13, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r25 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r4, x+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r5, r3 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r6, r2 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r12, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r13, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r14, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r28 \n\t" \ + "adc r24, r29 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r5, r4 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r6, r3 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r7, r2 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r8, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r9, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r10, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r11, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r12, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r13, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r14, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r25 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r5, x+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r6, r4 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r7, r3 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r2 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r12, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r13, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r14, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r15, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r28 \n\t" \ + "adc r24, r29 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r6, r5 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r7, r4 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r8, r3 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r9, r2 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r10, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r11, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r12, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r13, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r14, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r15, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r25 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r7, r5 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r8, r4 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r3 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r2 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r12, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r13, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r14, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r15, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r16, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r28 \n\t" \ + "adc r24, r29 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r8, r5 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r9, r4 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r10, r3 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r11, r2 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r12, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r13, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r14, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r15, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r16, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r9, r5 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r10, r4 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r3 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r12, r2 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r13, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r14, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r15, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r16, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r17, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r28 \n\t" \ + "adc r24, r29 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r10, r5 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r11, r4 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r12, r3 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r13, r2 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r14, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r15, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r16, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r17, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r11, r5 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r12, r4 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r13, r3 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r14, r2 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r15, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r16, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r17, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r18, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r28 \n\t" \ + "adc r24, r29 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r12, r5 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r13, r4 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r14, r3 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r15, r2 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r16, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r17, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r18, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r13, r5 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r14, r4 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r15, r3 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r16, r2 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r17, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r18, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r19, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r28 \n\t" \ + "adc r24, r29 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r14, r5 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r15, r4 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r16, r3 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r17, r2 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r18, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r19, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r15, r5 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r16, r4 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r17, r3 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r18, r2 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r19, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r20, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r28 \n\t" \ + "adc r24, r29 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r16, r5 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r17, r4 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r18, r3 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r19, r2 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r20, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r17, r5 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r18, r4 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r19, r3 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r20, r2 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r21, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r28 \n\t" \ + "adc r24, r29 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r18, r5 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r19, r4 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r20, r3 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r21, r2 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r19, r5 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r20, r4 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r21, r3 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r2, r2 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r28 \n\t" \ + "adc r24, r29 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r20, r5 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r21, r4 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r2, r3 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r21, r5 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r2, r4 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r3, r3 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r28 \n\t" \ + "adc r24, r29 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r2, r5 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r3, r4 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r3, r5 \n\t" \ + "add r28, r0 \n\t" \ + "adc r29, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "add r28, r0 \n\t" \ + "adc r29, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r4 \n\t" \ + "add r28, r0 \n\t" \ + "adc r29, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r28 \n\t" \ + \ + "ldi r28, 0 \n\t" \ + "mul r4, r5 \n\t" \ + "add r29, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r28, r25 \n\t" \ + "add r29, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r28, r25 \n\t" \ + "st z+, r29 \n\t" \ + \ + "mul r5, r5 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "st z+, r23 \n\t" \ + "st z+, r28 \n\t" + +#define FAST_SQUARE_ASM_24_TO_28 \ + "cpi r20, 24 \n\t" \ + "brne 1f \n\t" \ + "jmp 2f \n\t" \ + "1: \n\t" \ + "ld r2, x+ \n\t" \ + "ld r3, x+ \n\t" \ + "ld r4, x+ \n\t" \ + "ld r5, x+ \n\t" \ + "sbiw r26, 28 \n\t" \ + "sbiw r30, 24 \n\t" \ + "ld r6, x+ \n\t" \ + "ld r7, x+ \n\t" \ + "ld r8, x+ \n\t" \ + "ld r9, x+ \n\t" \ + \ + "mul r2, r6 \n\t" \ + "mov r10, r0 \n\t" \ + "mov r11, r1 \n\t" \ + "mov r12, r25 \n\t" \ + "mov r13, r25 \n\t" \ + "mul r2, r7 \n\t" \ + "add r11, r0 \n\t" \ + "adc r12, r1 \n\t" \ + "adc r13, r25 \n\t" \ + "mul r3, r6 \n\t" \ + "add r11, r0 \n\t" \ + "adc r12, r1 \n\t" \ + "adc r13, r25 \n\t" \ + \ + "mov r14, r25 \n\t" \ + "mul r2, r8 \n\t" \ + "add r12, r0 \n\t" \ + "adc r13, r1 \n\t" \ + "adc r14, r25 \n\t" \ + "mul r3, r7 \n\t" \ + "add r12, r0 \n\t" \ + "adc r13, r1 \n\t" \ + "adc r14, r25 \n\t" \ + "mul r4, r6 \n\t" \ + "add r12, r0 \n\t" \ + "adc r13, r1 \n\t" \ + "adc r14, r25 \n\t" \ + \ + "mov r15, r25 \n\t" \ + "mul r2, r9 \n\t" \ + "add r13, r0 \n\t" \ + "adc r14, r1 \n\t" \ + "adc r15, r25 \n\t" \ + "mul r3, r8 \n\t" \ + "add r13, r0 \n\t" \ + "adc r14, r1 \n\t" \ + "adc r15, r25 \n\t" \ + "mul r4, r7 \n\t" \ + "add r13, r0 \n\t" \ + "adc r14, r1 \n\t" \ + "adc r15, r25 \n\t" \ + "mul r5, r6 \n\t" \ + "add r13, r0 \n\t" \ + "adc r14, r1 \n\t" \ + "adc r15, r25 \n\t" \ + \ + "ld r6, x+ \n\t" \ + "mov r16, r25 \n\t" \ + "mul r2, r6 \n\t" \ + "add r14, r0 \n\t" \ + "adc r15, r1 \n\t" \ + "adc r16, r25 \n\t" \ + "mul r3, r9 \n\t" \ + "add r14, r0 \n\t" \ + "adc r15, r1 \n\t" \ + "adc r16, r25 \n\t" \ + "mul r4, r8 \n\t" \ + "add r14, r0 \n\t" \ + "adc r15, r1 \n\t" \ + "adc r16, r25 \n\t" \ + "mul r5, r7 \n\t" \ + "add r14, r0 \n\t" \ + "adc r15, r1 \n\t" \ + "adc r16, r25 \n\t" \ + \ + "ld r7, x+ \n\t" \ + "mov r17, r25 \n\t" \ + "mul r2, r7 \n\t" \ + "add r15, r0 \n\t" \ + "adc r16, r1 \n\t" \ + "adc r17, r25 \n\t" \ + "mul r3, r6 \n\t" \ + "add r15, r0 \n\t" \ + "adc r16, r1 \n\t" \ + "adc r17, r25 \n\t" \ + "mul r4, r9 \n\t" \ + "add r15, r0 \n\t" \ + "adc r16, r1 \n\t" \ + "adc r17, r25 \n\t" \ + "mul r5, r8 \n\t" \ + "add r15, r0 \n\t" \ + "adc r16, r1 \n\t" \ + "adc r17, r25 \n\t" \ + \ + "ld r8, x+ \n\t" \ + "mov r18, r25 \n\t" \ + "mul r2, r8 \n\t" \ + "add r16, r0 \n\t" \ + "adc r17, r1 \n\t" \ + "adc r18, r25 \n\t" \ + "mul r3, r7 \n\t" \ + "add r16, r0 \n\t" \ + "adc r17, r1 \n\t" \ + "adc r18, r25 \n\t" \ + "mul r4, r6 \n\t" \ + "add r16, r0 \n\t" \ + "adc r17, r1 \n\t" \ + "adc r18, r25 \n\t" \ + "mul r5, r9 \n\t" \ + "add r16, r0 \n\t" \ + "adc r17, r1 \n\t" \ + "adc r18, r25 \n\t" \ + \ + "ld r9, x+ \n\t" \ + "mov r19, r25 \n\t" \ + "mul r2, r9 \n\t" \ + "add r17, r0 \n\t" \ + "adc r18, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r3, r8 \n\t" \ + "add r17, r0 \n\t" \ + "adc r18, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r4, r7 \n\t" \ + "add r17, r0 \n\t" \ + "adc r18, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r5, r6 \n\t" \ + "add r17, r0 \n\t" \ + "adc r18, r1 \n\t" \ + "adc r19, r25 \n\t" \ + \ + "ld r6, x+ \n\t" \ + "mov r21, r25 \n\t" \ + "mul r2, r6 \n\t" \ + "add r18, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r3, r9 \n\t" \ + "add r18, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r4, r8 \n\t" \ + "add r18, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r5, r7 \n\t" \ + "add r18, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r21, r25 \n\t" \ + \ + "ld r7, x+ \n\t" \ + "mov r22, r25 \n\t" \ + "mul r2, r7 \n\t" \ + "add r19, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r6 \n\t" \ + "add r19, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r9 \n\t" \ + "add r19, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r8 \n\t" \ + "add r19, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r22, r25 \n\t" \ + \ + "ld r8, x+ \n\t" \ + "mov r23, r25 \n\t" \ + "mul r2, r8 \n\t" \ + "add r21, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r7 \n\t" \ + "add r21, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r6 \n\t" \ + "add r21, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r9 \n\t" \ + "add r21, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + \ + "ld r9, x+ \n\t" \ + "mov r24, r25 \n\t" \ + "mul r2, r9 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r8 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r7 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r6 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + \ + "ld r6, x+ \n\t" \ + "mov r28, r25 \n\t" \ + "mul r2, r6 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r28, r25 \n\t" \ + "mul r3, r9 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r28, r25 \n\t" \ + "mul r4, r8 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r28, r25 \n\t" \ + "mul r5, r7 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r28, r25 \n\t" \ + \ + "ld r7, x+ \n\t" \ + "mov r29, r25 \n\t" \ + "mul r2, r7 \n\t" \ + "add r24, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r3, r6 \n\t" \ + "add r24, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r4, r9 \n\t" \ + "add r24, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r5, r8 \n\t" \ + "add r24, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + \ + "lsl r10 \n\t" \ + "rol r11 \n\t" \ + "rol r12 \n\t" \ + "rol r13 \n\t" \ + "rol r14 \n\t" \ + "rol r15 \n\t" \ + "rol r16 \n\t" \ + "rol r17 \n\t" \ + "rol r18 \n\t" \ + "rol r19 \n\t" \ + "rol r21 \n\t" \ + "rol r22 \n\t" \ + "rol r23 \n\t" \ + "rol r24 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "ld r0, z \n\t" \ + "add r10, r0 \n\t" \ + "st z+, r10 \n\t" \ + "ld r0, z \n\t" \ + "adc r11, r0 \n\t" \ + "st z+, r11 \n\t" \ + "ld r0, z \n\t" \ + "adc r12, r0 \n\t" \ + "st z+, r12 \n\t" \ + "ld r0, z \n\t" \ + "adc r13, r0 \n\t" \ + "st z+, r13 \n\t" \ + "ld r0, z \n\t" \ + "adc r14, r0 \n\t" \ + "st z+, r14 \n\t" \ + "ld r0, z \n\t" \ + "adc r15, r0 \n\t" \ + "st z+, r15 \n\t" \ + "ld r0, z \n\t" \ + "adc r16, r0 \n\t" \ + "st z+, r16 \n\t" \ + "ld r0, z \n\t" \ + "adc r17, r0 \n\t" \ + "st z+, r17 \n\t" \ + "ld r0, z \n\t" \ + "adc r18, r0 \n\t" \ + "st z+, r18 \n\t" \ + "ld r0, z \n\t" \ + "adc r19, r0 \n\t" \ + "st z+, r19 \n\t" \ + "ld r0, z \n\t" \ + "adc r21, r0 \n\t" \ + "st z+, r21 \n\t" \ + "ld r0, z \n\t" \ + "adc r22, r0 \n\t" \ + "st z+, r22 \n\t" \ + "ld r0, z \n\t" \ + "adc r23, r0 \n\t" \ + "st z+, r23 \n\t" \ + "ld r0, z \n\t" \ + "adc r24, r0 \n\t" \ + "st z+, r24 \n\t" \ + "adc r28, r25 \n\t" \ + "adc r29, r25 \n\t" \ + "bst r28, 0 \n\t" \ + "lsr r29 \n\t" \ + "ror r28 \n\t" \ + \ + "ld r8, x+ \n\t" \ + "mov r10, r25 \n\t" \ + "mul r2, r8 \n\t" \ + "add r28, r0 \n\t" \ + "adc r29, r1 \n\t" \ + "adc r10, r25 \n\t" \ + "mul r3, r7 \n\t" \ + "add r28, r0 \n\t" \ + "adc r29, r1 \n\t" \ + "adc r10, r25 \n\t" \ + "mul r4, r6 \n\t" \ + "add r28, r0 \n\t" \ + "adc r29, r1 \n\t" \ + "adc r10, r25 \n\t" \ + "mul r5, r9 \n\t" \ + "add r28, r0 \n\t" \ + "adc r29, r1 \n\t" \ + "adc r10, r25 \n\t" \ + \ + "ld r9, x+ \n\t" \ + "mov r11, r25 \n\t" \ + "mul r2, r9 \n\t" \ + "add r29, r0 \n\t" \ + "adc r10, r1 \n\t" \ + "adc r11, r25 \n\t" \ + "mul r3, r8 \n\t" \ + "add r29, r0 \n\t" \ + "adc r10, r1 \n\t" \ + "adc r11, r25 \n\t" \ + "mul r4, r7 \n\t" \ + "add r29, r0 \n\t" \ + "adc r10, r1 \n\t" \ + "adc r11, r25 \n\t" \ + "mul r5, r6 \n\t" \ + "add r29, r0 \n\t" \ + "adc r10, r1 \n\t" \ + "adc r11, r25 \n\t" \ + \ + "ld r6, x+ \n\t" \ + "mov r12, r25 \n\t" \ + "mul r2, r6 \n\t" \ + "add r10, r0 \n\t" \ + "adc r11, r1 \n\t" \ + "adc r12, r25 \n\t" \ + "mul r3, r9 \n\t" \ + "add r10, r0 \n\t" \ + "adc r11, r1 \n\t" \ + "adc r12, r25 \n\t" \ + "mul r4, r8 \n\t" \ + "add r10, r0 \n\t" \ + "adc r11, r1 \n\t" \ + "adc r12, r25 \n\t" \ + "mul r5, r7 \n\t" \ + "add r10, r0 \n\t" \ + "adc r11, r1 \n\t" \ + "adc r12, r25 \n\t" \ + \ + "ld r7, x+ \n\t" \ + "mov r13, r25 \n\t" \ + "mul r2, r7 \n\t" \ + "add r11, r0 \n\t" \ + "adc r12, r1 \n\t" \ + "adc r13, r25 \n\t" \ + "mul r3, r6 \n\t" \ + "add r11, r0 \n\t" \ + "adc r12, r1 \n\t" \ + "adc r13, r25 \n\t" \ + "mul r4, r9 \n\t" \ + "add r11, r0 \n\t" \ + "adc r12, r1 \n\t" \ + "adc r13, r25 \n\t" \ + "mul r5, r8 \n\t" \ + "add r11, r0 \n\t" \ + "adc r12, r1 \n\t" \ + "adc r13, r25 \n\t" \ + \ + "ld r8, x+ \n\t" \ + "mov r14, r25 \n\t" \ + "mul r2, r8 \n\t" \ + "add r12, r0 \n\t" \ + "adc r13, r1 \n\t" \ + "adc r14, r25 \n\t" \ + "mul r3, r7 \n\t" \ + "add r12, r0 \n\t" \ + "adc r13, r1 \n\t" \ + "adc r14, r25 \n\t" \ + "mul r4, r6 \n\t" \ + "add r12, r0 \n\t" \ + "adc r13, r1 \n\t" \ + "adc r14, r25 \n\t" \ + "mul r5, r9 \n\t" \ + "add r12, r0 \n\t" \ + "adc r13, r1 \n\t" \ + "adc r14, r25 \n\t" \ + \ + "ld r9, x+ \n\t" \ + "mov r15, r25 \n\t" \ + "mul r2, r9 \n\t" \ + "add r13, r0 \n\t" \ + "adc r14, r1 \n\t" \ + "adc r15, r25 \n\t" \ + "mul r3, r8 \n\t" \ + "add r13, r0 \n\t" \ + "adc r14, r1 \n\t" \ + "adc r15, r25 \n\t" \ + "mul r4, r7 \n\t" \ + "add r13, r0 \n\t" \ + "adc r14, r1 \n\t" \ + "adc r15, r25 \n\t" \ + "mul r5, r6 \n\t" \ + "add r13, r0 \n\t" \ + "adc r14, r1 \n\t" \ + "adc r15, r25 \n\t" \ + \ + "ld r6, x+ \n\t" \ + "mov r16, r25 \n\t" \ + "mul r2, r6 \n\t" \ + "add r14, r0 \n\t" \ + "adc r15, r1 \n\t" \ + "adc r16, r25 \n\t" \ + "mul r3, r9 \n\t" \ + "add r14, r0 \n\t" \ + "adc r15, r1 \n\t" \ + "adc r16, r25 \n\t" \ + "mul r4, r8 \n\t" \ + "add r14, r0 \n\t" \ + "adc r15, r1 \n\t" \ + "adc r16, r25 \n\t" \ + "mul r5, r7 \n\t" \ + "add r14, r0 \n\t" \ + "adc r15, r1 \n\t" \ + "adc r16, r25 \n\t" \ + \ + "ld r7, x+ \n\t" \ + "mov r17, r25 \n\t" \ + "mul r2, r7 \n\t" \ + "add r15, r0 \n\t" \ + "adc r16, r1 \n\t" \ + "adc r17, r25 \n\t" \ + "mul r3, r6 \n\t" \ + "add r15, r0 \n\t" \ + "adc r16, r1 \n\t" \ + "adc r17, r25 \n\t" \ + "mul r4, r9 \n\t" \ + "add r15, r0 \n\t" \ + "adc r16, r1 \n\t" \ + "adc r17, r25 \n\t" \ + "mul r5, r8 \n\t" \ + "add r15, r0 \n\t" \ + "adc r16, r1 \n\t" \ + "adc r17, r25 \n\t" \ + \ + "ld r8, x+ \n\t" \ + "mov r18, r25 \n\t" \ + "mul r2, r8 \n\t" \ + "add r16, r0 \n\t" \ + "adc r17, r1 \n\t" \ + "adc r18, r25 \n\t" \ + "mul r3, r7 \n\t" \ + "add r16, r0 \n\t" \ + "adc r17, r1 \n\t" \ + "adc r18, r25 \n\t" \ + "mul r4, r6 \n\t" \ + "add r16, r0 \n\t" \ + "adc r17, r1 \n\t" \ + "adc r18, r25 \n\t" \ + "mul r5, r9 \n\t" \ + "add r16, r0 \n\t" \ + "adc r17, r1 \n\t" \ + "adc r18, r25 \n\t" \ + \ + "ld r9, x+ \n\t" \ + "mov r19, r25 \n\t" \ + "mul r2, r9 \n\t" \ + "add r17, r0 \n\t" \ + "adc r18, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r3, r8 \n\t" \ + "add r17, r0 \n\t" \ + "adc r18, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r4, r7 \n\t" \ + "add r17, r0 \n\t" \ + "adc r18, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r5, r6 \n\t" \ + "add r17, r0 \n\t" \ + "adc r18, r1 \n\t" \ + "adc r19, r25 \n\t" \ + \ + "lsl r28 \n\t" \ + "bld r28, 0 \n\t" \ + "rol r29 \n\t" \ + "rol r10 \n\t" \ + "rol r11 \n\t" \ + "rol r12 \n\t" \ + "rol r13 \n\t" \ + "rol r14 \n\t" \ + "rol r15 \n\t" \ + "rol r16 \n\t" \ + "rol r17 \n\t" \ + "rol r18 \n\t" \ + "rol r19 \n\t" \ + "ld r0, z \n\t" \ + "add r28, r0 \n\t" \ + "st z+, r28 \n\t" \ + "ld r0, z \n\t" \ + "adc r29, r0 \n\t" \ + "st z+, r29 \n\t" \ + "ld r0, z \n\t" \ + "adc r10, r0 \n\t" \ + "st z+, r10 \n\t" \ + "ld r0, z \n\t" \ + "adc r11, r0 \n\t" \ + "st z+, r11 \n\t" \ + "ld r0, z \n\t" \ + "adc r12, r0 \n\t" \ + "st z+, r12 \n\t" \ + "ld r0, z \n\t" \ + "adc r13, r0 \n\t" \ + "st z+, r13 \n\t" \ + "ld r0, z \n\t" \ + "adc r14, r0 \n\t" \ + "st z+, r14 \n\t" \ + "ld r0, z \n\t" \ + "adc r15, r0 \n\t" \ + "st z+, r15 \n\t" \ + "ld r0, z \n\t" \ + "adc r16, r0 \n\t" \ + "st z+, r16 \n\t" \ + "ld r0, z \n\t" \ + "adc r17, r0 \n\t" \ + "st z+, r17 \n\t" \ + "adc r18, r25 \n\t" \ + "adc r19, r25 \n\t" \ + \ + "mul r2, r2 \n\t" \ + "mov r21, r0 \n\t" \ + "mov r22, r1 \n\t" \ + "mul r3, r3 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r4, r4 \n\t" \ + "mov r28, r0 \n\t" \ + "mov r29, r1 \n\t" \ + "mul r5, r5 \n\t" \ + "mov r10, r0 \n\t" \ + "mov r11, r1 \n\t" \ + "add r21, r18 \n\t" \ + "adc r22, r19 \n\t" \ + "adc r23, r25 \n\t" \ + "adc r24, r25 \n\t" \ + \ + "mul r7, r5 \n\t" \ + "mov r18, r0 \n\t" \ + "mov r19, r1 \n\t" \ + "mov r12, r25 \n\t" \ + "mul r8, r4 \n\t" \ + "add r18, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r12, r25 \n\t" \ + "mul r9, r3 \n\t" \ + "add r18, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r12, r25 \n\t" \ + "mov r13, r25 \n\t" \ + "mul r8, r5 \n\t" \ + "add r19, r0 \n\t" \ + "adc r12, r1 \n\t" \ + "adc r13, r25 \n\t" \ + "mul r9, r4 \n\t" \ + "add r19, r0 \n\t" \ + "adc r12, r1 \n\t" \ + "adc r13, r25 \n\t" \ + "mul r2, r3 \n\t" \ + "add r19, r0 \n\t" \ + "adc r12, r1 \n\t" \ + "adc r13, r25 \n\t" \ + "mov r14, r25 \n\t" \ + "mul r9, r5 \n\t" \ + "add r12, r0 \n\t" \ + "adc r13, r1 \n\t" \ + "adc r14, r25 \n\t" \ + "mul r2, r4 \n\t" \ + "add r12, r0 \n\t" \ + "adc r13, r1 \n\t" \ + "adc r14, r25 \n\t" \ + "mov r15, r25 \n\t" \ + "mul r2, r5 \n\t" \ + "add r13, r0 \n\t" \ + "adc r14, r1 \n\t" \ + "adc r15, r25 \n\t" \ + "mul r3, r4 \n\t" \ + "add r13, r0 \n\t" \ + "adc r14, r1 \n\t" \ + "adc r15, r25 \n\t" \ + "mov r16, r25 \n\t" \ + "mul r3, r5 \n\t" \ + "add r14, r0 \n\t" \ + "adc r15, r1 \n\t" \ + "adc r16, r25 \n\t" \ + "mul r4, r5 \n\t" \ + "add r15, r0 \n\t" \ + "adc r16, r1 \n\t" \ + \ + "lsl r18 \n\t" \ + "rol r19 \n\t" \ + "rol r12 \n\t" \ + "rol r13 \n\t" \ + "rol r14 \n\t" \ + "rol r15 \n\t" \ + "rol r16 \n\t" \ + "adc r11, r25 \n\t" \ + "add r21, r18 \n\t" \ + "adc r22, r19 \n\t" \ + "adc r23, r12 \n\t" \ + "adc r24, r13 \n\t" \ + "adc r28, r14 \n\t" \ + "adc r29, r15 \n\t" \ + "adc r10, r16 \n\t" \ + "adc r11, r25 \n\t" \ + \ + "st z+, r21 \n\t" \ + "st z+, r22 \n\t" \ + "st z+, r23 \n\t" \ + "st z+, r24 \n\t" \ + "st z+, r28 \n\t" \ + "st z+, r29 \n\t" \ + "st z+, r10 \n\t" \ + "st z+, r11 \n\t" \ + "adiw r26, 4 \n\t" + +#define FAST_SQUARE_ASM_28 \ + "ldi r25, 0 \n\t" \ + "movw r28, r26 \n\t" \ + "ld r2, x+ \n\t" \ + "ld r3, x+ \n\t" \ + "ld r4, x+ \n\t" \ + "ld r5, x+ \n\t" \ + "adiw r28, 20 \n\t" \ + "ld r12, y+ \n\t" \ + "ld r13, y+ \n\t" \ + "ld r14, y+ \n\t" \ + "ld r15, y+ \n\t" \ + "adiw r30, 20 \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul 2, 12 \n\t" \ + "st z+, r0 \n\t" \ + "mov r22, r1 \n\t" \ + \ + "ldi r24, 0 \n\t" \ + "mul r2, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r2, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r2, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ld r12, y+ \n\t" \ + "ldi r24, 0 \n\t" \ + "mul r2, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ld r13, y+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r2, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r14, y+ \n\t" \ + "ldi r23, 0 \n\t" \ + "mul r2, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ld r15, y+ \n\t" \ + "ldi r24, 0 \n\t" \ + "mul r2, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ld r2, x+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r3, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r3, x+ \n\t" \ + "ldi r23, 0 \n\t" \ + "mul r4, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r2, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ld r4, x+ \n\t" \ + "ldi r24, 0 \n\t" \ + "mul r5, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r2, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ld r5, x+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r2, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r3, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ldi r24, 0 \n\t" \ + "mul r4, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "mul r5, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "st z+, r23 \n\t" \ + "st z+, r24 \n\t" \ + \ + "sbiw r26, 8 \n\t" \ + "sbiw r30, 36 \n\t" \ + "ld r2, x+ \n\t" \ + "ld r3, x+ \n\t" \ + "ld r4, x+ \n\t" \ + "ld r5, x+ \n\t" \ + "ld r6, x+ \n\t" \ + "ld r7, x+ \n\t" \ + "ld r8, x+ \n\t" \ + "ld r9, x+ \n\t" \ + "ld r10, x+ \n\t" \ + "ld r11, x+ \n\t" \ + "ld r12, x+ \n\t" \ + "ld r13, x+ \n\t" \ + "ld r14, x+ \n\t" \ + "ld r15, x+ \n\t" \ + "ld r16, x+ \n\t" \ + "ld r17, x+ \n\t" \ + "ld r18, x+ \n\t" \ + "ld r19, x+ \n\t" \ + "ld r20, x+ \n\t" \ + "ld r21, x+ \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r2, r2 \n\t" \ + "st z+, r0 \n\t" \ + "mov r22, r1 \n\t" \ + \ + "ldi r24, 0 \n\t" \ + "mul r2, r3 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r2, r4 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r3 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r2, r5 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r3, r4 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r2, r6 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r3, r5 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r4, r4 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r28 \n\t" \ + "adc r24, r29 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r2, r7 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r3, r6 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r4, r5 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r2, r8 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r3, r7 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r6 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r5, r5 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r28 \n\t" \ + "adc r24, r29 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r2, r9 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r3, r8 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r4, r7 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r5, r6 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r2, r10 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r3, r9 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r8 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r7 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r6, r6 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r28 \n\t" \ + "adc r24, r29 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r2, r11 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r3, r10 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r4, r9 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r5, r8 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r6, r7 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r2, r12 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r3, r11 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r10 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r9 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r8 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r7, r7 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r28 \n\t" \ + "adc r24, r29 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r2, r13 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r3, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r4, r11 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r5, r10 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r6, r9 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r7, r8 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r2, r14 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r3, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r11 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r10 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r9 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r8, r8 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r28 \n\t" \ + "adc r24, r29 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r2, r15 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r3, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r4, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r5, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r6, r11 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r7, r10 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r8, r9 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r2, r16 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r3, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r11 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r10 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r9, r9 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r28 \n\t" \ + "adc r24, r29 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r2, r17 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r3, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r4, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r5, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r6, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r7, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r8, r11 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r9, r10 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r2, r18 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r3, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r11 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r10, r10 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r28 \n\t" \ + "adc r24, r29 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r2, r19 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r3, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r4, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r5, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r6, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r7, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r8, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r9, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r10, r11 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r2, r20 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r3, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r11, r11 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r28 \n\t" \ + "adc r24, r29 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r2, r21 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r3, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r4, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r5, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r6, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r7, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r8, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r9, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r10, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r11, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r2, x+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r3, r21 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r4, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r12, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r28 \n\t" \ + "adc r24, r29 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r3, r2 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r4, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r5, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r6, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r7, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r8, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r9, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r10, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r11, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r12, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r25 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r3, x+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r4, r2 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r5, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r12, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r13, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r28 \n\t" \ + "adc r24, r29 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r4, r3 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r5, r2 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r6, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r7, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r8, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r9, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r10, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r11, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r12, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r13, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r25 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r4, x+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r5, r3 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r6, r2 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r12, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r13, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r14, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r28 \n\t" \ + "adc r24, r29 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r5, r4 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r6, r3 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r7, r2 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r8, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r9, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r10, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r11, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r12, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r13, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r14, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r25 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r5, x+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r6, r4 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r7, r3 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r2 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r12, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r13, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r14, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r15, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r28 \n\t" \ + "adc r24, r29 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r6, r5 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r7, r4 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r8, r3 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r9, r2 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r10, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r11, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r12, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r13, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r14, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r15, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r25 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r6, x+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r7, r5 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r8, r4 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r3 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r2 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r12, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r13, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r14, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r15, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r16, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r28 \n\t" \ + "adc r24, r29 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r7, r6 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r8, r5 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r9, r4 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r10, r3 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r11, r2 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r12, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r13, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r14, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r15, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r16, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r25 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r7, x+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r8, r6 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r9, r5 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r4 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r3 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r12, r2 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r13, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r14, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r15, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r16, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r17, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r28 \n\t" \ + "adc r24, r29 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r8, r7 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r9, r6 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r10, r5 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r11, r4 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r12, r3 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r13, r2 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r14, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r15, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r16, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r17, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r25 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r8, x+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r9, r7 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r10, r6 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r5 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r12, r4 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r13, r3 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r14, r2 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r15, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r16, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r17, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r18, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r28 \n\t" \ + "adc r24, r29 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r9, r8 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r10, r7 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r11, r6 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r12, r5 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r13, r4 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r14, r3 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r15, r2 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r16, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r17, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r18, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r25 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r9, x+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r10, r8 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r11, r7 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r12, r6 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r13, r5 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r14, r4 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r15, r3 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r16, r2 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r17, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r18, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r19, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r28 \n\t" \ + "adc r24, r29 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r10, r9 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r11, r8 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r12, r7 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r13, r6 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r14, r5 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r15, r4 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r16, r3 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r17, r2 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r18, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r19, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r25 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r11, r9 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r12, r8 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r13, r7 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r14, r6 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r15, r5 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r16, r4 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r17, r3 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r18, r2 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r19, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r20, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r28 \n\t" \ + "adc r24, r29 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r12, r9 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r13, r8 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r14, r7 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r15, r6 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r16, r5 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r17, r4 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r18, r3 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r19, r2 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r20, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r13, r9 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r14, r8 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r15, r7 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r16, r6 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r17, r5 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r18, r4 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r19, r3 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r20, r2 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r21, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r28 \n\t" \ + "adc r24, r29 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r14, r9 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r15, r8 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r16, r7 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r17, r6 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r18, r5 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r19, r4 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r20, r3 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r21, r2 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r15, r9 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r16, r8 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r17, r7 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r18, r6 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r19, r5 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r20, r4 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r21, r3 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r2, r2 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r28 \n\t" \ + "adc r24, r29 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r16, r9 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r17, r8 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r18, r7 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r19, r6 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r20, r5 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r21, r4 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r2, r3 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r17, r9 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r18, r8 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r19, r7 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r20, r6 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r21, r5 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r4 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r3, r3 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r28 \n\t" \ + "adc r24, r29 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r18, r9 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r19, r8 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r20, r7 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r21, r6 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r2, r5 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r3, r4 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r19, r9 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r20, r8 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r21, r7 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r6 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r5 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r4, r4 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r28 \n\t" \ + "adc r24, r29 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r20, r9 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r21, r8 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r2, r7 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r3, r6 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r4, r5 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r21, r9 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r2, r8 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r7 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r6 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r5, r5 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r28 \n\t" \ + "adc r24, r29 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r2, r9 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r3, r8 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r4, r7 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r5, r6 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r3, r9 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r4, r8 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r7 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r6, r6 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r28 \n\t" \ + "adc r24, r29 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r4, r9 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r5, r8 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r6, r7 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r5, r9 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r6, r8 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r7, r7 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r28 \n\t" \ + "adc r24, r29 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r6, r9 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r7, r8 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r7, r9 \n\t" \ + "add r28, r0 \n\t" \ + "adc r29, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "add r28, r0 \n\t" \ + "adc r29, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r8, r8 \n\t" \ + "add r28, r0 \n\t" \ + "adc r29, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r28 \n\t" \ + \ + "ldi r28, 0 \n\t" \ + "mul r8, r9 \n\t" \ + "add r29, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r28, r25 \n\t" \ + "add r29, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r28, r25 \n\t" \ + "st z+, r29 \n\t" \ + \ + "mul r9, r9 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "st z+, r23 \n\t" \ + "st z+, r28 \n\t" + +#define FAST_SQUARE_ASM_28_TO_32 \ + "cpi r20, 28 \n\t" \ + "brne 1f \n\t" \ + "jmp 2f \n\t" \ + "1: \n\t" \ + "ld r2, x+ \n\t" \ + "ld r3, x+ \n\t" \ + "ld r4, x+ \n\t" \ + "ld r5, x+ \n\t" \ + "sbiw r26, 32 \n\t" \ + "sbiw r30, 28 \n\t" \ + "ld r6, x+ \n\t" \ + "ld r7, x+ \n\t" \ + "ld r8, x+ \n\t" \ + "ld r9, x+ \n\t" \ + \ + "mul r2, r6 \n\t" \ + "mov r10, r0 \n\t" \ + "mov r11, r1 \n\t" \ + "mov r12, r25 \n\t" \ + "mov r13, r25 \n\t" \ + "mul r2, r7 \n\t" \ + "add r11, r0 \n\t" \ + "adc r12, r1 \n\t" \ + "adc r13, r25 \n\t" \ + "mul r3, r6 \n\t" \ + "add r11, r0 \n\t" \ + "adc r12, r1 \n\t" \ + "adc r13, r25 \n\t" \ + \ + "mov r14, r25 \n\t" \ + "mul r2, r8 \n\t" \ + "add r12, r0 \n\t" \ + "adc r13, r1 \n\t" \ + "adc r14, r25 \n\t" \ + "mul r3, r7 \n\t" \ + "add r12, r0 \n\t" \ + "adc r13, r1 \n\t" \ + "adc r14, r25 \n\t" \ + "mul r4, r6 \n\t" \ + "add r12, r0 \n\t" \ + "adc r13, r1 \n\t" \ + "adc r14, r25 \n\t" \ + \ + "mov r15, r25 \n\t" \ + "mul r2, r9 \n\t" \ + "add r13, r0 \n\t" \ + "adc r14, r1 \n\t" \ + "adc r15, r25 \n\t" \ + "mul r3, r8 \n\t" \ + "add r13, r0 \n\t" \ + "adc r14, r1 \n\t" \ + "adc r15, r25 \n\t" \ + "mul r4, r7 \n\t" \ + "add r13, r0 \n\t" \ + "adc r14, r1 \n\t" \ + "adc r15, r25 \n\t" \ + "mul r5, r6 \n\t" \ + "add r13, r0 \n\t" \ + "adc r14, r1 \n\t" \ + "adc r15, r25 \n\t" \ + \ + "ld r6, x+ \n\t" \ + "mov r16, r25 \n\t" \ + "mul r2, r6 \n\t" \ + "add r14, r0 \n\t" \ + "adc r15, r1 \n\t" \ + "adc r16, r25 \n\t" \ + "mul r3, r9 \n\t" \ + "add r14, r0 \n\t" \ + "adc r15, r1 \n\t" \ + "adc r16, r25 \n\t" \ + "mul r4, r8 \n\t" \ + "add r14, r0 \n\t" \ + "adc r15, r1 \n\t" \ + "adc r16, r25 \n\t" \ + "mul r5, r7 \n\t" \ + "add r14, r0 \n\t" \ + "adc r15, r1 \n\t" \ + "adc r16, r25 \n\t" \ + \ + "ld r7, x+ \n\t" \ + "mov r17, r25 \n\t" \ + "mul r2, r7 \n\t" \ + "add r15, r0 \n\t" \ + "adc r16, r1 \n\t" \ + "adc r17, r25 \n\t" \ + "mul r3, r6 \n\t" \ + "add r15, r0 \n\t" \ + "adc r16, r1 \n\t" \ + "adc r17, r25 \n\t" \ + "mul r4, r9 \n\t" \ + "add r15, r0 \n\t" \ + "adc r16, r1 \n\t" \ + "adc r17, r25 \n\t" \ + "mul r5, r8 \n\t" \ + "add r15, r0 \n\t" \ + "adc r16, r1 \n\t" \ + "adc r17, r25 \n\t" \ + \ + "ld r8, x+ \n\t" \ + "mov r18, r25 \n\t" \ + "mul r2, r8 \n\t" \ + "add r16, r0 \n\t" \ + "adc r17, r1 \n\t" \ + "adc r18, r25 \n\t" \ + "mul r3, r7 \n\t" \ + "add r16, r0 \n\t" \ + "adc r17, r1 \n\t" \ + "adc r18, r25 \n\t" \ + "mul r4, r6 \n\t" \ + "add r16, r0 \n\t" \ + "adc r17, r1 \n\t" \ + "adc r18, r25 \n\t" \ + "mul r5, r9 \n\t" \ + "add r16, r0 \n\t" \ + "adc r17, r1 \n\t" \ + "adc r18, r25 \n\t" \ + \ + "ld r9, x+ \n\t" \ + "mov r19, r25 \n\t" \ + "mul r2, r9 \n\t" \ + "add r17, r0 \n\t" \ + "adc r18, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r3, r8 \n\t" \ + "add r17, r0 \n\t" \ + "adc r18, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r4, r7 \n\t" \ + "add r17, r0 \n\t" \ + "adc r18, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r5, r6 \n\t" \ + "add r17, r0 \n\t" \ + "adc r18, r1 \n\t" \ + "adc r19, r25 \n\t" \ + \ + "ld r6, x+ \n\t" \ + "mov r21, r25 \n\t" \ + "mul r2, r6 \n\t" \ + "add r18, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r3, r9 \n\t" \ + "add r18, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r4, r8 \n\t" \ + "add r18, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r5, r7 \n\t" \ + "add r18, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r21, r25 \n\t" \ + \ + "ld r7, x+ \n\t" \ + "mov r22, r25 \n\t" \ + "mul r2, r7 \n\t" \ + "add r19, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r6 \n\t" \ + "add r19, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r9 \n\t" \ + "add r19, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r8 \n\t" \ + "add r19, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r22, r25 \n\t" \ + \ + "ld r8, x+ \n\t" \ + "mov r23, r25 \n\t" \ + "mul r2, r8 \n\t" \ + "add r21, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r7 \n\t" \ + "add r21, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r6 \n\t" \ + "add r21, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r9 \n\t" \ + "add r21, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + \ + "ld r9, x+ \n\t" \ + "mov r24, r25 \n\t" \ + "mul r2, r9 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r8 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r7 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r6 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + \ + "ld r6, x+ \n\t" \ + "mov r28, r25 \n\t" \ + "mul r2, r6 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r28, r25 \n\t" \ + "mul r3, r9 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r28, r25 \n\t" \ + "mul r4, r8 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r28, r25 \n\t" \ + "mul r5, r7 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r28, r25 \n\t" \ + \ + "ld r7, x+ \n\t" \ + "mov r29, r25 \n\t" \ + "mul r2, r7 \n\t" \ + "add r24, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r3, r6 \n\t" \ + "add r24, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r4, r9 \n\t" \ + "add r24, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r5, r8 \n\t" \ + "add r24, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + \ + "lsl r10 \n\t" \ + "rol r11 \n\t" \ + "rol r12 \n\t" \ + "rol r13 \n\t" \ + "rol r14 \n\t" \ + "rol r15 \n\t" \ + "rol r16 \n\t" \ + "rol r17 \n\t" \ + "rol r18 \n\t" \ + "rol r19 \n\t" \ + "rol r21 \n\t" \ + "rol r22 \n\t" \ + "rol r23 \n\t" \ + "rol r24 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "ld r0, z \n\t" \ + "add r10, r0 \n\t" \ + "st z+, r10 \n\t" \ + "ld r0, z \n\t" \ + "adc r11, r0 \n\t" \ + "st z+, r11 \n\t" \ + "ld r0, z \n\t" \ + "adc r12, r0 \n\t" \ + "st z+, r12 \n\t" \ + "ld r0, z \n\t" \ + "adc r13, r0 \n\t" \ + "st z+, r13 \n\t" \ + "ld r0, z \n\t" \ + "adc r14, r0 \n\t" \ + "st z+, r14 \n\t" \ + "ld r0, z \n\t" \ + "adc r15, r0 \n\t" \ + "st z+, r15 \n\t" \ + "ld r0, z \n\t" \ + "adc r16, r0 \n\t" \ + "st z+, r16 \n\t" \ + "ld r0, z \n\t" \ + "adc r17, r0 \n\t" \ + "st z+, r17 \n\t" \ + "ld r0, z \n\t" \ + "adc r18, r0 \n\t" \ + "st z+, r18 \n\t" \ + "ld r0, z \n\t" \ + "adc r19, r0 \n\t" \ + "st z+, r19 \n\t" \ + "ld r0, z \n\t" \ + "adc r21, r0 \n\t" \ + "st z+, r21 \n\t" \ + "ld r0, z \n\t" \ + "adc r22, r0 \n\t" \ + "st z+, r22 \n\t" \ + "ld r0, z \n\t" \ + "adc r23, r0 \n\t" \ + "st z+, r23 \n\t" \ + "ld r0, z \n\t" \ + "adc r24, r0 \n\t" \ + "st z+, r24 \n\t" \ + "adc r28, r25 \n\t" \ + "adc r29, r25 \n\t" \ + "bst r28, 0 \n\t" \ + "lsr r29 \n\t" \ + "ror r28 \n\t" \ + \ + "ld r8, x+ \n\t" \ + "mov r10, r25 \n\t" \ + "mul r2, r8 \n\t" \ + "add r28, r0 \n\t" \ + "adc r29, r1 \n\t" \ + "adc r10, r25 \n\t" \ + "mul r3, r7 \n\t" \ + "add r28, r0 \n\t" \ + "adc r29, r1 \n\t" \ + "adc r10, r25 \n\t" \ + "mul r4, r6 \n\t" \ + "add r28, r0 \n\t" \ + "adc r29, r1 \n\t" \ + "adc r10, r25 \n\t" \ + "mul r5, r9 \n\t" \ + "add r28, r0 \n\t" \ + "adc r29, r1 \n\t" \ + "adc r10, r25 \n\t" \ + \ + "ld r9, x+ \n\t" \ + "mov r11, r25 \n\t" \ + "mul r2, r9 \n\t" \ + "add r29, r0 \n\t" \ + "adc r10, r1 \n\t" \ + "adc r11, r25 \n\t" \ + "mul r3, r8 \n\t" \ + "add r29, r0 \n\t" \ + "adc r10, r1 \n\t" \ + "adc r11, r25 \n\t" \ + "mul r4, r7 \n\t" \ + "add r29, r0 \n\t" \ + "adc r10, r1 \n\t" \ + "adc r11, r25 \n\t" \ + "mul r5, r6 \n\t" \ + "add r29, r0 \n\t" \ + "adc r10, r1 \n\t" \ + "adc r11, r25 \n\t" \ + \ + "ld r6, x+ \n\t" \ + "mov r12, r25 \n\t" \ + "mul r2, r6 \n\t" \ + "add r10, r0 \n\t" \ + "adc r11, r1 \n\t" \ + "adc r12, r25 \n\t" \ + "mul r3, r9 \n\t" \ + "add r10, r0 \n\t" \ + "adc r11, r1 \n\t" \ + "adc r12, r25 \n\t" \ + "mul r4, r8 \n\t" \ + "add r10, r0 \n\t" \ + "adc r11, r1 \n\t" \ + "adc r12, r25 \n\t" \ + "mul r5, r7 \n\t" \ + "add r10, r0 \n\t" \ + "adc r11, r1 \n\t" \ + "adc r12, r25 \n\t" \ + \ + "ld r7, x+ \n\t" \ + "mov r13, r25 \n\t" \ + "mul r2, r7 \n\t" \ + "add r11, r0 \n\t" \ + "adc r12, r1 \n\t" \ + "adc r13, r25 \n\t" \ + "mul r3, r6 \n\t" \ + "add r11, r0 \n\t" \ + "adc r12, r1 \n\t" \ + "adc r13, r25 \n\t" \ + "mul r4, r9 \n\t" \ + "add r11, r0 \n\t" \ + "adc r12, r1 \n\t" \ + "adc r13, r25 \n\t" \ + "mul r5, r8 \n\t" \ + "add r11, r0 \n\t" \ + "adc r12, r1 \n\t" \ + "adc r13, r25 \n\t" \ + \ + "ld r8, x+ \n\t" \ + "mov r14, r25 \n\t" \ + "mul r2, r8 \n\t" \ + "add r12, r0 \n\t" \ + "adc r13, r1 \n\t" \ + "adc r14, r25 \n\t" \ + "mul r3, r7 \n\t" \ + "add r12, r0 \n\t" \ + "adc r13, r1 \n\t" \ + "adc r14, r25 \n\t" \ + "mul r4, r6 \n\t" \ + "add r12, r0 \n\t" \ + "adc r13, r1 \n\t" \ + "adc r14, r25 \n\t" \ + "mul r5, r9 \n\t" \ + "add r12, r0 \n\t" \ + "adc r13, r1 \n\t" \ + "adc r14, r25 \n\t" \ + \ + "ld r9, x+ \n\t" \ + "mov r15, r25 \n\t" \ + "mul r2, r9 \n\t" \ + "add r13, r0 \n\t" \ + "adc r14, r1 \n\t" \ + "adc r15, r25 \n\t" \ + "mul r3, r8 \n\t" \ + "add r13, r0 \n\t" \ + "adc r14, r1 \n\t" \ + "adc r15, r25 \n\t" \ + "mul r4, r7 \n\t" \ + "add r13, r0 \n\t" \ + "adc r14, r1 \n\t" \ + "adc r15, r25 \n\t" \ + "mul r5, r6 \n\t" \ + "add r13, r0 \n\t" \ + "adc r14, r1 \n\t" \ + "adc r15, r25 \n\t" \ + \ + "ld r6, x+ \n\t" \ + "mov r16, r25 \n\t" \ + "mul r2, r6 \n\t" \ + "add r14, r0 \n\t" \ + "adc r15, r1 \n\t" \ + "adc r16, r25 \n\t" \ + "mul r3, r9 \n\t" \ + "add r14, r0 \n\t" \ + "adc r15, r1 \n\t" \ + "adc r16, r25 \n\t" \ + "mul r4, r8 \n\t" \ + "add r14, r0 \n\t" \ + "adc r15, r1 \n\t" \ + "adc r16, r25 \n\t" \ + "mul r5, r7 \n\t" \ + "add r14, r0 \n\t" \ + "adc r15, r1 \n\t" \ + "adc r16, r25 \n\t" \ + \ + "ld r7, x+ \n\t" \ + "mov r17, r25 \n\t" \ + "mul r2, r7 \n\t" \ + "add r15, r0 \n\t" \ + "adc r16, r1 \n\t" \ + "adc r17, r25 \n\t" \ + "mul r3, r6 \n\t" \ + "add r15, r0 \n\t" \ + "adc r16, r1 \n\t" \ + "adc r17, r25 \n\t" \ + "mul r4, r9 \n\t" \ + "add r15, r0 \n\t" \ + "adc r16, r1 \n\t" \ + "adc r17, r25 \n\t" \ + "mul r5, r8 \n\t" \ + "add r15, r0 \n\t" \ + "adc r16, r1 \n\t" \ + "adc r17, r25 \n\t" \ + \ + "ld r8, x+ \n\t" \ + "mov r18, r25 \n\t" \ + "mul r2, r8 \n\t" \ + "add r16, r0 \n\t" \ + "adc r17, r1 \n\t" \ + "adc r18, r25 \n\t" \ + "mul r3, r7 \n\t" \ + "add r16, r0 \n\t" \ + "adc r17, r1 \n\t" \ + "adc r18, r25 \n\t" \ + "mul r4, r6 \n\t" \ + "add r16, r0 \n\t" \ + "adc r17, r1 \n\t" \ + "adc r18, r25 \n\t" \ + "mul r5, r9 \n\t" \ + "add r16, r0 \n\t" \ + "adc r17, r1 \n\t" \ + "adc r18, r25 \n\t" \ + \ + "ld r9, x+ \n\t" \ + "mov r19, r25 \n\t" \ + "mul r2, r9 \n\t" \ + "add r17, r0 \n\t" \ + "adc r18, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r3, r8 \n\t" \ + "add r17, r0 \n\t" \ + "adc r18, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r4, r7 \n\t" \ + "add r17, r0 \n\t" \ + "adc r18, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r5, r6 \n\t" \ + "add r17, r0 \n\t" \ + "adc r18, r1 \n\t" \ + "adc r19, r25 \n\t" \ + \ + "ld r6, x+ \n\t" \ + "mov r21, r25 \n\t" \ + "mul r2, r6 \n\t" \ + "add r18, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r3, r9 \n\t" \ + "add r18, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r4, r8 \n\t" \ + "add r18, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r5, r7 \n\t" \ + "add r18, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r21, r25 \n\t" \ + \ + "ld r7, x+ \n\t" \ + "mov r22, r25 \n\t" \ + "mul r2, r7 \n\t" \ + "add r19, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r6 \n\t" \ + "add r19, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r9 \n\t" \ + "add r19, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r8 \n\t" \ + "add r19, r0 \n\t" \ + "adc r21, r1 \n\t" \ + "adc r22, r25 \n\t" \ + \ + "ld r8, x+ \n\t" \ + "mov r23, r25 \n\t" \ + "mul r2, r8 \n\t" \ + "add r21, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r7 \n\t" \ + "add r21, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r6 \n\t" \ + "add r21, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r9 \n\t" \ + "add r21, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + \ + "ld r9, x+ \n\t" \ + "mov r24, r25 \n\t" \ + "mul r2, r9 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r8 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r7 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r6 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + \ + "lsl r28 \n\t" \ + "bld r28, 0 \n\t" \ + "rol r29 \n\t" \ + "rol r10 \n\t" \ + "rol r11 \n\t" \ + "rol r12 \n\t" \ + "rol r13 \n\t" \ + "rol r14 \n\t" \ + "rol r15 \n\t" \ + "rol r16 \n\t" \ + "rol r17 \n\t" \ + "rol r18 \n\t" \ + "rol r19 \n\t" \ + "rol r21 \n\t" \ + "rol r22 \n\t" \ + "rol r23 \n\t" \ + "rol r24 \n\t" \ + "ld r0, z \n\t" \ + "add r28, r0 \n\t" \ + "st z+, r28 \n\t" \ + "ld r0, z \n\t" \ + "adc r29, r0 \n\t" \ + "st z+, r29 \n\t" \ + "ld r0, z \n\t" \ + "adc r10, r0 \n\t" \ + "st z+, r10 \n\t" \ + "ld r0, z \n\t" \ + "adc r11, r0 \n\t" \ + "st z+, r11 \n\t" \ + "ld r0, z \n\t" \ + "adc r12, r0 \n\t" \ + "st z+, r12 \n\t" \ + "ld r0, z \n\t" \ + "adc r13, r0 \n\t" \ + "st z+, r13 \n\t" \ + "ld r0, z \n\t" \ + "adc r14, r0 \n\t" \ + "st z+, r14 \n\t" \ + "ld r0, z \n\t" \ + "adc r15, r0 \n\t" \ + "st z+, r15 \n\t" \ + "ld r0, z \n\t" \ + "adc r16, r0 \n\t" \ + "st z+, r16 \n\t" \ + "ld r0, z \n\t" \ + "adc r17, r0 \n\t" \ + "st z+, r17 \n\t" \ + "ld r0, z \n\t" \ + "adc r18, r0 \n\t" \ + "st z+, r18 \n\t" \ + "ld r0, z \n\t" \ + "adc r19, r0 \n\t" \ + "st z+, r19 \n\t" \ + "ld r0, z \n\t" \ + "adc r21, r0 \n\t" \ + "st z+, r21 \n\t" \ + "ld r0, z \n\t" \ + "adc r22, r0 \n\t" \ + "st z+, r22 \n\t" \ + "adc r23, r25 \n\t" \ + "adc r24, r25 \n\t" \ + \ + "mul r2, r2 \n\t" \ + "mov r28, r0 \n\t" \ + "mov r29, r1 \n\t" \ + "mul r3, r3 \n\t" \ + "mov r10, r0 \n\t" \ + "mov r11, r1 \n\t" \ + "mul r4, r4 \n\t" \ + "mov r12, r0 \n\t" \ + "mov r13, r1 \n\t" \ + "mul r5, r5 \n\t" \ + "mov r14, r0 \n\t" \ + "mov r15, r1 \n\t" \ + "add r28, r23 \n\t" \ + "adc r29, r24 \n\t" \ + "adc r10, r25 \n\t" \ + "adc r11, r25 \n\t" \ + \ + "mul r7, r5 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mov r16, r25 \n\t" \ + "mul r8, r4 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r16, r25 \n\t" \ + "mul r9, r3 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r16, r25 \n\t" \ + "mov r17, r25 \n\t" \ + "mul r8, r5 \n\t" \ + "add r24, r0 \n\t" \ + "adc r16, r1 \n\t" \ + "adc r17, r25 \n\t" \ + "mul r9, r4 \n\t" \ + "add r24, r0 \n\t" \ + "adc r16, r1 \n\t" \ + "adc r17, r25 \n\t" \ + "mul r2, r3 \n\t" \ + "add r24, r0 \n\t" \ + "adc r16, r1 \n\t" \ + "adc r17, r25 \n\t" \ + "mov r18, r25 \n\t" \ + "mul r9, r5 \n\t" \ + "add r16, r0 \n\t" \ + "adc r17, r1 \n\t" \ + "adc r18, r25 \n\t" \ + "mul r2, r4 \n\t" \ + "add r16, r0 \n\t" \ + "adc r17, r1 \n\t" \ + "adc r18, r25 \n\t" \ + "mov r19, r25 \n\t" \ + "mul r2, r5 \n\t" \ + "add r17, r0 \n\t" \ + "adc r18, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mul r3, r4 \n\t" \ + "add r17, r0 \n\t" \ + "adc r18, r1 \n\t" \ + "adc r19, r25 \n\t" \ + "mov r21, r25 \n\t" \ + "mul r3, r5 \n\t" \ + "add r18, r0 \n\t" \ + "adc r19, r1 \n\t" \ + "adc r21, r25 \n\t" \ + "mul r4, r5 \n\t" \ + "add r19, r0 \n\t" \ + "adc r21, r1 \n\t" \ + \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r16 \n\t" \ + "rol r17 \n\t" \ + "rol r18 \n\t" \ + "rol r19 \n\t" \ + "rol r21 \n\t" \ + "adc r15, r25 \n\t" \ + "add r28, r23 \n\t" \ + "adc r29, r24 \n\t" \ + "adc r10, r16 \n\t" \ + "adc r11, r17 \n\t" \ + "adc r12, r18 \n\t" \ + "adc r13, r19 \n\t" \ + "adc r14, r21 \n\t" \ + "adc r15, r25 \n\t" \ + \ + "st z+, r28 \n\t" \ + "st z+, r29 \n\t" \ + "st z+, r10 \n\t" \ + "st z+, r11 \n\t" \ + "st z+, r12 \n\t" \ + "st z+, r13 \n\t" \ + "st z+, r14 \n\t" \ + "st z+, r15 \n\t" \ + "adiw r26, 4 \n\t" + +#define FAST_SQUARE_ASM_32 \ + "ldi r25, 0 \n\t" \ + "movw r28, r26 \n\t" \ + "ld r2, x+ \n\t" \ + "ld r3, x+ \n\t" \ + "ld r4, x+ \n\t" \ + "ld r5, x+ \n\t" \ + "ld r6, x+ \n\t" \ + "ld r7, x+ \n\t" \ + "adiw r28, 20 \n\t" \ + "ld r12, y+ \n\t" \ + "ld r13, y+ \n\t" \ + "ld r14, y+ \n\t" \ + "ld r15, y+ \n\t" \ + "ld r16, y+ \n\t" \ + "ld r17, y+ \n\t" \ + "adiw r30, 20 \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul 2, 12 \n\t" \ + "st z+, r0 \n\t" \ + "mov r22, r1 \n\t" \ + \ + "ldi r24, 0 \n\t" \ + "mul r2, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r2, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r2, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ldi r24, 0 \n\t" \ + "mul r2, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r2, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r12, y+ \n\t" \ + "ldi r23, 0 \n\t" \ + "mul r2, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ld r13, y+ \n\t" \ + "ldi r24, 0 \n\t" \ + "mul r2, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ld r14, y+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r2, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r15, y+ \n\t" \ + "ldi r23, 0 \n\t" \ + "mul r2, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ld r16, y+ \n\t" \ + "ldi r24, 0 \n\t" \ + "mul r2, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r12 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ld r17, y+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r2, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r2, x+ \n\t" \ + "ldi r23, 0 \n\t" \ + "mul r3, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r6, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r13 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r2, r12 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ld r3, x+ \n\t" \ + "ldi r24, 0 \n\t" \ + "mul r4, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r6, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r7, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r2, r13 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ld r4, x+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r5, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r5, x+ \n\t" \ + "ldi r23, 0 \n\t" \ + "mul r6, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r7, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r2, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r3, r14 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ld r6, x+ \n\t" \ + "ldi r24, 0 \n\t" \ + "mul r7, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r2, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r3, r15 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r4, r14 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ld r7, x+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r2, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r3, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r4, r16 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r5, r15 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "ldi r24, 0 \n\t" \ + "mul r4, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "mul r5, r16 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r5, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r6, r17 \n\t" \ + "add r24, r0 \n\t" \ + "adc r22, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r24 \n\t" \ + \ + "mul r7, r17 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "st z+, r22 \n\t" \ + "st z+, r23 \n\t" \ + \ + "sbiw r26, 12 \n\t" \ + "sbiw r30, 44 \n\t" \ + "ld r2, x+ \n\t" \ + "ld r3, x+ \n\t" \ + "ld r4, x+ \n\t" \ + "ld r5, x+ \n\t" \ + "ld r6, x+ \n\t" \ + "ld r7, x+ \n\t" \ + "ld r8, x+ \n\t" \ + "ld r9, x+ \n\t" \ + "ld r10, x+ \n\t" \ + "ld r11, x+ \n\t" \ + "ld r12, x+ \n\t" \ + "ld r13, x+ \n\t" \ + "ld r14, x+ \n\t" \ + "ld r15, x+ \n\t" \ + "ld r16, x+ \n\t" \ + "ld r17, x+ \n\t" \ + "ld r18, x+ \n\t" \ + "ld r19, x+ \n\t" \ + "ld r20, x+ \n\t" \ + "ld r21, x+ \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r2, r2 \n\t" \ + "st z+, r0 \n\t" \ + "mov r22, r1 \n\t" \ + \ + "ldi r24, 0 \n\t" \ + "mul r2, r3 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "add r22, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r24, r25 \n\t" \ + "st z+, r22 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r2, r4 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r3 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r2, r5 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r3, r4 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r2, r6 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r3, r5 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r4, r4 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r28 \n\t" \ + "adc r24, r29 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r2, r7 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r3, r6 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r4, r5 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r2, r8 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r3, r7 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r6 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r5, r5 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r28 \n\t" \ + "adc r24, r29 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r2, r9 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r3, r8 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r4, r7 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r5, r6 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r2, r10 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r3, r9 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r8 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r7 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r6, r6 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r28 \n\t" \ + "adc r24, r29 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r2, r11 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r3, r10 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r4, r9 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r5, r8 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r6, r7 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r2, r12 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r3, r11 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r10 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r9 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r8 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r7, r7 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r28 \n\t" \ + "adc r24, r29 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r2, r13 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r3, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r4, r11 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r5, r10 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r6, r9 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r7, r8 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r2, r14 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r3, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r11 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r10 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r9 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r8, r8 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r28 \n\t" \ + "adc r24, r29 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r2, r15 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r3, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r4, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r5, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r6, r11 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r7, r10 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r8, r9 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r2, r16 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r3, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r11 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r10 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r9, r9 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r28 \n\t" \ + "adc r24, r29 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r2, r17 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r3, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r4, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r5, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r6, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r7, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r8, r11 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r9, r10 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r2, r18 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r3, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r11 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r10, r10 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r28 \n\t" \ + "adc r24, r29 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r2, r19 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r3, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r4, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r5, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r6, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r7, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r8, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r9, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r10, r11 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r2, r20 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r3, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r11, r11 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r28 \n\t" \ + "adc r24, r29 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r2, r21 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r3, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r4, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r5, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r6, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r7, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r8, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r9, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r10, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r11, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r2, x+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r3, r21 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r4, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r12, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r28 \n\t" \ + "adc r24, r29 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r3, r2 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r4, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r5, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r6, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r7, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r8, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r9, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r10, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r11, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r12, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r25 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r3, x+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r4, r2 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r5, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r12, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r13, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r28 \n\t" \ + "adc r24, r29 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r4, r3 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r5, r2 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r6, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r7, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r8, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r9, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r10, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r11, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r12, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r13, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r25 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r4, x+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r5, r3 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r6, r2 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r12, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r13, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r14, r14 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r28 \n\t" \ + "adc r24, r29 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r5, r4 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r6, r3 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r7, r2 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r8, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r9, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r10, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r11, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r12, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r13, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r14, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r25 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r5, x+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r6, r4 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r7, r3 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r2 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r12, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r13, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r14, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r15, r15 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r28 \n\t" \ + "adc r24, r29 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r6, r5 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r7, r4 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r8, r3 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r9, r2 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r10, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r11, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r12, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r13, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r14, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r15, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r25 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r6, x+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r7, r5 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r8, r4 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r3 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r2 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r12, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r13, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r14, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r15, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r16, r16 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r28 \n\t" \ + "adc r24, r29 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r7, r6 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r8, r5 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r9, r4 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r10, r3 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r11, r2 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r12, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r13, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r14, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r15, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r16, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r25 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r7, x+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r8, r6 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r9, r5 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r10, r4 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r3 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r12, r2 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r13, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r14, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r15, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r16, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r17, r17 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r28 \n\t" \ + "adc r24, r29 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r8, r7 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r9, r6 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r10, r5 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r11, r4 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r12, r3 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r13, r2 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r14, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r15, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r16, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r17, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r25 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r8, x+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r9, r7 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r10, r6 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r11, r5 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r12, r4 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r13, r3 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r14, r2 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r15, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r16, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r17, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r18, r18 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r28 \n\t" \ + "adc r24, r29 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r9, r8 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r10, r7 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r11, r6 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r12, r5 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r13, r4 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r14, r3 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r15, r2 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r16, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r17, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r18, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r25 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r9, x+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r10, r8 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r11, r7 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r12, r6 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r13, r5 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r14, r4 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r15, r3 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r16, r2 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r17, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r18, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r19, r19 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r28 \n\t" \ + "adc r24, r29 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r10, r9 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r11, r8 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r12, r7 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r13, r6 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r14, r5 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r15, r4 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r16, r3 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r17, r2 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r18, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r19, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r25 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r10, x+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r11, r9 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r12, r8 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r13, r7 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r14, r6 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r15, r5 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r16, r4 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r17, r3 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r18, r2 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r19, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r20, r20 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r28 \n\t" \ + "adc r24, r29 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r11, r10 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r12, r9 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r13, r8 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r14, r7 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r15, r6 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r16, r5 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r17, r4 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r18, r3 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r19, r2 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r20, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r25 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r11, x+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r12, r10 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r13, r9 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r14, r8 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r15, r7 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r16, r6 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r17, r5 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r18, r4 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r19, r3 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r20, r2 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r21, r21 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r28 \n\t" \ + "adc r24, r29 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r12, r11 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r13, r10 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r14, r9 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r15, r8 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r16, r7 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r17, r6 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r18, r5 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r19, r4 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r20, r3 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r21, r2 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r25 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r12, x+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r13, r11 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r14, r10 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r15, r9 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r16, r8 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r17, r7 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r18, r6 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r19, r5 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r20, r4 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r21, r3 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r2, r2 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r28 \n\t" \ + "adc r24, r29 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r13, r12 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r14, r11 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r15, r10 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r16, r9 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r17, r8 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r18, r7 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r19, r6 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r20, r5 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r21, r4 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r2, r3 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r25 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ld r13, x+ \n\t" \ + "ldi r22, 0 \n\t" \ + "mul r14, r12 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r15, r11 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r16, r10 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r17, r9 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r18, r8 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r19, r7 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r20, r6 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r21, r5 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r4 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r25 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r3, r3 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r28 \n\t" \ + "adc r24, r29 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r14, r13 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r15, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r16, r11 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r17, r10 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r18, r9 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r19, r8 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r20, r7 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r21, r6 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r2, r5 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r3, r4 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "ld r0, z \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r25 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r15, r13 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r16, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r17, r11 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r18, r10 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r19, r9 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r20, r8 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r21, r7 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r6 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r5 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r4, r4 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r28 \n\t" \ + "adc r24, r29 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r16, r13 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r17, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r18, r11 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r19, r10 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r20, r9 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r21, r8 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r2, r7 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r3, r6 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r4, r5 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r17, r13 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r18, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r19, r11 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r20, r10 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r21, r9 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r8 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r7 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r6 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r5, r5 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r28 \n\t" \ + "adc r24, r29 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r18, r13 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r19, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r20, r11 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r21, r10 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r2, r9 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r3, r8 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r4, r7 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r5, r6 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r19, r13 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r20, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r21, r11 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r2, r10 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r9 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r8 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r7 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r6, r6 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r28 \n\t" \ + "adc r24, r29 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r20, r13 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r21, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r2, r11 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r3, r10 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r4, r9 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r5, r8 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r6, r7 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r21, r13 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r2, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r3, r11 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r4, r10 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r9 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r8 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r7, r7 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r28 \n\t" \ + "adc r24, r29 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r2, r13 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r3, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r4, r11 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r5, r10 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r6, r9 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r7, r8 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r3, r13 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r4, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r5, r11 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r6, r10 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r9 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r8, r8 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r28 \n\t" \ + "adc r24, r29 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r4, r13 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r5, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r6, r11 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r7, r10 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r8, r9 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r5, r13 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r6, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r7, r11 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r8, r10 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r9, r9 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r28 \n\t" \ + "adc r24, r29 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r6, r13 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r7, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r8, r11 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r9, r10 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r7, r13 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r8, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "mul r9, r11 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r10, r10 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r28 \n\t" \ + "adc r24, r29 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r8, r13 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r9, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "mul r10, r11 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r22, 0 \n\t" \ + "mul r9, r13 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r24, r1 \n\t" \ + "mul r10, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r24 \n\t" \ + "rol r22 \n\t" \ + "mul r11, r11 \n\t" \ + "add r23, r0 \n\t" \ + "adc r24, r1 \n\t" \ + "adc r22, r25 \n\t" \ + "add r23, r28 \n\t" \ + "adc r24, r29 \n\t" \ + "adc r22, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r29, 0 \n\t" \ + "mul r10, r13 \n\t" \ + "mov r23, r0 \n\t" \ + "mov r28, r1 \n\t" \ + "mul r11, r12 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "adc r29, r25 \n\t" \ + "lsl r23 \n\t" \ + "rol r28 \n\t" \ + "rol r29 \n\t" \ + "add r23, r24 \n\t" \ + "adc r28, r22 \n\t" \ + "adc r29, r25 \n\t" \ + "st z+, r23 \n\t" \ + \ + "ldi r23, 0 \n\t" \ + "mul r11, r13 \n\t" \ + "add r28, r0 \n\t" \ + "adc r29, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "add r28, r0 \n\t" \ + "adc r29, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "mul r12, r12 \n\t" \ + "add r28, r0 \n\t" \ + "adc r29, r1 \n\t" \ + "adc r23, r25 \n\t" \ + "st z+, r28 \n\t" \ + \ + "ldi r28, 0 \n\t" \ + "mul r12, r13 \n\t" \ + "add r29, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r28, r25 \n\t" \ + "add r29, r0 \n\t" \ + "adc r23, r1 \n\t" \ + "adc r28, r25 \n\t" \ + "st z+, r29 \n\t" \ + \ + "mul r13, r13 \n\t" \ + "add r23, r0 \n\t" \ + "adc r28, r1 \n\t" \ + "st z+, r23 \n\t" \ + "st z+, r28 \n\t" + +#endif /* _UECC_ASM_AVR_MULT_SQUARE_H_ */ diff --git a/deps/picoquic/picotls/deps/micro-ecc/curve-specific.inc b/deps/picoquic/picotls/deps/micro-ecc/curve-specific.inc new file mode 100644 index 0000000..0453b21 --- /dev/null +++ b/deps/picoquic/picotls/deps/micro-ecc/curve-specific.inc @@ -0,0 +1,1248 @@ +/* Copyright 2015, Kenneth MacKay. Licensed under the BSD 2-clause license. */ + +#ifndef _UECC_CURVE_SPECIFIC_H_ +#define _UECC_CURVE_SPECIFIC_H_ + +#define num_bytes_secp160r1 20 +#define num_bytes_secp192r1 24 +#define num_bytes_secp224r1 28 +#define num_bytes_secp256r1 32 +#define num_bytes_secp256k1 32 + +#if (uECC_WORD_SIZE == 1) + +#define num_words_secp160r1 20 +#define num_words_secp192r1 24 +#define num_words_secp224r1 28 +#define num_words_secp256r1 32 +#define num_words_secp256k1 32 + +#define BYTES_TO_WORDS_8(a, b, c, d, e, f, g, h) \ + 0x##a, 0x##b, 0x##c, 0x##d, 0x##e, 0x##f, 0x##g, 0x##h +#define BYTES_TO_WORDS_4(a, b, c, d) 0x##a, 0x##b, 0x##c, 0x##d + +#elif (uECC_WORD_SIZE == 4) + +#define num_words_secp160r1 5 +#define num_words_secp192r1 6 +#define num_words_secp224r1 7 +#define num_words_secp256r1 8 +#define num_words_secp256k1 8 + +#define BYTES_TO_WORDS_8(a, b, c, d, e, f, g, h) 0x##d##c##b##a, 0x##h##g##f##e +#define BYTES_TO_WORDS_4(a, b, c, d) 0x##d##c##b##a + +#elif (uECC_WORD_SIZE == 8) + +#define num_words_secp160r1 3 +#define num_words_secp192r1 3 +#define num_words_secp224r1 4 +#define num_words_secp256r1 4 +#define num_words_secp256k1 4 + +#define BYTES_TO_WORDS_8(a, b, c, d, e, f, g, h) 0x##h##g##f##e##d##c##b##a##ull +#define BYTES_TO_WORDS_4(a, b, c, d) 0x##d##c##b##a##ull + +#endif /* uECC_WORD_SIZE */ + +#if uECC_SUPPORTS_secp160r1 || uECC_SUPPORTS_secp192r1 || \ + uECC_SUPPORTS_secp224r1 || uECC_SUPPORTS_secp256r1 +static void double_jacobian_default(uECC_word_t * X1, + uECC_word_t * Y1, + uECC_word_t * Z1, + uECC_Curve curve) { + /* t1 = X, t2 = Y, t3 = Z */ + uECC_word_t t4[uECC_MAX_WORDS]; + uECC_word_t t5[uECC_MAX_WORDS]; + wordcount_t num_words = curve->num_words; + + if (uECC_vli_isZero(Z1, num_words)) { + return; + } + + uECC_vli_modSquare_fast(t4, Y1, curve); /* t4 = y1^2 */ + uECC_vli_modMult_fast(t5, X1, t4, curve); /* t5 = x1*y1^2 = A */ + uECC_vli_modSquare_fast(t4, t4, curve); /* t4 = y1^4 */ + uECC_vli_modMult_fast(Y1, Y1, Z1, curve); /* t2 = y1*z1 = z3 */ + uECC_vli_modSquare_fast(Z1, Z1, curve); /* t3 = z1^2 */ + + uECC_vli_modAdd(X1, X1, Z1, curve->p, num_words); /* t1 = x1 + z1^2 */ + uECC_vli_modAdd(Z1, Z1, Z1, curve->p, num_words); /* t3 = 2*z1^2 */ + uECC_vli_modSub(Z1, X1, Z1, curve->p, num_words); /* t3 = x1 - z1^2 */ + uECC_vli_modMult_fast(X1, X1, Z1, curve); /* t1 = x1^2 - z1^4 */ + + uECC_vli_modAdd(Z1, X1, X1, curve->p, num_words); /* t3 = 2*(x1^2 - z1^4) */ + uECC_vli_modAdd(X1, X1, Z1, curve->p, num_words); /* t1 = 3*(x1^2 - z1^4) */ + if (uECC_vli_testBit(X1, 0)) { + uECC_word_t l_carry = uECC_vli_add(X1, X1, curve->p, num_words); + uECC_vli_rshift1(X1, num_words); + X1[num_words - 1] |= l_carry << (uECC_WORD_BITS - 1); + } else { + uECC_vli_rshift1(X1, num_words); + } + /* t1 = 3/2*(x1^2 - z1^4) = B */ + + uECC_vli_modSquare_fast(Z1, X1, curve); /* t3 = B^2 */ + uECC_vli_modSub(Z1, Z1, t5, curve->p, num_words); /* t3 = B^2 - A */ + uECC_vli_modSub(Z1, Z1, t5, curve->p, num_words); /* t3 = B^2 - 2A = x3 */ + uECC_vli_modSub(t5, t5, Z1, curve->p, num_words); /* t5 = A - x3 */ + uECC_vli_modMult_fast(X1, X1, t5, curve); /* t1 = B * (A - x3) */ + uECC_vli_modSub(t4, X1, t4, curve->p, num_words); /* t4 = B * (A - x3) - y1^4 = y3 */ + + uECC_vli_set(X1, Z1, num_words); + uECC_vli_set(Z1, Y1, num_words); + uECC_vli_set(Y1, t4, num_words); +} + +/* Computes result = x^3 + ax + b. result must not overlap x. */ +static void x_side_default(uECC_word_t *result, const uECC_word_t *x, uECC_Curve curve) { + uECC_word_t _3[uECC_MAX_WORDS] = {3}; /* -a = 3 */ + wordcount_t num_words = curve->num_words; + + uECC_vli_modSquare_fast(result, x, curve); /* r = x^2 */ + uECC_vli_modSub(result, result, _3, curve->p, num_words); /* r = x^2 - 3 */ + uECC_vli_modMult_fast(result, result, x, curve); /* r = x^3 - 3x */ + uECC_vli_modAdd(result, result, curve->b, curve->p, num_words); /* r = x^3 - 3x + b */ +} +#endif /* uECC_SUPPORTS_secp... */ + +#if uECC_SUPPORT_COMPRESSED_POINT +#if uECC_SUPPORTS_secp160r1 || uECC_SUPPORTS_secp192r1 || \ + uECC_SUPPORTS_secp256r1 || uECC_SUPPORTS_secp256k1 +/* Compute a = sqrt(a) (mod curve_p). */ +static void mod_sqrt_default(uECC_word_t *a, uECC_Curve curve) { + bitcount_t i; + uECC_word_t p1[uECC_MAX_WORDS] = {1}; + uECC_word_t l_result[uECC_MAX_WORDS] = {1}; + wordcount_t num_words = curve->num_words; + + /* When curve->p == 3 (mod 4), we can compute + sqrt(a) = a^((curve->p + 1) / 4) (mod curve->p). */ + uECC_vli_add(p1, curve->p, p1, num_words); /* p1 = curve_p + 1 */ + for (i = uECC_vli_numBits(p1, num_words) - 1; i > 1; --i) { + uECC_vli_modSquare_fast(l_result, l_result, curve); + if (uECC_vli_testBit(p1, i)) { + uECC_vli_modMult_fast(l_result, l_result, a, curve); + } + } + uECC_vli_set(a, l_result, num_words); +} +#endif /* uECC_SUPPORTS_secp... */ +#endif /* uECC_SUPPORT_COMPRESSED_POINT */ + +#if uECC_SUPPORTS_secp160r1 + +#if (uECC_OPTIMIZATION_LEVEL > 0) +static void vli_mmod_fast_secp160r1(uECC_word_t *result, uECC_word_t *product); +#endif + +static const struct uECC_Curve_t curve_secp160r1 = { + num_words_secp160r1, + num_bytes_secp160r1, + 161, /* num_n_bits */ + { BYTES_TO_WORDS_8(FF, FF, FF, 7F, FF, FF, FF, FF), + BYTES_TO_WORDS_8(FF, FF, FF, FF, FF, FF, FF, FF), + BYTES_TO_WORDS_4(FF, FF, FF, FF) }, + { BYTES_TO_WORDS_8(57, 22, 75, CA, D3, AE, 27, F9), + BYTES_TO_WORDS_8(C8, F4, 01, 00, 00, 00, 00, 00), + BYTES_TO_WORDS_8(00, 00, 00, 00, 01, 00, 00, 00) }, + { BYTES_TO_WORDS_8(82, FC, CB, 13, B9, 8B, C3, 68), + BYTES_TO_WORDS_8(89, 69, 64, 46, 28, 73, F5, 8E), + BYTES_TO_WORDS_4(68, B5, 96, 4A), + + BYTES_TO_WORDS_8(32, FB, C5, 7A, 37, 51, 23, 04), + BYTES_TO_WORDS_8(12, C9, DC, 59, 7D, 94, 68, 31), + BYTES_TO_WORDS_4(55, 28, A6, 23) }, + { BYTES_TO_WORDS_8(45, FA, 65, C5, AD, D4, D4, 81), + BYTES_TO_WORDS_8(9F, F8, AC, 65, 8B, 7A, BD, 54), + BYTES_TO_WORDS_4(FC, BE, 97, 1C) }, + &double_jacobian_default, +#if uECC_SUPPORT_COMPRESSED_POINT + &mod_sqrt_default, +#endif + &x_side_default, +#if (uECC_OPTIMIZATION_LEVEL > 0) + &vli_mmod_fast_secp160r1 +#endif +}; + +uECC_Curve uECC_secp160r1(void) { return &curve_secp160r1; } + +#if (uECC_OPTIMIZATION_LEVEL > 0 && !asm_mmod_fast_secp160r1) +/* Computes result = product % curve_p + see http://www.isys.uni-klu.ac.at/PDF/2001-0126-MT.pdf page 354 + + Note that this only works if log2(omega) < log2(p) / 2 */ +static void omega_mult_secp160r1(uECC_word_t *result, const uECC_word_t *right); +#if uECC_WORD_SIZE == 8 +static void vli_mmod_fast_secp160r1(uECC_word_t *result, uECC_word_t *product) { + uECC_word_t tmp[2 * num_words_secp160r1]; + uECC_word_t copy; + + uECC_vli_clear(tmp, num_words_secp160r1); + uECC_vli_clear(tmp + num_words_secp160r1, num_words_secp160r1); + + omega_mult_secp160r1(tmp, product + num_words_secp160r1 - 1); /* (Rq, q) = q * c */ + + product[num_words_secp160r1 - 1] &= 0xffffffff; + copy = tmp[num_words_secp160r1 - 1]; + tmp[num_words_secp160r1 - 1] &= 0xffffffff; + uECC_vli_add(result, product, tmp, num_words_secp160r1); /* (C, r) = r + q */ + uECC_vli_clear(product, num_words_secp160r1); + tmp[num_words_secp160r1 - 1] = copy; + omega_mult_secp160r1(product, tmp + num_words_secp160r1 - 1); /* Rq*c */ + uECC_vli_add(result, result, product, num_words_secp160r1); /* (C1, r) = r + Rq*c */ + + while (uECC_vli_cmp_unsafe(result, curve_secp160r1.p, num_words_secp160r1) > 0) { + uECC_vli_sub(result, result, curve_secp160r1.p, num_words_secp160r1); + } +} + +static void omega_mult_secp160r1(uint64_t *result, const uint64_t *right) { + uint32_t carry; + unsigned i; + + /* Multiply by (2^31 + 1). */ + carry = 0; + for (i = 0; i < num_words_secp160r1; ++i) { + uint64_t tmp = (right[i] >> 32) | (right[i + 1] << 32); + result[i] = (tmp << 31) + tmp + carry; + carry = (tmp >> 33) + (result[i] < tmp || (carry && result[i] == tmp)); + } + result[i] = carry; +} +#else +static void vli_mmod_fast_secp160r1(uECC_word_t *result, uECC_word_t *product) { + uECC_word_t tmp[2 * num_words_secp160r1]; + uECC_word_t carry; + + uECC_vli_clear(tmp, num_words_secp160r1); + uECC_vli_clear(tmp + num_words_secp160r1, num_words_secp160r1); + + omega_mult_secp160r1(tmp, product + num_words_secp160r1); /* (Rq, q) = q * c */ + + carry = uECC_vli_add(result, product, tmp, num_words_secp160r1); /* (C, r) = r + q */ + uECC_vli_clear(product, num_words_secp160r1); + omega_mult_secp160r1(product, tmp + num_words_secp160r1); /* Rq*c */ + carry += uECC_vli_add(result, result, product, num_words_secp160r1); /* (C1, r) = r + Rq*c */ + + while (carry > 0) { + --carry; + uECC_vli_sub(result, result, curve_secp160r1.p, num_words_secp160r1); + } + if (uECC_vli_cmp_unsafe(result, curve_secp160r1.p, num_words_secp160r1) > 0) { + uECC_vli_sub(result, result, curve_secp160r1.p, num_words_secp160r1); + } +} +#endif + +#if uECC_WORD_SIZE == 1 +static void omega_mult_secp160r1(uint8_t *result, const uint8_t *right) { + uint8_t carry; + uint8_t i; + + /* Multiply by (2^31 + 1). */ + uECC_vli_set(result + 4, right, num_words_secp160r1); /* 2^32 */ + uECC_vli_rshift1(result + 4, num_words_secp160r1); /* 2^31 */ + result[3] = right[0] << 7; /* get last bit from shift */ + + carry = uECC_vli_add(result, result, right, num_words_secp160r1); /* 2^31 + 1 */ + for (i = num_words_secp160r1; carry; ++i) { + uint16_t sum = (uint16_t)result[i] + carry; + result[i] = (uint8_t)sum; + carry = sum >> 8; + } +} +#elif uECC_WORD_SIZE == 4 +static void omega_mult_secp160r1(uint32_t *result, const uint32_t *right) { + uint32_t carry; + unsigned i; + + /* Multiply by (2^31 + 1). */ + uECC_vli_set(result + 1, right, num_words_secp160r1); /* 2^32 */ + uECC_vli_rshift1(result + 1, num_words_secp160r1); /* 2^31 */ + result[0] = right[0] << 31; /* get last bit from shift */ + + carry = uECC_vli_add(result, result, right, num_words_secp160r1); /* 2^31 + 1 */ + for (i = num_words_secp160r1; carry; ++i) { + uint64_t sum = (uint64_t)result[i] + carry; + result[i] = (uint32_t)sum; + carry = sum >> 32; + } +} +#endif /* uECC_WORD_SIZE */ +#endif /* (uECC_OPTIMIZATION_LEVEL > 0 && !asm_mmod_fast_secp160r1) */ + +#endif /* uECC_SUPPORTS_secp160r1 */ + +#if uECC_SUPPORTS_secp192r1 + +#if (uECC_OPTIMIZATION_LEVEL > 0) +static void vli_mmod_fast_secp192r1(uECC_word_t *result, uECC_word_t *product); +#endif + +static const struct uECC_Curve_t curve_secp192r1 = { + num_words_secp192r1, + num_bytes_secp192r1, + 192, /* num_n_bits */ + { BYTES_TO_WORDS_8(FF, FF, FF, FF, FF, FF, FF, FF), + BYTES_TO_WORDS_8(FE, FF, FF, FF, FF, FF, FF, FF), + BYTES_TO_WORDS_8(FF, FF, FF, FF, FF, FF, FF, FF) }, + { BYTES_TO_WORDS_8(31, 28, D2, B4, B1, C9, 6B, 14), + BYTES_TO_WORDS_8(36, F8, DE, 99, FF, FF, FF, FF), + BYTES_TO_WORDS_8(FF, FF, FF, FF, FF, FF, FF, FF) }, + { BYTES_TO_WORDS_8(12, 10, FF, 82, FD, 0A, FF, F4), + BYTES_TO_WORDS_8(00, 88, A1, 43, EB, 20, BF, 7C), + BYTES_TO_WORDS_8(F6, 90, 30, B0, 0E, A8, 8D, 18), + + BYTES_TO_WORDS_8(11, 48, 79, 1E, A1, 77, F9, 73), + BYTES_TO_WORDS_8(D5, CD, 24, 6B, ED, 11, 10, 63), + BYTES_TO_WORDS_8(78, DA, C8, FF, 95, 2B, 19, 07) }, + { BYTES_TO_WORDS_8(B1, B9, 46, C1, EC, DE, B8, FE), + BYTES_TO_WORDS_8(49, 30, 24, 72, AB, E9, A7, 0F), + BYTES_TO_WORDS_8(E7, 80, 9C, E5, 19, 05, 21, 64) }, + &double_jacobian_default, +#if uECC_SUPPORT_COMPRESSED_POINT + &mod_sqrt_default, +#endif + &x_side_default, +#if (uECC_OPTIMIZATION_LEVEL > 0) + &vli_mmod_fast_secp192r1 +#endif +}; + +uECC_Curve uECC_secp192r1(void) { return &curve_secp192r1; } + +#if (uECC_OPTIMIZATION_LEVEL > 0) +/* Computes result = product % curve_p. + See algorithm 5 and 6 from http://www.isys.uni-klu.ac.at/PDF/2001-0126-MT.pdf */ +#if uECC_WORD_SIZE == 1 +static void vli_mmod_fast_secp192r1(uint8_t *result, uint8_t *product) { + uint8_t tmp[num_words_secp192r1]; + uint8_t carry; + + uECC_vli_set(result, product, num_words_secp192r1); + + uECC_vli_set(tmp, &product[24], num_words_secp192r1); + carry = uECC_vli_add(result, result, tmp, num_words_secp192r1); + + tmp[0] = tmp[1] = tmp[2] = tmp[3] = tmp[4] = tmp[5] = tmp[6] = tmp[7] = 0; + tmp[8] = product[24]; tmp[9] = product[25]; tmp[10] = product[26]; tmp[11] = product[27]; + tmp[12] = product[28]; tmp[13] = product[29]; tmp[14] = product[30]; tmp[15] = product[31]; + tmp[16] = product[32]; tmp[17] = product[33]; tmp[18] = product[34]; tmp[19] = product[35]; + tmp[20] = product[36]; tmp[21] = product[37]; tmp[22] = product[38]; tmp[23] = product[39]; + carry += uECC_vli_add(result, result, tmp, num_words_secp192r1); + + tmp[0] = tmp[8] = product[40]; + tmp[1] = tmp[9] = product[41]; + tmp[2] = tmp[10] = product[42]; + tmp[3] = tmp[11] = product[43]; + tmp[4] = tmp[12] = product[44]; + tmp[5] = tmp[13] = product[45]; + tmp[6] = tmp[14] = product[46]; + tmp[7] = tmp[15] = product[47]; + tmp[16] = tmp[17] = tmp[18] = tmp[19] = tmp[20] = tmp[21] = tmp[22] = tmp[23] = 0; + carry += uECC_vli_add(result, result, tmp, num_words_secp192r1); + + while (carry || uECC_vli_cmp_unsafe(curve_secp192r1.p, result, num_words_secp192r1) != 1) { + carry -= uECC_vli_sub(result, result, curve_secp192r1.p, num_words_secp192r1); + } +} +#elif uECC_WORD_SIZE == 4 +static void vli_mmod_fast_secp192r1(uint32_t *result, uint32_t *product) { + uint32_t tmp[num_words_secp192r1]; + int carry; + + uECC_vli_set(result, product, num_words_secp192r1); + + uECC_vli_set(tmp, &product[6], num_words_secp192r1); + carry = uECC_vli_add(result, result, tmp, num_words_secp192r1); + + tmp[0] = tmp[1] = 0; + tmp[2] = product[6]; + tmp[3] = product[7]; + tmp[4] = product[8]; + tmp[5] = product[9]; + carry += uECC_vli_add(result, result, tmp, num_words_secp192r1); + + tmp[0] = tmp[2] = product[10]; + tmp[1] = tmp[3] = product[11]; + tmp[4] = tmp[5] = 0; + carry += uECC_vli_add(result, result, tmp, num_words_secp192r1); + + while (carry || uECC_vli_cmp_unsafe(curve_secp192r1.p, result, num_words_secp192r1) != 1) { + carry -= uECC_vli_sub(result, result, curve_secp192r1.p, num_words_secp192r1); + } +} +#else +static void vli_mmod_fast_secp192r1(uint64_t *result, uint64_t *product) { + uint64_t tmp[num_words_secp192r1]; + int carry; + + uECC_vli_set(result, product, num_words_secp192r1); + + uECC_vli_set(tmp, &product[3], num_words_secp192r1); + carry = (int)uECC_vli_add(result, result, tmp, num_words_secp192r1); + + tmp[0] = 0; + tmp[1] = product[3]; + tmp[2] = product[4]; + carry += uECC_vli_add(result, result, tmp, num_words_secp192r1); + + tmp[0] = tmp[1] = product[5]; + tmp[2] = 0; + carry += uECC_vli_add(result, result, tmp, num_words_secp192r1); + + while (carry || uECC_vli_cmp_unsafe(curve_secp192r1.p, result, num_words_secp192r1) != 1) { + carry -= uECC_vli_sub(result, result, curve_secp192r1.p, num_words_secp192r1); + } +} +#endif /* uECC_WORD_SIZE */ +#endif /* (uECC_OPTIMIZATION_LEVEL > 0) */ + +#endif /* uECC_SUPPORTS_secp192r1 */ + +#if uECC_SUPPORTS_secp224r1 + +#if uECC_SUPPORT_COMPRESSED_POINT +static void mod_sqrt_secp224r1(uECC_word_t *a, uECC_Curve curve); +#endif +#if (uECC_OPTIMIZATION_LEVEL > 0) +static void vli_mmod_fast_secp224r1(uECC_word_t *result, uECC_word_t *product); +#endif + +static const struct uECC_Curve_t curve_secp224r1 = { + num_words_secp224r1, + num_bytes_secp224r1, + 224, /* num_n_bits */ + { BYTES_TO_WORDS_8(01, 00, 00, 00, 00, 00, 00, 00), + BYTES_TO_WORDS_8(00, 00, 00, 00, FF, FF, FF, FF), + BYTES_TO_WORDS_8(FF, FF, FF, FF, FF, FF, FF, FF), + BYTES_TO_WORDS_4(FF, FF, FF, FF) }, + { BYTES_TO_WORDS_8(3D, 2A, 5C, 5C, 45, 29, DD, 13), + BYTES_TO_WORDS_8(3E, F0, B8, E0, A2, 16, FF, FF), + BYTES_TO_WORDS_8(FF, FF, FF, FF, FF, FF, FF, FF), + BYTES_TO_WORDS_4(FF, FF, FF, FF) }, + { BYTES_TO_WORDS_8(21, 1D, 5C, 11, D6, 80, 32, 34), + BYTES_TO_WORDS_8(22, 11, C2, 56, D3, C1, 03, 4A), + BYTES_TO_WORDS_8(B9, 90, 13, 32, 7F, BF, B4, 6B), + BYTES_TO_WORDS_4(BD, 0C, 0E, B7), + + BYTES_TO_WORDS_8(34, 7E, 00, 85, 99, 81, D5, 44), + BYTES_TO_WORDS_8(64, 47, 07, 5A, A0, 75, 43, CD), + BYTES_TO_WORDS_8(E6, DF, 22, 4C, FB, 23, F7, B5), + BYTES_TO_WORDS_4(88, 63, 37, BD) }, + { BYTES_TO_WORDS_8(B4, FF, 55, 23, 43, 39, 0B, 27), + BYTES_TO_WORDS_8(BA, D8, BF, D7, B7, B0, 44, 50), + BYTES_TO_WORDS_8(56, 32, 41, F5, AB, B3, 04, 0C), + BYTES_TO_WORDS_4(85, 0A, 05, B4) }, + &double_jacobian_default, +#if uECC_SUPPORT_COMPRESSED_POINT + &mod_sqrt_secp224r1, +#endif + &x_side_default, +#if (uECC_OPTIMIZATION_LEVEL > 0) + &vli_mmod_fast_secp224r1 +#endif +}; + +uECC_Curve uECC_secp224r1(void) { return &curve_secp224r1; } + + +#if uECC_SUPPORT_COMPRESSED_POINT +/* Routine 3.2.4 RS; from http://www.nsa.gov/ia/_files/nist-routines.pdf */ +static void mod_sqrt_secp224r1_rs(uECC_word_t *d1, + uECC_word_t *e1, + uECC_word_t *f1, + const uECC_word_t *d0, + const uECC_word_t *e0, + const uECC_word_t *f0) { + uECC_word_t t[num_words_secp224r1]; + + uECC_vli_modSquare_fast(t, d0, &curve_secp224r1); /* t <-- d0 ^ 2 */ + uECC_vli_modMult_fast(e1, d0, e0, &curve_secp224r1); /* e1 <-- d0 * e0 */ + uECC_vli_modAdd(d1, t, f0, curve_secp224r1.p, num_words_secp224r1); /* d1 <-- t + f0 */ + uECC_vli_modAdd(e1, e1, e1, curve_secp224r1.p, num_words_secp224r1); /* e1 <-- e1 + e1 */ + uECC_vli_modMult_fast(f1, t, f0, &curve_secp224r1); /* f1 <-- t * f0 */ + uECC_vli_modAdd(f1, f1, f1, curve_secp224r1.p, num_words_secp224r1); /* f1 <-- f1 + f1 */ + uECC_vli_modAdd(f1, f1, f1, curve_secp224r1.p, num_words_secp224r1); /* f1 <-- f1 + f1 */ +} + +/* Routine 3.2.5 RSS; from http://www.nsa.gov/ia/_files/nist-routines.pdf */ +static void mod_sqrt_secp224r1_rss(uECC_word_t *d1, + uECC_word_t *e1, + uECC_word_t *f1, + const uECC_word_t *d0, + const uECC_word_t *e0, + const uECC_word_t *f0, + const bitcount_t j) { + bitcount_t i; + + uECC_vli_set(d1, d0, num_words_secp224r1); /* d1 <-- d0 */ + uECC_vli_set(e1, e0, num_words_secp224r1); /* e1 <-- e0 */ + uECC_vli_set(f1, f0, num_words_secp224r1); /* f1 <-- f0 */ + for (i = 1; i <= j; i++) { + mod_sqrt_secp224r1_rs(d1, e1, f1, d1, e1, f1); /* RS (d1,e1,f1,d1,e1,f1) */ + } +} + +/* Routine 3.2.6 RM; from http://www.nsa.gov/ia/_files/nist-routines.pdf */ +static void mod_sqrt_secp224r1_rm(uECC_word_t *d2, + uECC_word_t *e2, + uECC_word_t *f2, + const uECC_word_t *c, + const uECC_word_t *d0, + const uECC_word_t *e0, + const uECC_word_t *d1, + const uECC_word_t *e1) { + uECC_word_t t1[num_words_secp224r1]; + uECC_word_t t2[num_words_secp224r1]; + + uECC_vli_modMult_fast(t1, e0, e1, &curve_secp224r1); /* t1 <-- e0 * e1 */ + uECC_vli_modMult_fast(t1, t1, c, &curve_secp224r1); /* t1 <-- t1 * c */ + /* t1 <-- p - t1 */ + uECC_vli_modSub(t1, curve_secp224r1.p, t1, curve_secp224r1.p, num_words_secp224r1); + uECC_vli_modMult_fast(t2, d0, d1, &curve_secp224r1); /* t2 <-- d0 * d1 */ + uECC_vli_modAdd(t2, t2, t1, curve_secp224r1.p, num_words_secp224r1); /* t2 <-- t2 + t1 */ + uECC_vli_modMult_fast(t1, d0, e1, &curve_secp224r1); /* t1 <-- d0 * e1 */ + uECC_vli_modMult_fast(e2, d1, e0, &curve_secp224r1); /* e2 <-- d1 * e0 */ + uECC_vli_modAdd(e2, e2, t1, curve_secp224r1.p, num_words_secp224r1); /* e2 <-- e2 + t1 */ + uECC_vli_modSquare_fast(f2, e2, &curve_secp224r1); /* f2 <-- e2^2 */ + uECC_vli_modMult_fast(f2, f2, c, &curve_secp224r1); /* f2 <-- f2 * c */ + /* f2 <-- p - f2 */ + uECC_vli_modSub(f2, curve_secp224r1.p, f2, curve_secp224r1.p, num_words_secp224r1); + uECC_vli_set(d2, t2, num_words_secp224r1); /* d2 <-- t2 */ +} + +/* Routine 3.2.7 RP; from http://www.nsa.gov/ia/_files/nist-routines.pdf */ +static void mod_sqrt_secp224r1_rp(uECC_word_t *d1, + uECC_word_t *e1, + uECC_word_t *f1, + const uECC_word_t *c, + const uECC_word_t *r) { + wordcount_t i; + wordcount_t pow2i = 1; + uECC_word_t d0[num_words_secp224r1]; + uECC_word_t e0[num_words_secp224r1] = {1}; /* e0 <-- 1 */ + uECC_word_t f0[num_words_secp224r1]; + + uECC_vli_set(d0, r, num_words_secp224r1); /* d0 <-- r */ + /* f0 <-- p - c */ + uECC_vli_modSub(f0, curve_secp224r1.p, c, curve_secp224r1.p, num_words_secp224r1); + for (i = 0; i <= 6; i++) { + mod_sqrt_secp224r1_rss(d1, e1, f1, d0, e0, f0, pow2i); /* RSS (d1,e1,f1,d0,e0,f0,2^i) */ + mod_sqrt_secp224r1_rm(d1, e1, f1, c, d1, e1, d0, e0); /* RM (d1,e1,f1,c,d1,e1,d0,e0) */ + uECC_vli_set(d0, d1, num_words_secp224r1); /* d0 <-- d1 */ + uECC_vli_set(e0, e1, num_words_secp224r1); /* e0 <-- e1 */ + uECC_vli_set(f0, f1, num_words_secp224r1); /* f0 <-- f1 */ + pow2i *= 2; + } +} + +/* Compute a = sqrt(a) (mod curve_p). */ +/* Routine 3.2.8 mp_mod_sqrt_224; from http://www.nsa.gov/ia/_files/nist-routines.pdf */ +static void mod_sqrt_secp224r1(uECC_word_t *a, uECC_Curve curve) { + bitcount_t i; + uECC_word_t e1[num_words_secp224r1]; + uECC_word_t f1[num_words_secp224r1]; + uECC_word_t d0[num_words_secp224r1]; + uECC_word_t e0[num_words_secp224r1]; + uECC_word_t f0[num_words_secp224r1]; + uECC_word_t d1[num_words_secp224r1]; + + /* s = a; using constant instead of random value */ + mod_sqrt_secp224r1_rp(d0, e0, f0, a, a); /* RP (d0, e0, f0, c, s) */ + mod_sqrt_secp224r1_rs(d1, e1, f1, d0, e0, f0); /* RS (d1, e1, f1, d0, e0, f0) */ + for (i = 1; i <= 95; i++) { + uECC_vli_set(d0, d1, num_words_secp224r1); /* d0 <-- d1 */ + uECC_vli_set(e0, e1, num_words_secp224r1); /* e0 <-- e1 */ + uECC_vli_set(f0, f1, num_words_secp224r1); /* f0 <-- f1 */ + mod_sqrt_secp224r1_rs(d1, e1, f1, d0, e0, f0); /* RS (d1, e1, f1, d0, e0, f0) */ + if (uECC_vli_isZero(d1, num_words_secp224r1)) { /* if d1 == 0 */ + break; + } + } + uECC_vli_modInv(f1, e0, curve_secp224r1.p, num_words_secp224r1); /* f1 <-- 1 / e0 */ + uECC_vli_modMult_fast(a, d0, f1, &curve_secp224r1); /* a <-- d0 / e0 */ +} +#endif /* uECC_SUPPORT_COMPRESSED_POINT */ + +#if (uECC_OPTIMIZATION_LEVEL > 0) +/* Computes result = product % curve_p + from http://www.nsa.gov/ia/_files/nist-routines.pdf */ +#if uECC_WORD_SIZE == 1 +static void vli_mmod_fast_secp224r1(uint8_t *result, uint8_t *product) { + uint8_t tmp[num_words_secp224r1]; + int8_t carry; + + /* t */ + uECC_vli_set(result, product, num_words_secp224r1); + + /* s1 */ + tmp[0] = tmp[1] = tmp[2] = tmp[3] = 0; + tmp[4] = tmp[5] = tmp[6] = tmp[7] = 0; + tmp[8] = tmp[9] = tmp[10] = tmp[11] = 0; + tmp[12] = product[28]; tmp[13] = product[29]; tmp[14] = product[30]; tmp[15] = product[31]; + tmp[16] = product[32]; tmp[17] = product[33]; tmp[18] = product[34]; tmp[19] = product[35]; + tmp[20] = product[36]; tmp[21] = product[37]; tmp[22] = product[38]; tmp[23] = product[39]; + tmp[24] = product[40]; tmp[25] = product[41]; tmp[26] = product[42]; tmp[27] = product[43]; + carry = uECC_vli_add(result, result, tmp, num_words_secp224r1); + + /* s2 */ + tmp[12] = product[44]; tmp[13] = product[45]; tmp[14] = product[46]; tmp[15] = product[47]; + tmp[16] = product[48]; tmp[17] = product[49]; tmp[18] = product[50]; tmp[19] = product[51]; + tmp[20] = product[52]; tmp[21] = product[53]; tmp[22] = product[54]; tmp[23] = product[55]; + tmp[24] = tmp[25] = tmp[26] = tmp[27] = 0; + carry += uECC_vli_add(result, result, tmp, num_words_secp224r1); + + /* d1 */ + tmp[0] = product[28]; tmp[1] = product[29]; tmp[2] = product[30]; tmp[3] = product[31]; + tmp[4] = product[32]; tmp[5] = product[33]; tmp[6] = product[34]; tmp[7] = product[35]; + tmp[8] = product[36]; tmp[9] = product[37]; tmp[10] = product[38]; tmp[11] = product[39]; + tmp[12] = product[40]; tmp[13] = product[41]; tmp[14] = product[42]; tmp[15] = product[43]; + tmp[16] = product[44]; tmp[17] = product[45]; tmp[18] = product[46]; tmp[19] = product[47]; + tmp[20] = product[48]; tmp[21] = product[49]; tmp[22] = product[50]; tmp[23] = product[51]; + tmp[24] = product[52]; tmp[25] = product[53]; tmp[26] = product[54]; tmp[27] = product[55]; + carry -= uECC_vli_sub(result, result, tmp, num_words_secp224r1); + + /* d2 */ + tmp[0] = product[44]; tmp[1] = product[45]; tmp[2] = product[46]; tmp[3] = product[47]; + tmp[4] = product[48]; tmp[5] = product[49]; tmp[6] = product[50]; tmp[7] = product[51]; + tmp[8] = product[52]; tmp[9] = product[53]; tmp[10] = product[54]; tmp[11] = product[55]; + tmp[12] = tmp[13] = tmp[14] = tmp[15] = 0; + tmp[16] = tmp[17] = tmp[18] = tmp[19] = 0; + tmp[20] = tmp[21] = tmp[22] = tmp[23] = 0; + tmp[24] = tmp[25] = tmp[26] = tmp[27] = 0; + carry -= uECC_vli_sub(result, result, tmp, num_words_secp224r1); + + if (carry < 0) { + do { + carry += uECC_vli_add(result, result, curve_secp224r1.p, num_words_secp224r1); + } while (carry < 0); + } else { + while (carry || uECC_vli_cmp_unsafe(curve_secp224r1.p, result, num_words_secp224r1) != 1) { + carry -= uECC_vli_sub(result, result, curve_secp224r1.p, num_words_secp224r1); + } + } +} +#elif uECC_WORD_SIZE == 4 +static void vli_mmod_fast_secp224r1(uint32_t *result, uint32_t *product) +{ + uint32_t tmp[num_words_secp224r1]; + int carry; + + /* t */ + uECC_vli_set(result, product, num_words_secp224r1); + + /* s1 */ + tmp[0] = tmp[1] = tmp[2] = 0; + tmp[3] = product[7]; + tmp[4] = product[8]; + tmp[5] = product[9]; + tmp[6] = product[10]; + carry = uECC_vli_add(result, result, tmp, num_words_secp224r1); + + /* s2 */ + tmp[3] = product[11]; + tmp[4] = product[12]; + tmp[5] = product[13]; + tmp[6] = 0; + carry += uECC_vli_add(result, result, tmp, num_words_secp224r1); + + /* d1 */ + tmp[0] = product[7]; + tmp[1] = product[8]; + tmp[2] = product[9]; + tmp[3] = product[10]; + tmp[4] = product[11]; + tmp[5] = product[12]; + tmp[6] = product[13]; + carry -= uECC_vli_sub(result, result, tmp, num_words_secp224r1); + + /* d2 */ + tmp[0] = product[11]; + tmp[1] = product[12]; + tmp[2] = product[13]; + tmp[3] = tmp[4] = tmp[5] = tmp[6] = 0; + carry -= uECC_vli_sub(result, result, tmp, num_words_secp224r1); + + if (carry < 0) { + do { + carry += uECC_vli_add(result, result, curve_secp224r1.p, num_words_secp224r1); + } while (carry < 0); + } else { + while (carry || uECC_vli_cmp_unsafe(curve_secp224r1.p, result, num_words_secp224r1) != 1) { + carry -= uECC_vli_sub(result, result, curve_secp224r1.p, num_words_secp224r1); + } + } +} +#else +static void vli_mmod_fast_secp224r1(uint64_t *result, uint64_t *product) +{ + uint64_t tmp[num_words_secp224r1]; + int carry = 0; + + /* t */ + uECC_vli_set(result, product, num_words_secp224r1); + result[num_words_secp224r1 - 1] &= 0xffffffff; + + /* s1 */ + tmp[0] = 0; + tmp[1] = product[3] & 0xffffffff00000000ull; + tmp[2] = product[4]; + tmp[3] = product[5] & 0xffffffff; + uECC_vli_add(result, result, tmp, num_words_secp224r1); + + /* s2 */ + tmp[1] = product[5] & 0xffffffff00000000ull; + tmp[2] = product[6]; + tmp[3] = 0; + uECC_vli_add(result, result, tmp, num_words_secp224r1); + + /* d1 */ + tmp[0] = (product[3] >> 32) | (product[4] << 32); + tmp[1] = (product[4] >> 32) | (product[5] << 32); + tmp[2] = (product[5] >> 32) | (product[6] << 32); + tmp[3] = product[6] >> 32; + carry -= uECC_vli_sub(result, result, tmp, num_words_secp224r1); + + /* d2 */ + tmp[0] = (product[5] >> 32) | (product[6] << 32); + tmp[1] = product[6] >> 32; + tmp[2] = tmp[3] = 0; + carry -= uECC_vli_sub(result, result, tmp, num_words_secp224r1); + + if (carry < 0) { + do { + carry += uECC_vli_add(result, result, curve_secp224r1.p, num_words_secp224r1); + } while (carry < 0); + } else { + while (uECC_vli_cmp_unsafe(curve_secp224r1.p, result, num_words_secp224r1) != 1) { + uECC_vli_sub(result, result, curve_secp224r1.p, num_words_secp224r1); + } + } +} +#endif /* uECC_WORD_SIZE */ +#endif /* (uECC_OPTIMIZATION_LEVEL > 0) */ + +#endif /* uECC_SUPPORTS_secp224r1 */ + +#if uECC_SUPPORTS_secp256r1 + +#if (uECC_OPTIMIZATION_LEVEL > 0) +static void vli_mmod_fast_secp256r1(uECC_word_t *result, uECC_word_t *product); +#endif + +static const struct uECC_Curve_t curve_secp256r1 = { + num_words_secp256r1, + num_bytes_secp256r1, + 256, /* num_n_bits */ + { BYTES_TO_WORDS_8(FF, FF, FF, FF, FF, FF, FF, FF), + BYTES_TO_WORDS_8(FF, FF, FF, FF, 00, 00, 00, 00), + BYTES_TO_WORDS_8(00, 00, 00, 00, 00, 00, 00, 00), + BYTES_TO_WORDS_8(01, 00, 00, 00, FF, FF, FF, FF) }, + { BYTES_TO_WORDS_8(51, 25, 63, FC, C2, CA, B9, F3), + BYTES_TO_WORDS_8(84, 9E, 17, A7, AD, FA, E6, BC), + BYTES_TO_WORDS_8(FF, FF, FF, FF, FF, FF, FF, FF), + BYTES_TO_WORDS_8(00, 00, 00, 00, FF, FF, FF, FF) }, + { BYTES_TO_WORDS_8(96, C2, 98, D8, 45, 39, A1, F4), + BYTES_TO_WORDS_8(A0, 33, EB, 2D, 81, 7D, 03, 77), + BYTES_TO_WORDS_8(F2, 40, A4, 63, E5, E6, BC, F8), + BYTES_TO_WORDS_8(47, 42, 2C, E1, F2, D1, 17, 6B), + + BYTES_TO_WORDS_8(F5, 51, BF, 37, 68, 40, B6, CB), + BYTES_TO_WORDS_8(CE, 5E, 31, 6B, 57, 33, CE, 2B), + BYTES_TO_WORDS_8(16, 9E, 0F, 7C, 4A, EB, E7, 8E), + BYTES_TO_WORDS_8(9B, 7F, 1A, FE, E2, 42, E3, 4F) }, + { BYTES_TO_WORDS_8(4B, 60, D2, 27, 3E, 3C, CE, 3B), + BYTES_TO_WORDS_8(F6, B0, 53, CC, B0, 06, 1D, 65), + BYTES_TO_WORDS_8(BC, 86, 98, 76, 55, BD, EB, B3), + BYTES_TO_WORDS_8(E7, 93, 3A, AA, D8, 35, C6, 5A) }, + &double_jacobian_default, +#if uECC_SUPPORT_COMPRESSED_POINT + &mod_sqrt_default, +#endif + &x_side_default, +#if (uECC_OPTIMIZATION_LEVEL > 0) + &vli_mmod_fast_secp256r1 +#endif +}; + +uECC_Curve uECC_secp256r1(void) { return &curve_secp256r1; } + + +#if (uECC_OPTIMIZATION_LEVEL > 0 && !asm_mmod_fast_secp256r1) +/* Computes result = product % curve_p + from http://www.nsa.gov/ia/_files/nist-routines.pdf */ +#if uECC_WORD_SIZE == 1 +static void vli_mmod_fast_secp256r1(uint8_t *result, uint8_t *product) { + uint8_t tmp[num_words_secp256r1]; + int8_t carry; + + /* t */ + uECC_vli_set(result, product, num_words_secp256r1); + + /* s1 */ + tmp[0] = tmp[1] = tmp[2] = tmp[3] = 0; + tmp[4] = tmp[5] = tmp[6] = tmp[7] = 0; + tmp[8] = tmp[9] = tmp[10] = tmp[11] = 0; + tmp[12] = product[44]; tmp[13] = product[45]; tmp[14] = product[46]; tmp[15] = product[47]; + tmp[16] = product[48]; tmp[17] = product[49]; tmp[18] = product[50]; tmp[19] = product[51]; + tmp[20] = product[52]; tmp[21] = product[53]; tmp[22] = product[54]; tmp[23] = product[55]; + tmp[24] = product[56]; tmp[25] = product[57]; tmp[26] = product[58]; tmp[27] = product[59]; + tmp[28] = product[60]; tmp[29] = product[61]; tmp[30] = product[62]; tmp[31] = product[63]; + carry = uECC_vli_add(tmp, tmp, tmp, num_words_secp256r1); + carry += uECC_vli_add(result, result, tmp, num_words_secp256r1); + + /* s2 */ + tmp[12] = product[48]; tmp[13] = product[49]; tmp[14] = product[50]; tmp[15] = product[51]; + tmp[16] = product[52]; tmp[17] = product[53]; tmp[18] = product[54]; tmp[19] = product[55]; + tmp[20] = product[56]; tmp[21] = product[57]; tmp[22] = product[58]; tmp[23] = product[59]; + tmp[24] = product[60]; tmp[25] = product[61]; tmp[26] = product[62]; tmp[27] = product[63]; + tmp[28] = tmp[29] = tmp[30] = tmp[31] = 0; + carry += uECC_vli_add(tmp, tmp, tmp, num_words_secp256r1); + carry += uECC_vli_add(result, result, tmp, num_words_secp256r1); + + /* s3 */ + tmp[0] = product[32]; tmp[1] = product[33]; tmp[2] = product[34]; tmp[3] = product[35]; + tmp[4] = product[36]; tmp[5] = product[37]; tmp[6] = product[38]; tmp[7] = product[39]; + tmp[8] = product[40]; tmp[9] = product[41]; tmp[10] = product[42]; tmp[11] = product[43]; + tmp[12] = tmp[13] = tmp[14] = tmp[15] = 0; + tmp[16] = tmp[17] = tmp[18] = tmp[19] = 0; + tmp[20] = tmp[21] = tmp[22] = tmp[23] = 0; + tmp[24] = product[56]; tmp[25] = product[57]; tmp[26] = product[58]; tmp[27] = product[59]; + tmp[28] = product[60]; tmp[29] = product[61]; tmp[30] = product[62]; tmp[31] = product[63]; + carry += uECC_vli_add(result, result, tmp, num_words_secp256r1); + + /* s4 */ + tmp[0] = product[36]; tmp[1] = product[37]; tmp[2] = product[38]; tmp[3] = product[39]; + tmp[4] = product[40]; tmp[5] = product[41]; tmp[6] = product[42]; tmp[7] = product[43]; + tmp[8] = product[44]; tmp[9] = product[45]; tmp[10] = product[46]; tmp[11] = product[47]; + tmp[12] = product[52]; tmp[13] = product[53]; tmp[14] = product[54]; tmp[15] = product[55]; + tmp[16] = product[56]; tmp[17] = product[57]; tmp[18] = product[58]; tmp[19] = product[59]; + tmp[20] = product[60]; tmp[21] = product[61]; tmp[22] = product[62]; tmp[23] = product[63]; + tmp[24] = product[52]; tmp[25] = product[53]; tmp[26] = product[54]; tmp[27] = product[55]; + tmp[28] = product[32]; tmp[29] = product[33]; tmp[30] = product[34]; tmp[31] = product[35]; + carry += uECC_vli_add(result, result, tmp, num_words_secp256r1); + + /* d1 */ + tmp[0] = product[44]; tmp[1] = product[45]; tmp[2] = product[46]; tmp[3] = product[47]; + tmp[4] = product[48]; tmp[5] = product[49]; tmp[6] = product[50]; tmp[7] = product[51]; + tmp[8] = product[52]; tmp[9] = product[53]; tmp[10] = product[54]; tmp[11] = product[55]; + tmp[12] = tmp[13] = tmp[14] = tmp[15] = 0; + tmp[16] = tmp[17] = tmp[18] = tmp[19] = 0; + tmp[20] = tmp[21] = tmp[22] = tmp[23] = 0; + tmp[24] = product[32]; tmp[25] = product[33]; tmp[26] = product[34]; tmp[27] = product[35]; + tmp[28] = product[40]; tmp[29] = product[41]; tmp[30] = product[42]; tmp[31] = product[43]; + carry -= uECC_vli_sub(result, result, tmp, num_words_secp256r1); + + /* d2 */ + tmp[0] = product[48]; tmp[1] = product[49]; tmp[2] = product[50]; tmp[3] = product[51]; + tmp[4] = product[52]; tmp[5] = product[53]; tmp[6] = product[54]; tmp[7] = product[55]; + tmp[8] = product[56]; tmp[9] = product[57]; tmp[10] = product[58]; tmp[11] = product[59]; + tmp[12] = product[60]; tmp[13] = product[61]; tmp[14] = product[62]; tmp[15] = product[63]; + tmp[16] = tmp[17] = tmp[18] = tmp[19] = 0; + tmp[20] = tmp[21] = tmp[22] = tmp[23] = 0; + tmp[24] = product[36]; tmp[25] = product[37]; tmp[26] = product[38]; tmp[27] = product[39]; + tmp[28] = product[44]; tmp[29] = product[45]; tmp[30] = product[46]; tmp[31] = product[47]; + carry -= uECC_vli_sub(result, result, tmp, num_words_secp256r1); + + /* d3 */ + tmp[0] = product[52]; tmp[1] = product[53]; tmp[2] = product[54]; tmp[3] = product[55]; + tmp[4] = product[56]; tmp[5] = product[57]; tmp[6] = product[58]; tmp[7] = product[59]; + tmp[8] = product[60]; tmp[9] = product[61]; tmp[10] = product[62]; tmp[11] = product[63]; + tmp[12] = product[32]; tmp[13] = product[33]; tmp[14] = product[34]; tmp[15] = product[35]; + tmp[16] = product[36]; tmp[17] = product[37]; tmp[18] = product[38]; tmp[19] = product[39]; + tmp[20] = product[40]; tmp[21] = product[41]; tmp[22] = product[42]; tmp[23] = product[43]; + tmp[24] = tmp[25] = tmp[26] = tmp[27] = 0; + tmp[28] = product[48]; tmp[29] = product[49]; tmp[30] = product[50]; tmp[31] = product[51]; + carry -= uECC_vli_sub(result, result, tmp, num_words_secp256r1); + + /* d4 */ + tmp[0] = product[56]; tmp[1] = product[57]; tmp[2] = product[58]; tmp[3] = product[59]; + tmp[4] = product[60]; tmp[5] = product[61]; tmp[6] = product[62]; tmp[7] = product[63]; + tmp[8] = tmp[9] = tmp[10] = tmp[11] = 0; + tmp[12] = product[36]; tmp[13] = product[37]; tmp[14] = product[38]; tmp[15] = product[39]; + tmp[16] = product[40]; tmp[17] = product[41]; tmp[18] = product[42]; tmp[19] = product[43]; + tmp[20] = product[44]; tmp[21] = product[45]; tmp[22] = product[46]; tmp[23] = product[47]; + tmp[24] = tmp[25] = tmp[26] = tmp[27] = 0; + tmp[28] = product[52]; tmp[29] = product[53]; tmp[30] = product[54]; tmp[31] = product[55]; + carry -= uECC_vli_sub(result, result, tmp, num_words_secp256r1); + + if (carry < 0) { + do { + carry += uECC_vli_add(result, result, curve_secp256r1.p, num_words_secp256r1); + } while (carry < 0); + } else { + while (carry || uECC_vli_cmp_unsafe(curve_secp256r1.p, result, num_words_secp256r1) != 1) { + carry -= uECC_vli_sub(result, result, curve_secp256r1.p, num_words_secp256r1); + } + } +} +#elif uECC_WORD_SIZE == 4 +static void vli_mmod_fast_secp256r1(uint32_t *result, uint32_t *product) { + uint32_t tmp[num_words_secp256r1]; + int carry; + + /* t */ + uECC_vli_set(result, product, num_words_secp256r1); + + /* s1 */ + tmp[0] = tmp[1] = tmp[2] = 0; + tmp[3] = product[11]; + tmp[4] = product[12]; + tmp[5] = product[13]; + tmp[6] = product[14]; + tmp[7] = product[15]; + carry = uECC_vli_add(tmp, tmp, tmp, num_words_secp256r1); + carry += uECC_vli_add(result, result, tmp, num_words_secp256r1); + + /* s2 */ + tmp[3] = product[12]; + tmp[4] = product[13]; + tmp[5] = product[14]; + tmp[6] = product[15]; + tmp[7] = 0; + carry += uECC_vli_add(tmp, tmp, tmp, num_words_secp256r1); + carry += uECC_vli_add(result, result, tmp, num_words_secp256r1); + + /* s3 */ + tmp[0] = product[8]; + tmp[1] = product[9]; + tmp[2] = product[10]; + tmp[3] = tmp[4] = tmp[5] = 0; + tmp[6] = product[14]; + tmp[7] = product[15]; + carry += uECC_vli_add(result, result, tmp, num_words_secp256r1); + + /* s4 */ + tmp[0] = product[9]; + tmp[1] = product[10]; + tmp[2] = product[11]; + tmp[3] = product[13]; + tmp[4] = product[14]; + tmp[5] = product[15]; + tmp[6] = product[13]; + tmp[7] = product[8]; + carry += uECC_vli_add(result, result, tmp, num_words_secp256r1); + + /* d1 */ + tmp[0] = product[11]; + tmp[1] = product[12]; + tmp[2] = product[13]; + tmp[3] = tmp[4] = tmp[5] = 0; + tmp[6] = product[8]; + tmp[7] = product[10]; + carry -= uECC_vli_sub(result, result, tmp, num_words_secp256r1); + + /* d2 */ + tmp[0] = product[12]; + tmp[1] = product[13]; + tmp[2] = product[14]; + tmp[3] = product[15]; + tmp[4] = tmp[5] = 0; + tmp[6] = product[9]; + tmp[7] = product[11]; + carry -= uECC_vli_sub(result, result, tmp, num_words_secp256r1); + + /* d3 */ + tmp[0] = product[13]; + tmp[1] = product[14]; + tmp[2] = product[15]; + tmp[3] = product[8]; + tmp[4] = product[9]; + tmp[5] = product[10]; + tmp[6] = 0; + tmp[7] = product[12]; + carry -= uECC_vli_sub(result, result, tmp, num_words_secp256r1); + + /* d4 */ + tmp[0] = product[14]; + tmp[1] = product[15]; + tmp[2] = 0; + tmp[3] = product[9]; + tmp[4] = product[10]; + tmp[5] = product[11]; + tmp[6] = 0; + tmp[7] = product[13]; + carry -= uECC_vli_sub(result, result, tmp, num_words_secp256r1); + + if (carry < 0) { + do { + carry += uECC_vli_add(result, result, curve_secp256r1.p, num_words_secp256r1); + } while (carry < 0); + } else { + while (carry || uECC_vli_cmp_unsafe(curve_secp256r1.p, result, num_words_secp256r1) != 1) { + carry -= uECC_vli_sub(result, result, curve_secp256r1.p, num_words_secp256r1); + } + } +} +#else +static void vli_mmod_fast_secp256r1(uint64_t *result, uint64_t *product) { + uint64_t tmp[num_words_secp256r1]; + int carry; + + /* t */ + uECC_vli_set(result, product, num_words_secp256r1); + + /* s1 */ + tmp[0] = 0; + tmp[1] = product[5] & 0xffffffff00000000ull; + tmp[2] = product[6]; + tmp[3] = product[7]; + carry = (int)uECC_vli_add(tmp, tmp, tmp, num_words_secp256r1); + carry += uECC_vli_add(result, result, tmp, num_words_secp256r1); + + /* s2 */ + tmp[1] = product[6] << 32; + tmp[2] = (product[6] >> 32) | (product[7] << 32); + tmp[3] = product[7] >> 32; + carry += uECC_vli_add(tmp, tmp, tmp, num_words_secp256r1); + carry += uECC_vli_add(result, result, tmp, num_words_secp256r1); + + /* s3 */ + tmp[0] = product[4]; + tmp[1] = product[5] & 0xffffffff; + tmp[2] = 0; + tmp[3] = product[7]; + carry += uECC_vli_add(result, result, tmp, num_words_secp256r1); + + /* s4 */ + tmp[0] = (product[4] >> 32) | (product[5] << 32); + tmp[1] = (product[5] >> 32) | (product[6] & 0xffffffff00000000ull); + tmp[2] = product[7]; + tmp[3] = (product[6] >> 32) | (product[4] << 32); + carry += uECC_vli_add(result, result, tmp, num_words_secp256r1); + + /* d1 */ + tmp[0] = (product[5] >> 32) | (product[6] << 32); + tmp[1] = (product[6] >> 32); + tmp[2] = 0; + tmp[3] = (product[4] & 0xffffffff) | (product[5] << 32); + carry -= uECC_vli_sub(result, result, tmp, num_words_secp256r1); + + /* d2 */ + tmp[0] = product[6]; + tmp[1] = product[7]; + tmp[2] = 0; + tmp[3] = (product[4] >> 32) | (product[5] & 0xffffffff00000000ull); + carry -= uECC_vli_sub(result, result, tmp, num_words_secp256r1); + + /* d3 */ + tmp[0] = (product[6] >> 32) | (product[7] << 32); + tmp[1] = (product[7] >> 32) | (product[4] << 32); + tmp[2] = (product[4] >> 32) | (product[5] << 32); + tmp[3] = (product[6] << 32); + carry -= uECC_vli_sub(result, result, tmp, num_words_secp256r1); + + /* d4 */ + tmp[0] = product[7]; + tmp[1] = product[4] & 0xffffffff00000000ull; + tmp[2] = product[5]; + tmp[3] = product[6] & 0xffffffff00000000ull; + carry -= uECC_vli_sub(result, result, tmp, num_words_secp256r1); + + if (carry < 0) { + do { + carry += uECC_vli_add(result, result, curve_secp256r1.p, num_words_secp256r1); + } while (carry < 0); + } else { + while (carry || uECC_vli_cmp_unsafe(curve_secp256r1.p, result, num_words_secp256r1) != 1) { + carry -= uECC_vli_sub(result, result, curve_secp256r1.p, num_words_secp256r1); + } + } +} +#endif /* uECC_WORD_SIZE */ +#endif /* (uECC_OPTIMIZATION_LEVEL > 0 && !asm_mmod_fast_secp256r1) */ + +#endif /* uECC_SUPPORTS_secp256r1 */ + +#if uECC_SUPPORTS_secp256k1 + +static void double_jacobian_secp256k1(uECC_word_t * X1, + uECC_word_t * Y1, + uECC_word_t * Z1, + uECC_Curve curve); +static void x_side_secp256k1(uECC_word_t *result, const uECC_word_t *x, uECC_Curve curve); +#if (uECC_OPTIMIZATION_LEVEL > 0) +static void vli_mmod_fast_secp256k1(uECC_word_t *result, uECC_word_t *product); +#endif + +static const struct uECC_Curve_t curve_secp256k1 = { + num_words_secp256k1, + num_bytes_secp256k1, + 256, /* num_n_bits */ + { BYTES_TO_WORDS_8(2F, FC, FF, FF, FE, FF, FF, FF), + BYTES_TO_WORDS_8(FF, FF, FF, FF, FF, FF, FF, FF), + BYTES_TO_WORDS_8(FF, FF, FF, FF, FF, FF, FF, FF), + BYTES_TO_WORDS_8(FF, FF, FF, FF, FF, FF, FF, FF) }, + { BYTES_TO_WORDS_8(41, 41, 36, D0, 8C, 5E, D2, BF), + BYTES_TO_WORDS_8(3B, A0, 48, AF, E6, DC, AE, BA), + BYTES_TO_WORDS_8(FE, FF, FF, FF, FF, FF, FF, FF), + BYTES_TO_WORDS_8(FF, FF, FF, FF, FF, FF, FF, FF) }, + { BYTES_TO_WORDS_8(98, 17, F8, 16, 5B, 81, F2, 59), + BYTES_TO_WORDS_8(D9, 28, CE, 2D, DB, FC, 9B, 02), + BYTES_TO_WORDS_8(07, 0B, 87, CE, 95, 62, A0, 55), + BYTES_TO_WORDS_8(AC, BB, DC, F9, 7E, 66, BE, 79), + + BYTES_TO_WORDS_8(B8, D4, 10, FB, 8F, D0, 47, 9C), + BYTES_TO_WORDS_8(19, 54, 85, A6, 48, B4, 17, FD), + BYTES_TO_WORDS_8(A8, 08, 11, 0E, FC, FB, A4, 5D), + BYTES_TO_WORDS_8(65, C4, A3, 26, 77, DA, 3A, 48) }, + { BYTES_TO_WORDS_8(07, 00, 00, 00, 00, 00, 00, 00), + BYTES_TO_WORDS_8(00, 00, 00, 00, 00, 00, 00, 00), + BYTES_TO_WORDS_8(00, 00, 00, 00, 00, 00, 00, 00), + BYTES_TO_WORDS_8(00, 00, 00, 00, 00, 00, 00, 00) }, + &double_jacobian_secp256k1, +#if uECC_SUPPORT_COMPRESSED_POINT + &mod_sqrt_default, +#endif + &x_side_secp256k1, +#if (uECC_OPTIMIZATION_LEVEL > 0) + &vli_mmod_fast_secp256k1 +#endif +}; + +uECC_Curve uECC_secp256k1(void) { return &curve_secp256k1; } + + +/* Double in place */ +static void double_jacobian_secp256k1(uECC_word_t * X1, + uECC_word_t * Y1, + uECC_word_t * Z1, + uECC_Curve curve) { + /* t1 = X, t2 = Y, t3 = Z */ + uECC_word_t t4[num_words_secp256k1]; + uECC_word_t t5[num_words_secp256k1]; + + if (uECC_vli_isZero(Z1, num_words_secp256k1)) { + return; + } + + uECC_vli_modSquare_fast(t5, Y1, curve); /* t5 = y1^2 */ + uECC_vli_modMult_fast(t4, X1, t5, curve); /* t4 = x1*y1^2 = A */ + uECC_vli_modSquare_fast(X1, X1, curve); /* t1 = x1^2 */ + uECC_vli_modSquare_fast(t5, t5, curve); /* t5 = y1^4 */ + uECC_vli_modMult_fast(Z1, Y1, Z1, curve); /* t3 = y1*z1 = z3 */ + + uECC_vli_modAdd(Y1, X1, X1, curve->p, num_words_secp256k1); /* t2 = 2*x1^2 */ + uECC_vli_modAdd(Y1, Y1, X1, curve->p, num_words_secp256k1); /* t2 = 3*x1^2 */ + if (uECC_vli_testBit(Y1, 0)) { + uECC_word_t carry = uECC_vli_add(Y1, Y1, curve->p, num_words_secp256k1); + uECC_vli_rshift1(Y1, num_words_secp256k1); + Y1[num_words_secp256k1 - 1] |= carry << (uECC_WORD_BITS - 1); + } else { + uECC_vli_rshift1(Y1, num_words_secp256k1); + } + /* t2 = 3/2*(x1^2) = B */ + + uECC_vli_modSquare_fast(X1, Y1, curve); /* t1 = B^2 */ + uECC_vli_modSub(X1, X1, t4, curve->p, num_words_secp256k1); /* t1 = B^2 - A */ + uECC_vli_modSub(X1, X1, t4, curve->p, num_words_secp256k1); /* t1 = B^2 - 2A = x3 */ + + uECC_vli_modSub(t4, t4, X1, curve->p, num_words_secp256k1); /* t4 = A - x3 */ + uECC_vli_modMult_fast(Y1, Y1, t4, curve); /* t2 = B * (A - x3) */ + uECC_vli_modSub(Y1, Y1, t5, curve->p, num_words_secp256k1); /* t2 = B * (A - x3) - y1^4 = y3 */ +} + +/* Computes result = x^3 + b. result must not overlap x. */ +static void x_side_secp256k1(uECC_word_t *result, const uECC_word_t *x, uECC_Curve curve) { + uECC_vli_modSquare_fast(result, x, curve); /* r = x^2 */ + uECC_vli_modMult_fast(result, result, x, curve); /* r = x^3 */ + uECC_vli_modAdd(result, result, curve->b, curve->p, num_words_secp256k1); /* r = x^3 + b */ +} + +#if (uECC_OPTIMIZATION_LEVEL > 0 && !asm_mmod_fast_secp256k1) +static void omega_mult_secp256k1(uECC_word_t *result, const uECC_word_t *right); +static void vli_mmod_fast_secp256k1(uECC_word_t *result, uECC_word_t *product) { + uECC_word_t tmp[2 * num_words_secp256k1]; + uECC_word_t carry; + + uECC_vli_clear(tmp, num_words_secp256k1); + uECC_vli_clear(tmp + num_words_secp256k1, num_words_secp256k1); + + omega_mult_secp256k1(tmp, product + num_words_secp256k1); /* (Rq, q) = q * c */ + + carry = uECC_vli_add(result, product, tmp, num_words_secp256k1); /* (C, r) = r + q */ + uECC_vli_clear(product, num_words_secp256k1); + omega_mult_secp256k1(product, tmp + num_words_secp256k1); /* Rq*c */ + carry += uECC_vli_add(result, result, product, num_words_secp256k1); /* (C1, r) = r + Rq*c */ + + while (carry > 0) { + --carry; + uECC_vli_sub(result, result, curve_secp256k1.p, num_words_secp256k1); + } + if (uECC_vli_cmp_unsafe(result, curve_secp256k1.p, num_words_secp256k1) > 0) { + uECC_vli_sub(result, result, curve_secp256k1.p, num_words_secp256k1); + } +} + +#if uECC_WORD_SIZE == 1 +static void omega_mult_secp256k1(uint8_t * result, const uint8_t * right) { + /* Multiply by (2^32 + 2^9 + 2^8 + 2^7 + 2^6 + 2^4 + 1). */ + uECC_word_t r0 = 0; + uECC_word_t r1 = 0; + uECC_word_t r2 = 0; + wordcount_t k; + + /* Multiply by (2^9 + 2^8 + 2^7 + 2^6 + 2^4 + 1). */ + muladd(0xD1, right[0], &r0, &r1, &r2); + result[0] = r0; + r0 = r1; + r1 = r2; + /* r2 is still 0 */ + + for (k = 1; k < num_words_secp256k1; ++k) { + muladd(0x03, right[k - 1], &r0, &r1, &r2); + muladd(0xD1, right[k], &r0, &r1, &r2); + result[k] = r0; + r0 = r1; + r1 = r2; + r2 = 0; + } + muladd(0x03, right[num_words_secp256k1 - 1], &r0, &r1, &r2); + result[num_words_secp256k1] = r0; + result[num_words_secp256k1 + 1] = r1; + /* add the 2^32 multiple */ + result[4 + num_words_secp256k1] = + uECC_vli_add(result + 4, result + 4, right, num_words_secp256k1); +} +#elif uECC_WORD_SIZE == 4 +static void omega_mult_secp256k1(uint32_t * result, const uint32_t * right) { + /* Multiply by (2^9 + 2^8 + 2^7 + 2^6 + 2^4 + 1). */ + uint32_t carry = 0; + wordcount_t k; + + for (k = 0; k < num_words_secp256k1; ++k) { + uint64_t p = (uint64_t)0x3D1 * right[k] + carry; + result[k] = (uint32_t) p; + carry = p >> 32; + } + result[num_words_secp256k1] = carry; + /* add the 2^32 multiple */ + result[1 + num_words_secp256k1] = + uECC_vli_add(result + 1, result + 1, right, num_words_secp256k1); +} +#else +static void omega_mult_secp256k1(uint64_t * result, const uint64_t * right) { + uECC_word_t r0 = 0; + uECC_word_t r1 = 0; + uECC_word_t r2 = 0; + wordcount_t k; + + /* Multiply by (2^32 + 2^9 + 2^8 + 2^7 + 2^6 + 2^4 + 1). */ + for (k = 0; k < num_words_secp256k1; ++k) { + muladd(0x1000003D1ull, right[k], &r0, &r1, &r2); + result[k] = r0; + r0 = r1; + r1 = r2; + r2 = 0; + } + result[num_words_secp256k1] = r0; +} +#endif /* uECC_WORD_SIZE */ +#endif /* (uECC_OPTIMIZATION_LEVEL > 0 && && !asm_mmod_fast_secp256k1) */ + +#endif /* uECC_SUPPORTS_secp256k1 */ + +#endif /* _UECC_CURVE_SPECIFIC_H_ */ diff --git a/deps/picoquic/picotls/deps/micro-ecc/emk_project.py b/deps/picoquic/picotls/deps/micro-ecc/emk_project.py new file mode 100644 index 0000000..940fadc --- /dev/null +++ b/deps/picoquic/picotls/deps/micro-ecc/emk_project.py @@ -0,0 +1,127 @@ +import os + +c, link, asm, utils = emk.module("c", "link", "asm", "utils") + +default_compile_flags = ["-fvisibility=hidden", "-Wall", "-Wextra", "-Wshadow", "-Werror", "-Wno-missing-field-initializers", "-Wno-unused-parameter", \ + "-Wno-comment", "-Wno-unused", "-Wno-unknown-pragmas"] +default_link_flags = [] +opt_flags = {"dbg":["-g"], "std":["-O2"], "max":["-O3"], "small":["-Os"]} +opt_link_flags = {"dbg":[], "std":[], "max":[], "small":[]} +c_flags = ["-std=c99"] +cxx_flags = ["-std=c++11", "-Wno-reorder", "-fno-rtti", "-fno-exceptions"] +c_link_flags = [] +cxx_link_flags = ["-fno-rtti", "-fno-exceptions"] + +def setup_build_dir(): + build_arch = None + if "arch" in emk.options: + build_arch = emk.options["arch"] + elif not emk.cleaning: + build_arch = "osx" + emk.options["arch"] = build_arch + + opt_level = None + if "opt" in emk.options: + level = emk.options["opt"] + if level in opt_flags: + opt_level = level + else: + emk.log.warning("Unknown optimization level '%s'" % (level)) + elif not emk.cleaning: + opt_level = "dbg" + emk.options["opt"] = opt_level + + dirs = ["__build__"] + if build_arch: + dirs.append(build_arch) + if opt_level: + dirs.append(opt_level) + emk.build_dir = os.path.join(*dirs) + +def setup_osx(): + global c + global link + + flags = [("-arch", "x86_64"), "-fno-common", "-Wnewline-eof"] + c.flags.extend(flags) + c.cxx.flags += ["-stdlib=libc++"] + link.cxx.flags += ["-stdlib=libc++"] + + link_flags = [("-arch", "x86_64")] + link.local_flags.extend(link_flags) + +def setup_avr(): + global c + global link + + c.compiler = c.GccCompiler("/Projects/avr-tools/bin/avr-") + c.flags += ["-mmcu=atmega256rfr2", "-ffunction-sections", "-fdata-sections"] + link.linker = link.GccLinker("/Projects/avr-tools/bin/avr-") + link.flags += ["-mmcu=atmega256rfr2", "-mrelax", "-Wl,--gc-sections"] + link.strip = True + +def setup_arm_thumb(): + global c + global link + global asm + global utils + + asm.assembler = asm.GccAssembler("/cross/arm_cortex/bin/arm-none-eabi-") + c.compiler = c.GccCompiler("/cross/arm_cortex/bin/arm-none-eabi-") + link.linker = link.GccLinker("/cross/arm_cortex/bin/arm-none-eabi-") + + c.flags.extend(["-mcpu=cortex-m0", "-mthumb", "-ffunction-sections", "-fdata-sections", "-fno-builtin-fprintf", "-fno-builtin-printf"]) + c.defines["LPC11XX"] = 1 + + link.local_flags.extend(["-mcpu=cortex-m0", "-mthumb", "-nostartfiles", "-nostdlib", "-Wl,--gc-sections"]) + link.local_flags.extend(["-Tflash.lds", "-L/Projects/lpc11xx/core", "/Projects/lpc11xx/core/" + emk.build_dir + "/board_cstartup.o"]) + link.local_syslibs += ["gcc"] + link.depdirs += ["/Projects/lpc11xx/stdlib"] + + def do_objcopy(produces, requires): + utils.call("/cross/arm_cortex/bin/arm-none-eabi-objcopy", "-O", "binary", requires[0], produces[0]) + + def handle_exe(path): + emk.depend(path, "/Projects/lpc11xx/core/" + emk.build_dir + "/board_cstartup.o") + emk.rule(do_objcopy, path + ".bin", path, cwd_safe=True, ex_safe=True) + emk.autobuild(path + ".bin") + + link.exe_funcs.append(handle_exe) + link.strip = True + + emk.recurse("/Projects/lpc11xx/core") + +def setup_linux_rpi(): + global c + global link + + c.compiler = c.GccCompiler("/Volumes/xtools/arm-none-linux-gnueabi/bin/arm-none-linux-gnueabi-") + link.linker = link.GccLinker("/Volumes/xtools/arm-none-linux-gnueabi/bin/arm-none-linux-gnueabi-") + + c.flags.extend(["-fomit-frame-pointer"]) + +setup_build_dir() + +setup_funcs = {"osx":setup_osx, "avr":setup_avr, "arm_thumb":setup_arm_thumb, "rpi": setup_linux_rpi} + +if not emk.cleaning: + build_arch = emk.options["arch"] + opt_level = emk.options["opt"] + + c.flags.extend(default_compile_flags) + c.flags.extend(opt_flags[opt_level]) + c.c.flags.extend(c_flags) + c.cxx.flags.extend(cxx_flags) + link.local_flags.extend(default_link_flags) + link.local_flags.extend(opt_link_flags[opt_level]) + link.c.local_flags.extend(c_link_flags) + link.cxx.local_flags.extend(cxx_link_flags) + + c.include_dirs.append("$:proj:$") + + if build_arch in setup_funcs: + setup_funcs[build_arch]() + else: + raise emk.BuildError("Unknown target arch '%s'" % (build_arch)) + + c.defines["TARGET_ARCH_" + build_arch.upper()] = 1 diff --git a/deps/picoquic/picotls/deps/micro-ecc/emk_rules.py b/deps/picoquic/picotls/deps/micro-ecc/emk_rules.py new file mode 100644 index 0000000..b1d76c8 --- /dev/null +++ b/deps/picoquic/picotls/deps/micro-ecc/emk_rules.py @@ -0,0 +1,3 @@ +c, link = emk.module("c", "link") + +emk.subdir("test") diff --git a/deps/picoquic/picotls/deps/micro-ecc/platform-specific.inc b/deps/picoquic/picotls/deps/micro-ecc/platform-specific.inc new file mode 100644 index 0000000..1bb595a --- /dev/null +++ b/deps/picoquic/picotls/deps/micro-ecc/platform-specific.inc @@ -0,0 +1,67 @@ +/* Copyright 2015, Kenneth MacKay. Licensed under the BSD 2-clause license. */ + +#ifndef _UECC_PLATFORM_SPECIFIC_H_ +#define _UECC_PLATFORM_SPECIFIC_H_ + +#include "types.h" + +#if (defined(_WIN32) || defined(_WIN64)) +/* Windows */ + +#define WIN32_LEAN_AND_MEAN +#include +#include + +static int default_RNG(uint8_t *dest, unsigned size) { + HCRYPTPROV prov; + if (!CryptAcquireContext(&prov, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) { + return 0; + } + + CryptGenRandom(prov, size, (BYTE *)dest); + CryptReleaseContext(prov, 0); + return 1; +} +#define default_RNG_defined 1 + +#elif defined(unix) || defined(__linux__) || defined(__unix__) || defined(__unix) || \ + (defined(__APPLE__) && defined(__MACH__)) || defined(uECC_POSIX) + +/* Some POSIX-like system with /dev/urandom or /dev/random. */ +#include +#include +#include + +#ifndef O_CLOEXEC + #define O_CLOEXEC 0 +#endif + +static int default_RNG(uint8_t *dest, unsigned size) { + int fd = open("/dev/urandom", O_RDONLY | O_CLOEXEC); + if (fd == -1) { + fd = open("/dev/random", O_RDONLY | O_CLOEXEC); + if (fd == -1) { + return 0; + } + } + + char *ptr = (char *)dest; + size_t left = size; + while (left > 0) { + ssize_t bytes_read = read(fd, ptr, left); + if (bytes_read <= 0) { // read failed + close(fd); + return 0; + } + left -= bytes_read; + ptr += bytes_read; + } + + close(fd); + return 1; +} +#define default_RNG_defined 1 + +#endif /* platform */ + +#endif /* _UECC_PLATFORM_SPECIFIC_H_ */ diff --git a/deps/picoquic/picotls/deps/micro-ecc/scripts/mult_arm.py b/deps/picoquic/picotls/deps/micro-ecc/scripts/mult_arm.py new file mode 100755 index 0000000..402ace1 --- /dev/null +++ b/deps/picoquic/picotls/deps/micro-ecc/scripts/mult_arm.py @@ -0,0 +1,188 @@ +#!/usr/bin/env python + +import sys + +if len(sys.argv) < 2: + print "Provide the integer size in 32-bit words" + sys.exit(1) + +size = int(sys.argv[1]) + +full_rows = size // 3 +init_size = size % 3 + +if init_size == 0: + full_rows = full_rows - 1 + init_size = 3 + +def emit(line, *args): + s = '"' + line + r' \n\t"' + print s % args + +rx = [3, 4, 5] +ry = [6, 7, 8] + +#### set up registers +emit("add r0, %s", (size - init_size) * 4) # move z +emit("add r2, %s", (size - init_size) * 4) # move y + +emit("ldmia r1!, {%s}", ", ".join(["r%s" % (rx[i]) for i in xrange(init_size)])) +emit("ldmia r2!, {%s}", ", ".join(["r%s" % (ry[i]) for i in xrange(init_size)])) + +print "" +if init_size == 1: + emit("umull r9, r10, r3, r6") + emit("stmia r0!, {r9, r10}") +else: + #### first two multiplications of initial block + emit("umull r11, r12, r3, r6") + emit("stmia r0!, {r11}") + print "" + emit("mov r10, #0") + emit("umull r11, r9, r3, r7") + emit("adds r12, r12, r11") + emit("adc r9, r9, #0") + emit("umull r11, r14, r4, r6") + emit("adds r12, r12, r11") + emit("adcs r9, r9, r14") + emit("adc r10, r10, #0") + emit("stmia r0!, {r12}") + print "" + + #### rest of initial block, with moving accumulator registers + acc = [9, 10, 11, 12, 14] + if init_size == 3: + emit("mov r%s, #0", acc[2]) + for i in xrange(0, 3): + emit("umull r%s, r%s, r%s, r%s", acc[3], acc[4], rx[i], ry[2 - i]) + emit("adds r%s, r%s, r%s", acc[0], acc[0], acc[3]) + emit("adcs r%s, r%s, r%s", acc[1], acc[1], acc[4]) + emit("adc r%s, r%s, #0", acc[2], acc[2]) + emit("stmia r0!, {r%s}", acc[0]) + print "" + acc = acc[1:] + acc[:1] + + emit("mov r%s, #0", acc[2]) + for i in xrange(0, 2): + emit("umull r%s, r%s, r%s, r%s", acc[3], acc[4], rx[i + 1], ry[2 - i]) + emit("adds r%s, r%s, r%s", acc[0], acc[0], acc[3]) + emit("adcs r%s, r%s, r%s", acc[1], acc[1], acc[4]) + emit("adc r%s, r%s, #0", acc[2], acc[2]) + emit("stmia r0!, {r%s}", acc[0]) + print "" + acc = acc[1:] + acc[:1] + + emit("umull r%s, r%s, r%s, r%s", acc[3], acc[4], rx[init_size-1], ry[init_size-1]) + emit("adds r%s, r%s, r%s", acc[0], acc[0], acc[3]) + emit("adc r%s, r%s, r%s", acc[1], acc[1], acc[4]) + emit("stmia r0!, {r%s}", acc[0]) + emit("stmia r0!, {r%s}", acc[1]) +print "" + +#### reset y and z pointers +emit("sub r0, %s", (2 * init_size + 3) * 4) +emit("sub r2, %s", (init_size + 3) * 4) + +#### load y registers +emit("ldmia r2!, {%s}", ", ".join(["r%s" % (ry[i]) for i in xrange(3)])) + +#### load additional x registers +if init_size != 3: + emit("ldmia r1!, {%s}", ", ".join(["r%s" % (rx[i]) for i in xrange(init_size, 3)])) +print "" + +prev_size = init_size +for row in xrange(full_rows): + emit("umull r11, r12, r3, r6") + emit("stmia r0!, {r11}") + print "" + emit("mov r10, #0") + emit("umull r11, r9, r3, r7") + emit("adds r12, r12, r11") + emit("adc r9, r9, #0") + emit("umull r11, r14, r4, r6") + emit("adds r12, r12, r11") + emit("adcs r9, r9, r14") + emit("adc r10, r10, #0") + emit("stmia r0!, {r12}") + print "" + + acc = [9, 10, 11, 12, 14] + emit("mov r%s, #0", acc[2]) + for i in xrange(0, 3): + emit("umull r%s, r%s, r%s, r%s", acc[3], acc[4], rx[i], ry[2 - i]) + emit("adds r%s, r%s, r%s", acc[0], acc[0], acc[3]) + emit("adcs r%s, r%s, r%s", acc[1], acc[1], acc[4]) + emit("adc r%s, r%s, #0", acc[2], acc[2]) + emit("stmia r0!, {r%s}", acc[0]) + print "" + acc = acc[1:] + acc[:1] + + #### now we need to start shifting x and loading from z + x_regs = [3, 4, 5] + for r in xrange(0, prev_size): + x_regs = x_regs[1:] + x_regs[:1] + emit("ldmia r1!, {r%s}", x_regs[2]) + emit("mov r%s, #0", acc[2]) + for i in xrange(0, 3): + emit("umull r%s, r%s, r%s, r%s", acc[3], acc[4], x_regs[i], ry[2 - i]) + emit("adds r%s, r%s, r%s", acc[0], acc[0], acc[3]) + emit("adcs r%s, r%s, r%s", acc[1], acc[1], acc[4]) + emit("adc r%s, r%s, #0", acc[2], acc[2]) + emit("ldr r%s, [r0]", acc[3]) # load stored value from initial block, and add to accumulator + emit("adds r%s, r%s, r%s", acc[0], acc[0], acc[3]) + emit("adcs r%s, r%s, #0", acc[1], acc[1]) + emit("adc r%s, r%s, #0", acc[2], acc[2]) + emit("stmia r0!, {r%s}", acc[0]) + print "" + acc = acc[1:] + acc[:1] + + # done shifting x, start shifting y + y_regs = [6, 7, 8] + for r in xrange(0, prev_size): + y_regs = y_regs[1:] + y_regs[:1] + emit("ldmia r2!, {r%s}", y_regs[2]) + emit("mov r%s, #0", acc[2]) + for i in xrange(0, 3): + emit("umull r%s, r%s, r%s, r%s", acc[3], acc[4], x_regs[i], y_regs[2 - i]) + emit("adds r%s, r%s, r%s", acc[0], acc[0], acc[3]) + emit("adcs r%s, r%s, r%s", acc[1], acc[1], acc[4]) + emit("adc r%s, r%s, #0", acc[2], acc[2]) + emit("ldr r%s, [r0]", acc[3]) # load stored value from initial block, and add to accumulator + emit("adds r%s, r%s, r%s", acc[0], acc[0], acc[3]) + emit("adcs r%s, r%s, #0", acc[1], acc[1]) + emit("adc r%s, r%s, #0", acc[2], acc[2]) + emit("stmia r0!, {r%s}", acc[0]) + print "" + acc = acc[1:] + acc[:1] + + # done both shifts, do remaining corner + emit("mov r%s, #0", acc[2]) + for i in xrange(0, 2): + emit("umull r%s, r%s, r%s, r%s", acc[3], acc[4], x_regs[i + 1], y_regs[2 - i]) + emit("adds r%s, r%s, r%s", acc[0], acc[0], acc[3]) + emit("adcs r%s, r%s, r%s", acc[1], acc[1], acc[4]) + emit("adc r%s, r%s, #0", acc[2], acc[2]) + emit("stmia r0!, {r%s}", acc[0]) + print "" + acc = acc[1:] + acc[:1] + + emit("umull r%s, r%s, r%s, r%s", acc[3], acc[4], x_regs[2], y_regs[2]) + emit("adds r%s, r%s, r%s", acc[0], acc[0], acc[3]) + emit("adc r%s, r%s, r%s", acc[1], acc[1], acc[4]) + emit("stmia r0!, {r%s}", acc[0]) + emit("stmia r0!, {r%s}", acc[1]) + print "" + + prev_size = prev_size + 3 + if row < full_rows - 1: + #### reset x, y and z pointers + emit("sub r0, %s", (2 * prev_size + 3) * 4) + emit("sub r1, %s", prev_size * 4) + emit("sub r2, %s", (prev_size + 3) * 4) + + #### load x and y registers + emit("ldmia r1!, {%s}", ",".join(["r%s" % (rx[i]) for i in xrange(3)])) + emit("ldmia r2!, {%s}", ",".join(["r%s" % (ry[i]) for i in xrange(3)])) + + print "" diff --git a/deps/picoquic/picotls/deps/micro-ecc/scripts/mult_avr.py b/deps/picoquic/picotls/deps/micro-ecc/scripts/mult_avr.py new file mode 100755 index 0000000..d40e4c2 --- /dev/null +++ b/deps/picoquic/picotls/deps/micro-ecc/scripts/mult_avr.py @@ -0,0 +1,203 @@ +#!/usr/bin/env python + +import sys + +if len(sys.argv) < 2: + print "Provide the integer size in bytes" + sys.exit(1) + +size = int(sys.argv[1]) + +full_rows = size // 10 +init_size = size % 10 + +if init_size == 0: + full_rows = full_rows - 1 + init_size = 10 + +def rx(i): + return i + 2 + +def ry(i): + return i + 12 + +def emit(line, *args): + s = '"' + line + r' \n\t"' + print s % args + +#### set up registers +emit("adiw r30, %s", size - init_size) # move z +emit("adiw r28, %s", size - init_size) # move y + +for i in xrange(init_size): + emit("ld r%s, x+", rx(i)) +for i in xrange(init_size): + emit("ld r%s, y+", ry(i)) + +emit("ldi r25, 0") +print "" +if init_size == 1: + emit("mul r2, r12") + emit("st z+, r0") + emit("st z+, r1") +else: + #### first two multiplications of initial block + emit("ldi r23, 0") + emit("mul r2, r12") + emit("st z+, r0") + emit("mov r22, r1") + print "" + emit("ldi r24, 0") + emit("mul r2, r13") + emit("add r22, r0") + emit("adc r23, r1") + emit("mul r3, r12") + emit("add r22, r0") + emit("adc r23, r1") + emit("adc r24, r25") + emit("st z+, r22") + print "" + + #### rest of initial block, with moving accumulator registers + acc = [23, 24, 22] + for r in xrange(2, init_size): + emit("ldi r%s, 0", acc[2]) + for i in xrange(0, r+1): + emit("mul r%s, r%s", rx(i), ry(r - i)) + emit("add r%s, r0", acc[0]) + emit("adc r%s, r1", acc[1]) + emit("adc r%s, r25", acc[2]) + emit("st z+, r%s", acc[0]) + print "" + acc = acc[1:] + acc[:1] + for r in xrange(1, init_size-1): + emit("ldi r%s, 0", acc[2]) + for i in xrange(0, init_size-r): + emit("mul r%s, r%s", rx(r+i), ry((init_size-1) - i)) + emit("add r%s, r0", acc[0]) + emit("adc r%s, r1", acc[1]) + emit("adc r%s, r25", acc[2]) + emit("st z+, r%s", acc[0]) + print "" + acc = acc[1:] + acc[:1] + emit("mul r%s, r%s", rx(init_size-1), ry(init_size-1)) + emit("add r%s, r0", acc[0]) + emit("adc r%s, r1", acc[1]) + emit("st z+, r%s", acc[0]) + emit("st z+, r%s", acc[1]) +print "" + +#### reset y and z pointers +emit("sbiw r30, %s", 2 * init_size + 10) +emit("sbiw r28, %s", init_size + 10) + +#### load y registers +for i in xrange(10): + emit("ld r%s, y+", ry(i)) + +#### load additional x registers +for i in xrange(init_size, 10): + emit("ld r%s, x+", rx(i)) +print "" + +prev_size = init_size +for row in xrange(full_rows): + #### do x = 0-9, y = 0-9 multiplications + emit("ldi r23, 0") + emit("mul r2, r12") + emit("st z+, r0") + emit("mov r22, r1") + print "" + emit("ldi r24, 0") + emit("mul r2, r13") + emit("add r22, r0") + emit("adc r23, r1") + emit("mul r3, r12") + emit("add r22, r0") + emit("adc r23, r1") + emit("adc r24, r25") + emit("st z+, r22") + print "" + + acc = [23, 24, 22] + for r in xrange(2, 10): + emit("ldi r%s, 0", acc[2]) + for i in xrange(0, r+1): + emit("mul r%s, r%s", rx(i), ry(r - i)) + emit("add r%s, r0", acc[0]) + emit("adc r%s, r1", acc[1]) + emit("adc r%s, r25", acc[2]) + emit("st z+, r%s", acc[0]) + print "" + acc = acc[1:] + acc[:1] + + #### now we need to start shifting x and loading from z + x_regs = [2, 3, 4, 5, 6, 7, 8, 9, 10, 11] + for r in xrange(0, prev_size): + x_regs = x_regs[1:] + x_regs[:1] + emit("ld r%s, x+", x_regs[9]) # load next byte of left + emit("ldi r%s, 0", acc[2]) + for i in xrange(0, 10): + emit("mul r%s, r%s", x_regs[i], ry(9 - i)) + emit("add r%s, r0", acc[0]) + emit("adc r%s, r1", acc[1]) + emit("adc r%s, r25", acc[2]) + emit("ld r0, z") # load stored value from initial block, and add to accumulator (note z does not increment) + emit("add r%s, r0", acc[0]) + emit("adc r%s, r25", acc[1]) + emit("adc r%s, r25", acc[2]) + emit("st z+, r%s", acc[0]) # store next byte (z increments) + print "" + acc = acc[1:] + acc[:1] + + # done shifting x, start shifting y + y_regs = [12, 13, 14, 15, 16, 17, 18, 19, 20, 21] + for r in xrange(0, prev_size): + y_regs = y_regs[1:] + y_regs[:1] + emit("ld r%s, y+", y_regs[9]) # load next byte of right + emit("ldi r%s, 0", acc[2]) + for i in xrange(0, 10): + emit("mul r%s, r%s", x_regs[i], y_regs[9 -i]) + emit("add r%s, r0", acc[0]) + emit("adc r%s, r1", acc[1]) + emit("adc r%s, r25", acc[2]) + emit("ld r0, z") # load stored value from initial block, and add to accumulator (note z does not increment) + emit("add r%s, r0", acc[0]) + emit("adc r%s, r25", acc[1]) + emit("adc r%s, r25", acc[2]) + emit("st z+, r%s", acc[0]) # store next byte (z increments) + print "" + acc = acc[1:] + acc[:1] + + # done both shifts, do remaining corner + for r in xrange(1, 9): + emit("ldi r%s, 0", acc[2]) + for i in xrange(0, 10-r): + emit("mul r%s, r%s", x_regs[r+i], y_regs[9 - i]) + emit("add r%s, r0", acc[0]) + emit("adc r%s, r1", acc[1]) + emit("adc r%s, r25", acc[2]) + emit("st z+, r%s", acc[0]) + print "" + acc = acc[1:] + acc[:1] + emit("mul r%s, r%s", x_regs[9], y_regs[9]) + emit("add r%s, r0", acc[0]) + emit("adc r%s, r1", acc[1]) + emit("st z+, r%s", acc[0]) + emit("st z+, r%s", acc[1]) + print "" + + prev_size = prev_size + 10 + if row < full_rows - 1: + #### reset x, y and z pointers + emit("sbiw r30, %s", 2 * prev_size + 10) + emit("sbiw r28, %s", prev_size + 10) + emit("sbiw r26, %s", prev_size) + + #### load x and y registers + for i in xrange(10): + emit("ld r%s, x+", rx(i)) + emit("ld r%s, y+", ry(i)) + print "" + +emit("eor r1, r1") diff --git a/deps/picoquic/picotls/deps/micro-ecc/scripts/mult_avr_extra.py b/deps/picoquic/picotls/deps/micro-ecc/scripts/mult_avr_extra.py new file mode 100755 index 0000000..f6e654f --- /dev/null +++ b/deps/picoquic/picotls/deps/micro-ecc/scripts/mult_avr_extra.py @@ -0,0 +1,143 @@ +#!/usr/bin/env python + +import sys + +if len(sys.argv) < 2: + print "Provide the integer size in bytes" + sys.exit(1) + +size = int(sys.argv[1]) + +def lhi(i): + return i + 2 + +def rhi(i): + return i + 6 + +left_lo = [10, 11, 12, 13] +right_lo = [14, 15, 16, 17] + +def llo(i): + return left_lo[i] + +def rlo(i): + return right_lo[i] + +def emit(line, *args): + s = '"' + line + r' \n\t"' + print s % args + +def update_low(): + global left_lo + global right_lo + left_lo = left_lo[1:] + left_lo[:1] + right_lo = right_lo[1:] + right_lo[:1] + emit("ld r%s, x+", left_lo[3]) + emit("ld r%s, y+", right_lo[3]) + +accum = [19, 20, 21] + +def acc(i): + return accum[i] + +def rotate_acc(): + global accum + accum = accum[1:] + accum[:1] + +# Load high values +for i in xrange(4): + emit("ld r%s, x+", lhi(i)) + emit("ld r%s, y+", rhi(i)) + +emit("sbiw r26, %s", size + 4) +emit("sbiw r28, %s", size + 4) +emit("sbiw r30, %s", size) + +# Load low values +for i in xrange(4): + emit("ld r%s, x+", llo(i)) + emit("ld r%s, y+", rlo(i)) +print "" + +# Compute initial triangles +emit("mul r%s, r%s", lhi(0), rlo(0)) +emit("mov r%s, r0", acc(0)) +emit("mov r%s, r1", acc(1)) +emit("ldi r%s, 0", acc(2)) +emit("ld r0, z") +emit("add r%s, r0", acc(0)) +emit("adc r%s, r25", acc(1)) +emit("mul r%s, r%s", rhi(0), llo(0)) +emit("add r%s, r0", acc(0)) +emit("adc r%s, r1", acc(1)) +emit("adc r%s, r25", acc(2)) +emit("st z+, r%s", acc(0)) +print "" +rotate_acc() + +for i in xrange(1, 4): + emit("ldi r%s, 0", acc(2)) + emit("ld r0, z") + emit("add r%s, r0", acc(0)) + emit("adc r%s, r25", acc(1)) + for j in xrange(i + 1): + emit("mul r%s, r%s", lhi(j), rlo(i-j)) + emit("add r%s, r0", acc(0)) + emit("adc r%s, r1", acc(1)) + emit("adc r%s, r25", acc(2)) + emit("mul r%s, r%s", rhi(j), llo(i-j)) + emit("add r%s, r0", acc(0)) + emit("adc r%s, r1", acc(1)) + emit("adc r%s, r25", acc(2)) + emit("st z+, r%s", acc(0)) + print "" + rotate_acc() + +# Compute rows overlapping old block +for i in xrange(4, size): + emit("ldi r%s, 0", acc(2)) + emit("ld r0, z") + emit("add r%s, r0", acc(0)) + emit("adc r%s, r25", acc(1)) + update_low() + for j in xrange(4): + emit("mul r%s, r%s", lhi(j), rlo(3-j)) + emit("add r%s, r0", acc(0)) + emit("adc r%s, r1", acc(1)) + emit("adc r%s, r25", acc(2)) + emit("mul r%s, r%s", rhi(j), llo(3-j)) + emit("add r%s, r0", acc(0)) + emit("adc r%s, r1", acc(1)) + emit("adc r%s, r25", acc(2)) + emit("st z+, r%s", acc(0)) + print "" + rotate_acc() + +# Compute new triangle +left_combined = [llo(1), llo(2), llo(3), lhi(0), lhi(1), lhi(2), lhi(3)] +right_combined = [rlo(1), rlo(2), rlo(3), rhi(0), rhi(1), rhi(2), rhi(3)] + +def left(i): + return left_combined[i] + +def right(i): + return right_combined[i] + +for i in xrange(6): + emit("ldi r%s, 0", acc(2)) + for j in xrange(7 - i): + emit("mul r%s, r%s", left(i+j), right(6-j)) + emit("add r%s, r0", acc(0)) + emit("adc r%s, r1", acc(1)) + emit("adc r%s, r25", acc(2)) + emit("st z+, r%s", acc(0)) + print "" + rotate_acc() + +emit("mul r%s, r%s", left(6), right(6)) +emit("add r%s, r0", acc(0)) +emit("adc r%s, r1", acc(1)) +emit("st z+, r%s", acc(0)) +emit("st z+, r%s", acc(1)) +emit("adiw r26, 4") +emit("adiw r28, 4") diff --git a/deps/picoquic/picotls/deps/micro-ecc/scripts/square_arm.py b/deps/picoquic/picotls/deps/micro-ecc/scripts/square_arm.py new file mode 100755 index 0000000..5330c7e --- /dev/null +++ b/deps/picoquic/picotls/deps/micro-ecc/scripts/square_arm.py @@ -0,0 +1,242 @@ +#!/usr/bin/env python + +import sys + +if len(sys.argv) < 2: + print "Provide the integer size in 32-bit words" + sys.exit(1) + +size = int(sys.argv[1]) + +if size > 8: + print "This script doesn't work with integer size %s due to laziness" % (size) + sys.exit(1) + +init_size = 0 +if size > 6: + init_size = size - 6 + +def emit(line, *args): + s = '"' + line + r' \n\t"' + print s % args + +def mulacc(acc, r1, r2): + if size <= 6: + emit("umull r1, r14, r%s, r%s", r1, r2) + emit("adds r%s, r%s, r1", acc[0], acc[0]) + emit("adcs r%s, r%s, r14", acc[1], acc[1]) + emit("adc r%s, r%s, #0", acc[2], acc[2]) + else: + emit("mov r14, r%s", acc[1]) + emit("umlal r%s, r%s, r%s, r%s", acc[0], acc[1], r1, r2) + emit("cmp r14, r%s", acc[1]) + emit("it hi") + emit("adchi r%s, r%s, #0", acc[2], acc[2]) + +r = [2, 3, 4, 5, 6, 7] + +s = size - init_size + +if init_size == 1: + emit("ldmia r1!, {r2}") + emit("add r1, %s", (size - init_size * 2) * 4) + emit("ldmia r1!, {r5}") + + emit("add r0, %s", (size - init_size) * 4) + emit("umull r8, r9, r2, r5") + emit("stmia r0!, {r8, r9}") + + emit("sub r0, %s", (size + init_size) * 4) + emit("sub r1, %s", (size) * 4) + print "" +elif init_size == 2: + emit("ldmia r1!, {r2, r3}") + emit("add r1, %s", (size - init_size * 2) * 4) + emit("ldmia r1!, {r5, r6}") + + emit("add r0, %s", (size - init_size) * 4) + print "" + + emit("umull r8, r9, r2, r5") + emit("stmia r0!, {r8}") + print "" + + emit("umull r12, r10, r2, r6") + emit("adds r9, r9, r12") + emit("adc r10, r10, #0") + emit("stmia r0!, {r9}") + print "" + + emit("umull r8, r9, r3, r6") + emit("adds r10, r10, r8") + emit("adc r11, r9, #0") + emit("stmia r0!, {r10, r11}") + print "" + + emit("sub r0, %s", (size + init_size) * 4) + emit("sub r1, %s", (size) * 4) + +# load input words +emit("ldmia r1!, {%s}", ", ".join(["r%s" % (r[i]) for i in xrange(s)])) +print "" + +emit("umull r11, r12, r2, r2") +emit("stmia r0!, {r11}") +print "" +emit("mov r9, #0") +emit("umull r10, r11, r2, r3") +emit("adds r12, r12, r10") +emit("adcs r8, r11, #0") +emit("adc r9, r9, #0") +emit("adds r12, r12, r10") +emit("adcs r8, r8, r11") +emit("adc r9, r9, #0") +emit("stmia r0!, {r12}") +print "" +emit("mov r10, #0") +emit("umull r11, r12, r2, r4") +emit("adds r11, r11, r11") +emit("adcs r12, r12, r12") +emit("adc r10, r10, #0") +emit("adds r8, r8, r11") +emit("adcs r9, r9, r12") +emit("adc r10, r10, #0") +emit("umull r11, r12, r3, r3") +emit("adds r8, r8, r11") +emit("adcs r9, r9, r12") +emit("adc r10, r10, #0") +emit("stmia r0!, {r8}") +print "" + +acc = [8, 9, 10] +old_acc = [11, 12] +for i in xrange(3, s): + emit("mov r%s, #0", old_acc[1]) + tmp = [acc[1], acc[2]] + acc = [acc[0], old_acc[0], old_acc[1]] + old_acc = tmp + + # gather non-equal words + emit("umull r%s, r%s, r%s, r%s", acc[0], acc[1], r[0], r[i]) + for j in xrange(1, (i+1)//2): + mulacc(acc, r[j], r[i-j]) + # multiply by 2 + emit("adds r%s, r%s, r%s", acc[0], acc[0], acc[0]) + emit("adcs r%s, r%s, r%s", acc[1], acc[1], acc[1]) + emit("adc r%s, r%s, r%s", acc[2], acc[2], acc[2]) + + # add equal word (if any) + if ((i+1) % 2) != 0: + mulacc(acc, r[i//2], r[i//2]) + + # add old accumulator + emit("adds r%s, r%s, r%s", acc[0], acc[0], old_acc[0]) + emit("adcs r%s, r%s, r%s", acc[1], acc[1], old_acc[1]) + emit("adc r%s, r%s, #0", acc[2], acc[2]) + + # store + emit("stmia r0!, {r%s}", acc[0]) + print "" + +regs = list(r) +for i in xrange(init_size): + regs = regs[1:] + regs[:1] + emit("ldmia r1!, {r%s}", regs[5]) + + for limit in [4, 5]: + emit("mov r%s, #0", old_acc[1]) + tmp = [acc[1], acc[2]] + acc = [acc[0], old_acc[0], old_acc[1]] + old_acc = tmp + + # gather non-equal words + emit("umull r%s, r%s, r%s, r%s", acc[0], acc[1], regs[0], regs[limit]) + for j in xrange(1, (limit+1)//2): + mulacc(acc, regs[j], regs[limit-j]) + + emit("ldr r14, [r0]") # load stored value from initial block, and add to accumulator + emit("adds r%s, r%s, r14", acc[0], acc[0]) + emit("adcs r%s, r%s, #0", acc[1], acc[1]) + emit("adc r%s, r%s, #0", acc[2], acc[2]) + + # multiply by 2 + emit("adds r%s, r%s, r%s", acc[0], acc[0], acc[0]) + emit("adcs r%s, r%s, r%s", acc[1], acc[1], acc[1]) + emit("adc r%s, r%s, r%s", acc[2], acc[2], acc[2]) + + # add equal word + if limit == 4: + mulacc(acc, regs[2], regs[2]) + + # add old accumulator + emit("adds r%s, r%s, r%s", acc[0], acc[0], old_acc[0]) + emit("adcs r%s, r%s, r%s", acc[1], acc[1], old_acc[1]) + emit("adc r%s, r%s, #0", acc[2], acc[2]) + + # store + emit("stmia r0!, {r%s}", acc[0]) + print "" + +for i in xrange(1, s-3): + emit("mov r%s, #0", old_acc[1]) + tmp = [acc[1], acc[2]] + acc = [acc[0], old_acc[0], old_acc[1]] + old_acc = tmp + + # gather non-equal words + emit("umull r%s, r%s, r%s, r%s", acc[0], acc[1], regs[i], regs[s - 1]) + for j in xrange(1, (s-i)//2): + mulacc(acc, regs[i+j], regs[s - 1 - j]) + + # multiply by 2 + emit("adds r%s, r%s, r%s", acc[0], acc[0], acc[0]) + emit("adcs r%s, r%s, r%s", acc[1], acc[1], acc[1]) + emit("adc r%s, r%s, r%s", acc[2], acc[2], acc[2]) + + # add equal word (if any) + if ((s-i) % 2) != 0: + mulacc(acc, regs[i + (s-i)//2], regs[i + (s-i)//2]) + + # add old accumulator + emit("adds r%s, r%s, r%s", acc[0], acc[0], old_acc[0]) + emit("adcs r%s, r%s, r%s", acc[1], acc[1], old_acc[1]) + emit("adc r%s, r%s, #0", acc[2], acc[2]) + + # store + emit("stmia r0!, {r%s}", acc[0]) + print "" + +acc = acc[1:] + acc[:1] +emit("mov r%s, #0", acc[2]) +emit("umull r1, r%s, r%s, r%s", old_acc[1], regs[s - 3], regs[s - 1]) +emit("adds r1, r1, r1") +emit("adcs r%s, r%s, r%s", old_acc[1], old_acc[1], old_acc[1]) +emit("adc r%s, r%s, #0", acc[2], acc[2]) +emit("adds r%s, r%s, r1", acc[0], acc[0]) +emit("adcs r%s, r%s, r%s", acc[1], acc[1], old_acc[1]) +emit("adc r%s, r%s, #0", acc[2], acc[2]) +emit("umull r1, r%s, r%s, r%s", old_acc[1], regs[s - 2], regs[s - 2]) +emit("adds r%s, r%s, r1", acc[0], acc[0]) +emit("adcs r%s, r%s, r%s", acc[1], acc[1], old_acc[1]) +emit("adc r%s, r%s, #0", acc[2], acc[2]) +emit("stmia r0!, {r%s}", acc[0]) +print "" + +acc = acc[1:] + acc[:1] +emit("mov r%s, #0", acc[2]) +emit("umull r1, r%s, r%s, r%s", old_acc[1], regs[s - 2], regs[s - 1]) +emit("adds r1, r1, r1") +emit("adcs r%s, r%s, r%s", old_acc[1], old_acc[1], old_acc[1]) +emit("adc r%s, r%s, #0", acc[2], acc[2]) +emit("adds r%s, r%s, r1", acc[0], acc[0]) +emit("adcs r%s, r%s, r%s", acc[1], acc[1], old_acc[1]) +emit("adc r%s, r%s, #0", acc[2], acc[2]) +emit("stmia r0!, {r%s}", acc[0]) +print "" + +acc = acc[1:] + acc[:1] +emit("umull r1, r%s, r%s, r%s", old_acc[1], regs[s - 1], regs[s - 1]) +emit("adds r%s, r%s, r1", acc[0], acc[0]) +emit("adcs r%s, r%s, r%s", acc[1], acc[1], old_acc[1]) +emit("stmia r0!, {r%s}", acc[0]) +emit("stmia r0!, {r%s}", acc[1]) diff --git a/deps/picoquic/picotls/deps/micro-ecc/scripts/square_avr.py b/deps/picoquic/picotls/deps/micro-ecc/scripts/square_avr.py new file mode 100755 index 0000000..6571c3b --- /dev/null +++ b/deps/picoquic/picotls/deps/micro-ecc/scripts/square_avr.py @@ -0,0 +1,327 @@ +#!/usr/bin/env python + +import sys + +if len(sys.argv) < 2: + print "Provide the integer size in bytes" + sys.exit(1) + +size = int(sys.argv[1]) + +if size > 40: + print "This script doesn't work with integer size %s due to laziness" % (size) + sys.exit(1) + +init_size = size - 20 +if size < 20: + init_size = 0 + +def rg(i): + return i + 2 + +def lo(i): + return i + 2 + +def hi(i): + return i + 12 + +def emit(line, *args): + s = '"' + line + r' \n\t"' + print s % args + +#### set up registers +zero = "r25" +emit("ldi %s, 0", zero) # zero register + +if init_size > 0: + emit("movw r28, r26") # y = x + h = (init_size + 1)//2 + + for i in xrange(h): + emit("ld r%s, x+", lo(i)) + emit("adiw r28, %s", size - init_size) # move y to other end + for i in xrange(h): + emit("ld r%s, y+", hi(i)) + + emit("adiw r30, %s", size - init_size) # move z + + if init_size == 1: + emit("mul %s, %s", lo(0), hi(0)) + emit("st z+, r0") + emit("st z+, r1") + else: + #### first one + print "" + emit("ldi r23, 0") + emit("mul %s, %s", lo(0), hi(0)) + emit("st z+, r0") + emit("mov r22, r1") + print "" + + #### rest of initial block, with moving accumulator registers + acc = [22, 23, 24] + for r in xrange(1, h): + emit("ldi r%s, 0", acc[2]) + for i in xrange(0, (r+2)//2): + emit("mul r%s, r%s", lo(i), hi(r - i)) + emit("add r%s, r0", acc[0]) + emit("adc r%s, r1", acc[1]) + emit("adc r%s, %s", acc[2], zero) + emit("st z+, r%s", acc[0]) + print "" + acc = acc[1:] + acc[:1] + + lo_r = range(2, 2 + h) + hi_r = range(12, 12 + h) + + # now we need to start loading more from the high end + for r in xrange(h, init_size): + hi_r = hi_r[1:] + hi_r[:1] + emit("ld r%s, y+", hi_r[h-1]) + + emit("ldi r%s, 0", acc[2]) + for i in xrange(0, (r+2)//2): + emit("mul r%s, r%s", lo(i), hi_r[h - 1 - i]) + emit("add r%s, r0", acc[0]) + emit("adc r%s, r1", acc[1]) + emit("adc r%s, %s", acc[2], zero) + emit("st z+, r%s", acc[0]) + print "" + acc = acc[1:] + acc[:1] + + # loaded all of the high end bytes; now need to start loading the rest of the low end + for r in xrange(1, init_size-h): + lo_r = lo_r[1:] + lo_r[:1] + emit("ld r%s, x+", lo_r[h-1]) + + emit("ldi r%s, 0", acc[2]) + for i in xrange(0, (init_size+1 - r)//2): + emit("mul r%s, r%s", lo_r[i], hi_r[h - 1 - i]) + emit("add r%s, r0", acc[0]) + emit("adc r%s, r1", acc[1]) + emit("adc r%s, %s", acc[2], zero) + emit("st z+, r%s", acc[0]) + print "" + acc = acc[1:] + acc[:1] + + lo_r = lo_r[1:] + lo_r[:1] + emit("ld r%s, x+", lo_r[h-1]) + + # now we have loaded everything, and we just need to finish the last corner + for r in xrange(init_size-h, init_size-1): + emit("ldi r%s, 0", acc[2]) + for i in xrange(0, (init_size+1 - r)//2): + emit("mul r%s, r%s", lo_r[i], hi_r[h - 1 - i]) + emit("add r%s, r0", acc[0]) + emit("adc r%s, r1", acc[1]) + emit("adc r%s, %s", acc[2], zero) + emit("st z+, r%s", acc[0]) + print "" + acc = acc[1:] + acc[:1] + lo_r = lo_r[1:] + lo_r[:1] # make the indexing easy + + emit("mul r%s, r%s", lo_r[0], hi_r[h - 1]) + emit("add r%s, r0", acc[0]) + emit("adc r%s, r1", acc[1]) + emit("st z+, r%s", acc[0]) + emit("st z+, r%s", acc[1]) + print "" + emit("sbiw r26, %s", init_size) # reset x + emit("sbiw r30, %s", size + init_size) # reset z + +# TODO you could do more rows of size 20 here if your integers are larger than 40 bytes + +s = size - init_size + +for i in xrange(s): + emit("ld r%s, x+", rg(i)) + +#### first few columns +# NOTE: this is only valid if size >= 3 +print "" +emit("ldi r23, 0") +emit("mul r%s, r%s", rg(0), rg(0)) +emit("st z+, r0") +emit("mov r22, r1") +print "" +emit("ldi r24, 0") +emit("mul r%s, r%s", rg(0), rg(1)) +emit("add r22, r0") +emit("adc r23, r1") +emit("adc r24, %s", zero) +emit("add r22, r0") +emit("adc r23, r1") +emit("adc r24, %s", zero) +emit("st z+, r22") +print "" +emit("ldi r22, 0") +emit("mul r%s, r%s", rg(0), rg(2)) +emit("add r23, r0") +emit("adc r24, r1") +emit("adc r22, %s", zero) +emit("add r23, r0") +emit("adc r24, r1") +emit("adc r22, %s", zero) +emit("mul r%s, r%s", rg(1), rg(1)) +emit("add r23, r0") +emit("adc r24, r1") +emit("adc r22, %s", zero) +emit("st z+, r23") +print "" + +acc = [23, 24, 22] +old_acc = [28, 29] +for i in xrange(3, s): + emit("ldi r%s, 0", old_acc[1]) + tmp = [acc[1], acc[2]] + acc = [acc[0], old_acc[0], old_acc[1]] + old_acc = tmp + + # gather non-equal words + emit("mul r%s, r%s", rg(0), rg(i)) + emit("mov r%s, r0", acc[0]) + emit("mov r%s, r1", acc[1]) + for j in xrange(1, (i+1)//2): + emit("mul r%s, r%s", rg(j), rg(i-j)) + emit("add r%s, r0", acc[0]) + emit("adc r%s, r1", acc[1]) + emit("adc r%s, %s", acc[2], zero) + # multiply by 2 + emit("lsl r%s", acc[0]) + emit("rol r%s", acc[1]) + emit("rol r%s", acc[2]) + + # add equal word (if any) + if ((i+1) % 2) != 0: + emit("mul r%s, r%s", rg(i//2), rg(i//2)) + emit("add r%s, r0", acc[0]) + emit("adc r%s, r1", acc[1]) + emit("adc r%s, %s", acc[2], zero) + + # add old accumulator + emit("add r%s, r%s", acc[0], old_acc[0]) + emit("adc r%s, r%s", acc[1], old_acc[1]) + emit("adc r%s, %s", acc[2], zero) + + # store + emit("st z+, r%s", acc[0]) + print "" + +regs = range(2, 22) +for i in xrange(init_size): + regs = regs[1:] + regs[:1] + emit("ld r%s, x+", regs[19]) + + for limit in [18, 19]: + emit("ldi r%s, 0", old_acc[1]) + tmp = [acc[1], acc[2]] + acc = [acc[0], old_acc[0], old_acc[1]] + old_acc = tmp + + # gather non-equal words + emit("mul r%s, r%s", regs[0], regs[limit]) + emit("mov r%s, r0", acc[0]) + emit("mov r%s, r1", acc[1]) + for j in xrange(1, (limit+1)//2): + emit("mul r%s, r%s", regs[j], regs[limit-j]) + emit("add r%s, r0", acc[0]) + emit("adc r%s, r1", acc[1]) + emit("adc r%s, %s", acc[2], zero) + + emit("ld r0, z") # load stored value from initial block, and add to accumulator (note z does not increment) + emit("add r%s, r0", acc[0]) + emit("adc r%s, r25", acc[1]) + emit("adc r%s, r25", acc[2]) + + # multiply by 2 + emit("lsl r%s", acc[0]) + emit("rol r%s", acc[1]) + emit("rol r%s", acc[2]) + + # add equal word + if limit == 18: + emit("mul r%s, r%s", regs[9], regs[9]) + emit("add r%s, r0", acc[0]) + emit("adc r%s, r1", acc[1]) + emit("adc r%s, %s", acc[2], zero) + + # add old accumulator + emit("add r%s, r%s", acc[0], old_acc[0]) + emit("adc r%s, r%s", acc[1], old_acc[1]) + emit("adc r%s, %s", acc[2], zero) + + # store + emit("st z+, r%s", acc[0]) + print "" + +for i in xrange(1, s-3): + emit("ldi r%s, 0", old_acc[1]) + tmp = [acc[1], acc[2]] + acc = [acc[0], old_acc[0], old_acc[1]] + old_acc = tmp + + # gather non-equal words + emit("mul r%s, r%s", regs[i], regs[s - 1]) + emit("mov r%s, r0", acc[0]) + emit("mov r%s, r1", acc[1]) + for j in xrange(1, (s-i)//2): + emit("mul r%s, r%s", regs[i+j], regs[s - 1 - j]) + emit("add r%s, r0", acc[0]) + emit("adc r%s, r1", acc[1]) + emit("adc r%s, %s", acc[2], zero) + # multiply by 2 + emit("lsl r%s", acc[0]) + emit("rol r%s", acc[1]) + emit("rol r%s", acc[2]) + + # add equal word (if any) + if ((s-i) % 2) != 0: + emit("mul r%s, r%s", regs[i + (s-i)//2], regs[i + (s-i)//2]) + emit("add r%s, r0", acc[0]) + emit("adc r%s, r1", acc[1]) + emit("adc r%s, %s", acc[2], zero) + + # add old accumulator + emit("add r%s, r%s", acc[0], old_acc[0]) + emit("adc r%s, r%s", acc[1], old_acc[1]) + emit("adc r%s, %s", acc[2], zero) + + # store + emit("st z+, r%s", acc[0]) + print "" + +acc = acc[1:] + acc[:1] +emit("ldi r%s, 0", acc[2]) +emit("mul r%s, r%s", regs[17], regs[19]) +emit("add r%s, r0", acc[0]) +emit("adc r%s, r1", acc[1]) +emit("adc r%s, %s", acc[2], zero) +emit("add r%s, r0", acc[0]) +emit("adc r%s, r1", acc[1]) +emit("adc r%s, %s", acc[2], zero) +emit("mul r%s, r%s", regs[18], regs[18]) +emit("add r%s, r0", acc[0]) +emit("adc r%s, r1", acc[1]) +emit("adc r%s, %s", acc[2], zero) +emit("st z+, r%s", acc[0]) +print "" + +acc = acc[1:] + acc[:1] +emit("ldi r%s, 0", acc[2]) +emit("mul r%s, r%s", regs[18], regs[19]) +emit("add r%s, r0", acc[0]) +emit("adc r%s, r1", acc[1]) +emit("adc r%s, %s", acc[2], zero) +emit("add r%s, r0", acc[0]) +emit("adc r%s, r1", acc[1]) +emit("adc r%s, %s", acc[2], zero) +emit("st z+, r%s", acc[0]) +print "" + +emit("mul r%s, r%s", regs[19], regs[19]) +emit("add r%s, r0", acc[1]) +emit("adc r%s, r1", acc[2]) +emit("st z+, r%s", acc[1]) + +emit("st z+, r%s", acc[2]) +emit("eor r1, r1") diff --git a/deps/picoquic/picotls/deps/micro-ecc/test/ecc_test/ecc_test.ino b/deps/picoquic/picotls/deps/micro-ecc/test/ecc_test/ecc_test.ino new file mode 100644 index 0000000..c3c8900 --- /dev/null +++ b/deps/picoquic/picotls/deps/micro-ecc/test/ecc_test/ecc_test.ino @@ -0,0 +1,85 @@ +#include + +extern "C" { + +static int RNG(uint8_t *dest, unsigned size) { + // Use the least-significant bits from the ADC for an unconnected pin (or connected to a source of + // random noise). This can take a long time to generate random data if the result of analogRead(0) + // doesn't change very frequently. + while (size) { + uint8_t val = 0; + for (unsigned i = 0; i < 8; ++i) { + int init = analogRead(0); + int count = 0; + while (analogRead(0) == init) { + ++count; + } + + if (count == 0) { + val = (val << 1) | (init & 0x01); + } else { + val = (val << 1) | (count & 0x01); + } + } + *dest = val; + ++dest; + --size; + } + // NOTE: it would be a good idea to hash the resulting random data using SHA-256 or similar. + return 1; +} + +} // extern "C" + +void setup() { + Serial.begin(115200); + Serial.print("Testing ecc\n"); + uECC_set_rng(&RNG); +} + +void loop() { + const struct uECC_Curve_t * curve = uECC_secp160r1(); + uint8_t private1[21]; + uint8_t private2[21]; + + uint8_t public1[40]; + uint8_t public2[40]; + + uint8_t secret1[20]; + uint8_t secret2[20]; + + unsigned long a = millis(); + uECC_make_key(public1, private1, curve); + unsigned long b = millis(); + + Serial.print("Made key 1 in "); Serial.println(b-a); + a = millis(); + uECC_make_key(public2, private2, curve); + b = millis(); + Serial.print("Made key 2 in "); Serial.println(b-a); + + a = millis(); + int r = uECC_shared_secret(public2, private1, secret1, curve); + b = millis(); + Serial.print("Shared secret 1 in "); Serial.println(b-a); + if (!r) { + Serial.print("shared_secret() failed (1)\n"); + return; + } + + a = millis(); + r = uECC_shared_secret(public1, private2, secret2, curve); + b = millis(); + Serial.print("Shared secret 2 in "); Serial.println(b-a); + if (!r) { + Serial.print("shared_secret() failed (2)\n"); + return; + } + + if (memcmp(secret1, secret2, 20) != 0) { + Serial.print("Shared secrets are not identical!\n"); + } else { + Serial.print("Shared secrets are identical\n"); + } +} + diff --git a/deps/picoquic/picotls/deps/micro-ecc/test/emk_rules.py b/deps/picoquic/picotls/deps/micro-ecc/test/emk_rules.py new file mode 100644 index 0000000..956ccf5 --- /dev/null +++ b/deps/picoquic/picotls/deps/micro-ecc/test/emk_rules.py @@ -0,0 +1,4 @@ +c, link = emk.module("c", "link") +link.depdirs += [ + "$:proj:$" +] diff --git a/deps/picoquic/picotls/deps/micro-ecc/test/test_compress.c b/deps/picoquic/picotls/deps/micro-ecc/test/test_compress.c new file mode 100644 index 0000000..aef374c --- /dev/null +++ b/deps/picoquic/picotls/deps/micro-ecc/test/test_compress.c @@ -0,0 +1,79 @@ +/* Copyright 2014, Kenneth MacKay. Licensed under the BSD 2-clause license. */ + +#include "uECC.h" + +#include +#include + +#ifndef uECC_TEST_NUMBER_OF_ITERATIONS +#define uECC_TEST_NUMBER_OF_ITERATIONS 256 +#endif + +void vli_print(char *str, uint8_t *vli, unsigned int size) { + printf("%s ", str); + for(unsigned i=0; i +#include + +void vli_print(char *str, uint8_t *vli, unsigned int size) { + printf("%s ", str); + for(unsigned i=0; i +#include + +void vli_print(uint8_t *vli, unsigned int size) { + for(unsigned i=0; i +#include + +int main() { + int i, c; + uint8_t private[32] = {0}; + uint8_t public[64] = {0}; + uint8_t hash[32] = {0}; + uint8_t sig[64] = {0}; + + const struct uECC_Curve_t * curves[5]; + int num_curves = 0; +#if uECC_SUPPORTS_secp160r1 + curves[num_curves++] = uECC_secp160r1(); +#endif +#if uECC_SUPPORTS_secp192r1 + curves[num_curves++] = uECC_secp192r1(); +#endif +#if uECC_SUPPORTS_secp224r1 + curves[num_curves++] = uECC_secp224r1(); +#endif +#if uECC_SUPPORTS_secp256r1 + curves[num_curves++] = uECC_secp256r1(); +#endif +#if uECC_SUPPORTS_secp256k1 + curves[num_curves++] = uECC_secp256k1(); +#endif + + printf("Testing 256 signatures\n"); + for (c = 0; c < num_curves; ++c) { + for (i = 0; i < 256; ++i) { + printf("."); + fflush(stdout); + + if (!uECC_make_key(public, private, curves[c])) { + printf("uECC_make_key() failed\n"); + return 1; + } + memcpy(hash, public, sizeof(hash)); + + if (!uECC_sign(private, hash, sizeof(hash), sig, curves[c])) { + printf("uECC_sign() failed\n"); + return 1; + } + + if (!uECC_verify(public, hash, sizeof(hash), sig, curves[c])) { + printf("uECC_verify() failed\n"); + return 1; + } + } + printf("\n"); + } + + return 0; +} diff --git a/deps/picoquic/picotls/deps/micro-ecc/test/test_ecdsa_deterministic.c.example b/deps/picoquic/picotls/deps/micro-ecc/test/test_ecdsa_deterministic.c.example new file mode 100644 index 0000000..df9aa10 --- /dev/null +++ b/deps/picoquic/picotls/deps/micro-ecc/test/test_ecdsa_deterministic.c.example @@ -0,0 +1,93 @@ +/* Copyright 2014, Kenneth MacKay. Licensed under the BSD 2-clause license. */ + +#include "uECC.h" + +#include +#include + +#define SHA256_BLOCK_LENGTH 64 +#define SHA256_DIGEST_LENGTH 32 + +typedef struct SHA256_CTX { + uint32_t state[8]; + uint64_t bitcount; + uint8_t buffer[SHA256_BLOCK_LENGTH]; +} SHA256_CTX; + +extern void SHA256_Init(SHA256_CTX *ctx); +extern void SHA256_Update(SHA256_CTX *ctx, const uint8_t *message, size_t message_size); +extern void SHA256_Final(uint8_t digest[SHA256_DIGEST_LENGTH], SHA256_CTX *ctx); + +typedef struct SHA256_HashContext { + uECC_HashContext uECC; + SHA256_CTX ctx; +} SHA256_HashContext; + +static void init_SHA256(const uECC_HashContext *base) { + SHA256_HashContext *context = (SHA256_HashContext *)base; + SHA256_Init(&context->ctx); +} + +static void update_SHA256(const uECC_HashContext *base, + const uint8_t *message, + unsigned message_size) { + SHA256_HashContext *context = (SHA256_HashContext *)base; + SHA256_Update(&context->ctx, message, message_size); +} + +static void finish_SHA256(const uECC_HashContext *base, uint8_t *hash_result) { + SHA256_HashContext *context = (SHA256_HashContext *)base; + SHA256_Final(hash_result, &context->ctx); +} + +int main() { + int i, c; + uint8_t private[32] = {0}; + uint8_t public[64] = {0}; + uint8_t hash[32] = {0}; + uint8_t sig[64] = {0}; + + uint8_t tmp[2 * SHA256_DIGEST_LENGTH + SHA256_BLOCK_LENGTH]; + SHA256_HashContext ctx = {{ + &init_SHA256, + &update_SHA256, + &finish_SHA256, + SHA256_BLOCK_LENGTH, + SHA256_DIGEST_LENGTH, + tmp + }}; + + const struct uECC_Curve_t * curves[5]; + curves[0] = uECC_secp160r1(); + curves[1] = uECC_secp192r1(); + curves[2] = uECC_secp224r1(); + curves[3] = uECC_secp256r1(); + curves[4] = uECC_secp256k1(); + + printf("Testing 256 signatures\n"); + for (c = 0; c < 5; ++c) { + for (i = 0; i < 256; ++i) { + printf("."); + fflush(stdout); + + if (!uECC_make_key(public, private, curves[c])) { + printf("uECC_make_key() failed\n"); + return 1; + } + memcpy(hash, public, sizeof(hash)); + + if (!uECC_sign_deterministic(private, hash, sizeof(hash), &ctx.uECC, sig, curves[c])) { + printf("uECC_sign() failed\n"); + return 1; + } + + if (!uECC_verify(public, hash, sizeof(hash), sig, curves[c])) { + printf("uECC_verify() failed\n"); + return 1; + } + } + printf("\n"); + } + + return 0; +} diff --git a/deps/picoquic/picotls/deps/micro-ecc/types.h b/deps/picoquic/picotls/deps/micro-ecc/types.h new file mode 100644 index 0000000..9ee8143 --- /dev/null +++ b/deps/picoquic/picotls/deps/micro-ecc/types.h @@ -0,0 +1,108 @@ +/* Copyright 2015, Kenneth MacKay. Licensed under the BSD 2-clause license. */ + +#ifndef _UECC_TYPES_H_ +#define _UECC_TYPES_H_ + +#ifndef uECC_PLATFORM + #if __AVR__ + #define uECC_PLATFORM uECC_avr + #elif defined(__thumb2__) || defined(_M_ARMT) /* I think MSVC only supports Thumb-2 targets */ + #define uECC_PLATFORM uECC_arm_thumb2 + #elif defined(__thumb__) + #define uECC_PLATFORM uECC_arm_thumb + #elif defined(__arm__) || defined(_M_ARM) + #define uECC_PLATFORM uECC_arm + #elif defined(__aarch64__) + #define uECC_PLATFORM uECC_arm64 + #elif defined(__i386__) || defined(_M_IX86) || defined(_X86_) || defined(__I86__) + #define uECC_PLATFORM uECC_x86 + #elif defined(__amd64__) || defined(_M_X64) + #define uECC_PLATFORM uECC_x86_64 + #else + #define uECC_PLATFORM uECC_arch_other + #endif +#endif + +#ifndef uECC_ARM_USE_UMAAL + #if (uECC_PLATFORM == uECC_arm) && (__ARM_ARCH >= 6) + #define uECC_ARM_USE_UMAAL 1 + #elif (uECC_PLATFORM == uECC_arm_thumb2) && (__ARM_ARCH >= 6) && !__ARM_ARCH_7M__ + #define uECC_ARM_USE_UMAAL 1 + #else + #define uECC_ARM_USE_UMAAL 0 + #endif +#endif + +#ifndef uECC_WORD_SIZE + #if uECC_PLATFORM == uECC_avr + #define uECC_WORD_SIZE 1 + #elif (uECC_PLATFORM == uECC_x86_64 || uECC_PLATFORM == uECC_arm64) + #define uECC_WORD_SIZE 8 + #else + #define uECC_WORD_SIZE 4 + #endif +#endif + +#if (uECC_WORD_SIZE != 1) && (uECC_WORD_SIZE != 4) && (uECC_WORD_SIZE != 8) + #error "Unsupported value for uECC_WORD_SIZE" +#endif + +#if ((uECC_PLATFORM == uECC_avr) && (uECC_WORD_SIZE != 1)) + #pragma message ("uECC_WORD_SIZE must be 1 for AVR") + #undef uECC_WORD_SIZE + #define uECC_WORD_SIZE 1 +#endif + +#if ((uECC_PLATFORM == uECC_arm || uECC_PLATFORM == uECC_arm_thumb || \ + uECC_PLATFORM == uECC_arm_thumb2) && \ + (uECC_WORD_SIZE != 4)) + #pragma message ("uECC_WORD_SIZE must be 4 for ARM") + #undef uECC_WORD_SIZE + #define uECC_WORD_SIZE 4 +#endif + +#if defined(__SIZEOF_INT128__) || ((__clang_major__ * 100 + __clang_minor__) >= 302) + #define SUPPORTS_INT128 1 +#else + #define SUPPORTS_INT128 0 +#endif + +typedef int8_t wordcount_t; +typedef int16_t bitcount_t; +typedef int8_t cmpresult_t; + +#if (uECC_WORD_SIZE == 1) + +typedef uint8_t uECC_word_t; +typedef uint16_t uECC_dword_t; + +#define HIGH_BIT_SET 0x80 +#define uECC_WORD_BITS 8 +#define uECC_WORD_BITS_SHIFT 3 +#define uECC_WORD_BITS_MASK 0x07 + +#elif (uECC_WORD_SIZE == 4) + +typedef uint32_t uECC_word_t; +typedef uint64_t uECC_dword_t; + +#define HIGH_BIT_SET 0x80000000 +#define uECC_WORD_BITS 32 +#define uECC_WORD_BITS_SHIFT 5 +#define uECC_WORD_BITS_MASK 0x01F + +#elif (uECC_WORD_SIZE == 8) + +typedef uint64_t uECC_word_t; +#if SUPPORTS_INT128 +typedef unsigned __int128 uECC_dword_t; +#endif + +#define HIGH_BIT_SET 0x8000000000000000ull +#define uECC_WORD_BITS 64 +#define uECC_WORD_BITS_SHIFT 6 +#define uECC_WORD_BITS_MASK 0x03F + +#endif /* uECC_WORD_SIZE */ + +#endif /* _UECC_TYPES_H_ */ diff --git a/deps/picoquic/picotls/deps/micro-ecc/uECC.c b/deps/picoquic/picotls/deps/micro-ecc/uECC.c new file mode 100644 index 0000000..461f713 --- /dev/null +++ b/deps/picoquic/picotls/deps/micro-ecc/uECC.c @@ -0,0 +1,1634 @@ +/* Copyright 2014, Kenneth MacKay. Licensed under the BSD 2-clause license. */ + +#include "uECC.h" +#include "uECC_vli.h" + +#ifndef uECC_RNG_MAX_TRIES + #define uECC_RNG_MAX_TRIES 64 +#endif + +#if uECC_ENABLE_VLI_API + #define uECC_VLI_API +#else + #define uECC_VLI_API static +#endif + +#define CONCATX(a, ...) a ## __VA_ARGS__ +#define CONCAT(a, ...) CONCATX(a, __VA_ARGS__) + +#define STRX(a) #a +#define STR(a) STRX(a) + +#define EVAL(...) EVAL1(EVAL1(EVAL1(EVAL1(__VA_ARGS__)))) +#define EVAL1(...) EVAL2(EVAL2(EVAL2(EVAL2(__VA_ARGS__)))) +#define EVAL2(...) EVAL3(EVAL3(EVAL3(EVAL3(__VA_ARGS__)))) +#define EVAL3(...) EVAL4(EVAL4(EVAL4(EVAL4(__VA_ARGS__)))) +#define EVAL4(...) __VA_ARGS__ + +#define DEC_1 0 +#define DEC_2 1 +#define DEC_3 2 +#define DEC_4 3 +#define DEC_5 4 +#define DEC_6 5 +#define DEC_7 6 +#define DEC_8 7 +#define DEC_9 8 +#define DEC_10 9 +#define DEC_11 10 +#define DEC_12 11 +#define DEC_13 12 +#define DEC_14 13 +#define DEC_15 14 +#define DEC_16 15 +#define DEC_17 16 +#define DEC_18 17 +#define DEC_19 18 +#define DEC_20 19 +#define DEC_21 20 +#define DEC_22 21 +#define DEC_23 22 +#define DEC_24 23 +#define DEC_25 24 +#define DEC_26 25 +#define DEC_27 26 +#define DEC_28 27 +#define DEC_29 28 +#define DEC_30 29 +#define DEC_31 30 +#define DEC_32 31 + +#define DEC(N) CONCAT(DEC_, N) + +#define SECOND_ARG(_, val, ...) val +#define SOME_CHECK_0 ~, 0 +#define GET_SECOND_ARG(...) SECOND_ARG(__VA_ARGS__, SOME,) +#define SOME_OR_0(N) GET_SECOND_ARG(CONCAT(SOME_CHECK_, N)) + +#define EMPTY(...) +#define DEFER(...) __VA_ARGS__ EMPTY() + +#define REPEAT_NAME_0() REPEAT_0 +#define REPEAT_NAME_SOME() REPEAT_SOME +#define REPEAT_0(...) +#define REPEAT_SOME(N, stuff) DEFER(CONCAT(REPEAT_NAME_, SOME_OR_0(DEC(N))))()(DEC(N), stuff) stuff +#define REPEAT(N, stuff) EVAL(REPEAT_SOME(N, stuff)) + +#define REPEATM_NAME_0() REPEATM_0 +#define REPEATM_NAME_SOME() REPEATM_SOME +#define REPEATM_0(...) +#define REPEATM_SOME(N, macro) macro(N) \ + DEFER(CONCAT(REPEATM_NAME_, SOME_OR_0(DEC(N))))()(DEC(N), macro) +#define REPEATM(N, macro) EVAL(REPEATM_SOME(N, macro)) + +#include "platform-specific.inc" + +#if (uECC_WORD_SIZE == 1) + #if uECC_SUPPORTS_secp160r1 + #define uECC_MAX_WORDS 21 /* Due to the size of curve_n. */ + #endif + #if uECC_SUPPORTS_secp192r1 + #undef uECC_MAX_WORDS + #define uECC_MAX_WORDS 24 + #endif + #if uECC_SUPPORTS_secp224r1 + #undef uECC_MAX_WORDS + #define uECC_MAX_WORDS 28 + #endif + #if (uECC_SUPPORTS_secp256r1 || uECC_SUPPORTS_secp256k1) + #undef uECC_MAX_WORDS + #define uECC_MAX_WORDS 32 + #endif +#elif (uECC_WORD_SIZE == 4) + #if uECC_SUPPORTS_secp160r1 + #define uECC_MAX_WORDS 6 /* Due to the size of curve_n. */ + #endif + #if uECC_SUPPORTS_secp192r1 + #undef uECC_MAX_WORDS + #define uECC_MAX_WORDS 6 + #endif + #if uECC_SUPPORTS_secp224r1 + #undef uECC_MAX_WORDS + #define uECC_MAX_WORDS 7 + #endif + #if (uECC_SUPPORTS_secp256r1 || uECC_SUPPORTS_secp256k1) + #undef uECC_MAX_WORDS + #define uECC_MAX_WORDS 8 + #endif +#elif (uECC_WORD_SIZE == 8) + #if uECC_SUPPORTS_secp160r1 + #define uECC_MAX_WORDS 3 + #endif + #if uECC_SUPPORTS_secp192r1 + #undef uECC_MAX_WORDS + #define uECC_MAX_WORDS 3 + #endif + #if uECC_SUPPORTS_secp224r1 + #undef uECC_MAX_WORDS + #define uECC_MAX_WORDS 4 + #endif + #if (uECC_SUPPORTS_secp256r1 || uECC_SUPPORTS_secp256k1) + #undef uECC_MAX_WORDS + #define uECC_MAX_WORDS 4 + #endif +#endif /* uECC_WORD_SIZE */ + +#define BITS_TO_WORDS(num_bits) ((num_bits + ((uECC_WORD_SIZE * 8) - 1)) / (uECC_WORD_SIZE * 8)) +#define BITS_TO_BYTES(num_bits) ((num_bits + 7) / 8) + +struct uECC_Curve_t { + wordcount_t num_words; + wordcount_t num_bytes; + bitcount_t num_n_bits; + uECC_word_t p[uECC_MAX_WORDS]; + uECC_word_t n[uECC_MAX_WORDS]; + uECC_word_t G[uECC_MAX_WORDS * 2]; + uECC_word_t b[uECC_MAX_WORDS]; + void (*double_jacobian)(uECC_word_t * X1, + uECC_word_t * Y1, + uECC_word_t * Z1, + uECC_Curve curve); +#if uECC_SUPPORT_COMPRESSED_POINT + void (*mod_sqrt)(uECC_word_t *a, uECC_Curve curve); +#endif + void (*x_side)(uECC_word_t *result, const uECC_word_t *x, uECC_Curve curve); +#if (uECC_OPTIMIZATION_LEVEL > 0) + void (*mmod_fast)(uECC_word_t *result, uECC_word_t *product); +#endif +}; + +#if uECC_VLI_NATIVE_LITTLE_ENDIAN +static void bcopy(uint8_t *dst, + const uint8_t *src, + unsigned num_bytes) { + while (0 != num_bytes) { + num_bytes--; + dst[num_bytes] = src[num_bytes]; + } +} +#endif + +static cmpresult_t uECC_vli_cmp_unsafe(const uECC_word_t *left, + const uECC_word_t *right, + wordcount_t num_words); + +#if (uECC_PLATFORM == uECC_arm || uECC_PLATFORM == uECC_arm_thumb || \ + uECC_PLATFORM == uECC_arm_thumb2) + #include "asm_arm.inc" +#endif + +#if (uECC_PLATFORM == uECC_avr) + #include "asm_avr.inc" +#endif + +#if default_RNG_defined +static uECC_RNG_Function g_rng_function = &default_RNG; +#else +static uECC_RNG_Function g_rng_function = 0; +#endif + +void uECC_set_rng(uECC_RNG_Function rng_function) { + g_rng_function = rng_function; +} + +uECC_RNG_Function uECC_get_rng(void) { + return g_rng_function; +} + +int uECC_curve_private_key_size(uECC_Curve curve) { + return BITS_TO_BYTES(curve->num_n_bits); +} + +int uECC_curve_public_key_size(uECC_Curve curve) { + return 2 * curve->num_bytes; +} + +#if !asm_clear +uECC_VLI_API void uECC_vli_clear(uECC_word_t *vli, wordcount_t num_words) { + wordcount_t i; + for (i = 0; i < num_words; ++i) { + vli[i] = 0; + } +} +#endif /* !asm_clear */ + +/* Constant-time comparison to zero - secure way to compare long integers */ +/* Returns 1 if vli == 0, 0 otherwise. */ +uECC_VLI_API uECC_word_t uECC_vli_isZero(const uECC_word_t *vli, wordcount_t num_words) { + uECC_word_t bits = 0; + wordcount_t i; + for (i = 0; i < num_words; ++i) { + bits |= vli[i]; + } + return (bits == 0); +} + +/* Returns nonzero if bit 'bit' of vli is set. */ +uECC_VLI_API uECC_word_t uECC_vli_testBit(const uECC_word_t *vli, bitcount_t bit) { + return (vli[bit >> uECC_WORD_BITS_SHIFT] & ((uECC_word_t)1 << (bit & uECC_WORD_BITS_MASK))); +} + +/* Counts the number of words in vli. */ +static wordcount_t vli_numDigits(const uECC_word_t *vli, const wordcount_t max_words) { + wordcount_t i; + /* Search from the end until we find a non-zero digit. + We do it in reverse because we expect that most digits will be nonzero. */ + for (i = max_words - 1; i >= 0 && vli[i] == 0; --i) { + } + + return (i + 1); +} + +/* Counts the number of bits required to represent vli. */ +uECC_VLI_API bitcount_t uECC_vli_numBits(const uECC_word_t *vli, const wordcount_t max_words) { + uECC_word_t i; + uECC_word_t digit; + + wordcount_t num_digits = vli_numDigits(vli, max_words); + if (num_digits == 0) { + return 0; + } + + digit = vli[num_digits - 1]; + for (i = 0; digit; ++i) { + digit >>= 1; + } + + return (((bitcount_t)(num_digits - 1) << uECC_WORD_BITS_SHIFT) + i); +} + +/* Sets dest = src. */ +#if !asm_set +uECC_VLI_API void uECC_vli_set(uECC_word_t *dest, const uECC_word_t *src, wordcount_t num_words) { + wordcount_t i; + for (i = 0; i < num_words; ++i) { + dest[i] = src[i]; + } +} +#endif /* !asm_set */ + +/* Returns sign of left - right. */ +static cmpresult_t uECC_vli_cmp_unsafe(const uECC_word_t *left, + const uECC_word_t *right, + wordcount_t num_words) { + wordcount_t i; + for (i = num_words - 1; i >= 0; --i) { + if (left[i] > right[i]) { + return 1; + } else if (left[i] < right[i]) { + return -1; + } + } + return 0; +} + +/* Constant-time comparison function - secure way to compare long integers */ +/* Returns one if left == right, zero otherwise. */ +uECC_VLI_API uECC_word_t uECC_vli_equal(const uECC_word_t *left, + const uECC_word_t *right, + wordcount_t num_words) { + uECC_word_t diff = 0; + wordcount_t i; + for (i = num_words - 1; i >= 0; --i) { + diff |= (left[i] ^ right[i]); + } + return (diff == 0); +} + +uECC_VLI_API uECC_word_t uECC_vli_sub(uECC_word_t *result, + const uECC_word_t *left, + const uECC_word_t *right, + wordcount_t num_words); + +/* Returns sign of left - right, in constant time. */ +uECC_VLI_API cmpresult_t uECC_vli_cmp(const uECC_word_t *left, + const uECC_word_t *right, + wordcount_t num_words) { + uECC_word_t tmp[uECC_MAX_WORDS]; + uECC_word_t neg = !!uECC_vli_sub(tmp, left, right, num_words); + uECC_word_t equal = uECC_vli_isZero(tmp, num_words); + return (cmpresult_t)(!equal - 2 * neg); +} + +/* Computes vli = vli >> 1. */ +#if !asm_rshift1 +uECC_VLI_API void uECC_vli_rshift1(uECC_word_t *vli, wordcount_t num_words) { + uECC_word_t *end = vli; + uECC_word_t carry = 0; + + vli += num_words; + while (vli-- > end) { + uECC_word_t temp = *vli; + *vli = (temp >> 1) | carry; + carry = temp << (uECC_WORD_BITS - 1); + } +} +#endif /* !asm_rshift1 */ + +/* Computes result = left + right, returning carry. Can modify in place. */ +#if !asm_add +uECC_VLI_API uECC_word_t uECC_vli_add(uECC_word_t *result, + const uECC_word_t *left, + const uECC_word_t *right, + wordcount_t num_words) { + uECC_word_t carry = 0; + wordcount_t i; + for (i = 0; i < num_words; ++i) { + uECC_word_t sum = left[i] + right[i] + carry; + if (sum != left[i]) { + carry = (sum < left[i]); + } + result[i] = sum; + } + return carry; +} +#endif /* !asm_add */ + +/* Computes result = left - right, returning borrow. Can modify in place. */ +#if !asm_sub +uECC_VLI_API uECC_word_t uECC_vli_sub(uECC_word_t *result, + const uECC_word_t *left, + const uECC_word_t *right, + wordcount_t num_words) { + uECC_word_t borrow = 0; + wordcount_t i; + for (i = 0; i < num_words; ++i) { + uECC_word_t diff = left[i] - right[i] - borrow; + if (diff != left[i]) { + borrow = (diff > left[i]); + } + result[i] = diff; + } + return borrow; +} +#endif /* !asm_sub */ + +#if !asm_mult || (uECC_SQUARE_FUNC && !asm_square) || \ + (uECC_SUPPORTS_secp256k1 && (uECC_OPTIMIZATION_LEVEL > 0) && \ + ((uECC_WORD_SIZE == 1) || (uECC_WORD_SIZE == 8))) +static void muladd(uECC_word_t a, + uECC_word_t b, + uECC_word_t *r0, + uECC_word_t *r1, + uECC_word_t *r2) { +#if uECC_WORD_SIZE == 8 && !SUPPORTS_INT128 + uint64_t a0 = a & 0xffffffffull; + uint64_t a1 = a >> 32; + uint64_t b0 = b & 0xffffffffull; + uint64_t b1 = b >> 32; + + uint64_t i0 = a0 * b0; + uint64_t i1 = a0 * b1; + uint64_t i2 = a1 * b0; + uint64_t i3 = a1 * b1; + + uint64_t p0, p1; + + i2 += (i0 >> 32); + i2 += i1; + if (i2 < i1) { /* overflow */ + i3 += 0x100000000ull; + } + + p0 = (i0 & 0xffffffffull) | (i2 << 32); + p1 = i3 + (i2 >> 32); + + *r0 += p0; + *r1 += (p1 + (*r0 < p0)); + *r2 += ((*r1 < p1) || (*r1 == p1 && *r0 < p0)); +#else + uECC_dword_t p = (uECC_dword_t)a * b; + uECC_dword_t r01 = ((uECC_dword_t)(*r1) << uECC_WORD_BITS) | *r0; + r01 += p; + *r2 += (r01 < p); + *r1 = r01 >> uECC_WORD_BITS; + *r0 = (uECC_word_t)r01; +#endif +} +#endif /* muladd needed */ + +#if !asm_mult +uECC_VLI_API void uECC_vli_mult(uECC_word_t *result, + const uECC_word_t *left, + const uECC_word_t *right, + wordcount_t num_words) { + uECC_word_t r0 = 0; + uECC_word_t r1 = 0; + uECC_word_t r2 = 0; + wordcount_t i, k; + + /* Compute each digit of result in sequence, maintaining the carries. */ + for (k = 0; k < num_words; ++k) { + for (i = 0; i <= k; ++i) { + muladd(left[i], right[k - i], &r0, &r1, &r2); + } + result[k] = r0; + r0 = r1; + r1 = r2; + r2 = 0; + } + for (k = num_words; k < num_words * 2 - 1; ++k) { + for (i = (k + 1) - num_words; i < num_words; ++i) { + muladd(left[i], right[k - i], &r0, &r1, &r2); + } + result[k] = r0; + r0 = r1; + r1 = r2; + r2 = 0; + } + result[num_words * 2 - 1] = r0; +} +#endif /* !asm_mult */ + +#if uECC_SQUARE_FUNC + +#if !asm_square +static void mul2add(uECC_word_t a, + uECC_word_t b, + uECC_word_t *r0, + uECC_word_t *r1, + uECC_word_t *r2) { +#if uECC_WORD_SIZE == 8 && !SUPPORTS_INT128 + uint64_t a0 = a & 0xffffffffull; + uint64_t a1 = a >> 32; + uint64_t b0 = b & 0xffffffffull; + uint64_t b1 = b >> 32; + + uint64_t i0 = a0 * b0; + uint64_t i1 = a0 * b1; + uint64_t i2 = a1 * b0; + uint64_t i3 = a1 * b1; + + uint64_t p0, p1; + + i2 += (i0 >> 32); + i2 += i1; + if (i2 < i1) + { /* overflow */ + i3 += 0x100000000ull; + } + + p0 = (i0 & 0xffffffffull) | (i2 << 32); + p1 = i3 + (i2 >> 32); + + *r2 += (p1 >> 63); + p1 = (p1 << 1) | (p0 >> 63); + p0 <<= 1; + + *r0 += p0; + *r1 += (p1 + (*r0 < p0)); + *r2 += ((*r1 < p1) || (*r1 == p1 && *r0 < p0)); +#else + uECC_dword_t p = (uECC_dword_t)a * b; + uECC_dword_t r01 = ((uECC_dword_t)(*r1) << uECC_WORD_BITS) | *r0; + *r2 += (p >> (uECC_WORD_BITS * 2 - 1)); + p *= 2; + r01 += p; + *r2 += (r01 < p); + *r1 = r01 >> uECC_WORD_BITS; + *r0 = (uECC_word_t)r01; +#endif +} + +uECC_VLI_API void uECC_vli_square(uECC_word_t *result, + const uECC_word_t *left, + wordcount_t num_words) { + uECC_word_t r0 = 0; + uECC_word_t r1 = 0; + uECC_word_t r2 = 0; + + wordcount_t i, k; + + for (k = 0; k < num_words * 2 - 1; ++k) { + uECC_word_t min = (k < num_words ? 0 : (k + 1) - num_words); + for (i = min; i <= k && i <= k - i; ++i) { + if (i < k-i) { + mul2add(left[i], left[k - i], &r0, &r1, &r2); + } else { + muladd(left[i], left[k - i], &r0, &r1, &r2); + } + } + result[k] = r0; + r0 = r1; + r1 = r2; + r2 = 0; + } + + result[num_words * 2 - 1] = r0; +} +#endif /* !asm_square */ + +#else /* uECC_SQUARE_FUNC */ + +#if uECC_ENABLE_VLI_API +uECC_VLI_API void uECC_vli_square(uECC_word_t *result, + const uECC_word_t *left, + wordcount_t num_words) { + uECC_vli_mult(result, left, left, num_words); +} +#endif /* uECC_ENABLE_VLI_API */ + +#endif /* uECC_SQUARE_FUNC */ + +/* Computes result = (left + right) % mod. + Assumes that left < mod and right < mod, and that result does not overlap mod. */ +uECC_VLI_API void uECC_vli_modAdd(uECC_word_t *result, + const uECC_word_t *left, + const uECC_word_t *right, + const uECC_word_t *mod, + wordcount_t num_words) { + uECC_word_t carry = uECC_vli_add(result, left, right, num_words); + if (carry || uECC_vli_cmp_unsafe(mod, result, num_words) != 1) { + /* result > mod (result = mod + remainder), so subtract mod to get remainder. */ + uECC_vli_sub(result, result, mod, num_words); + } +} + +/* Computes result = (left - right) % mod. + Assumes that left < mod and right < mod, and that result does not overlap mod. */ +uECC_VLI_API void uECC_vli_modSub(uECC_word_t *result, + const uECC_word_t *left, + const uECC_word_t *right, + const uECC_word_t *mod, + wordcount_t num_words) { + uECC_word_t l_borrow = uECC_vli_sub(result, left, right, num_words); + if (l_borrow) { + /* In this case, result == -diff == (max int) - diff. Since -x % d == d - x, + we can get the correct result from result + mod (with overflow). */ + uECC_vli_add(result, result, mod, num_words); + } +} + +/* Computes result = product % mod, where product is 2N words long. */ +/* Currently only designed to work for curve_p or curve_n. */ +uECC_VLI_API void uECC_vli_mmod(uECC_word_t *result, + uECC_word_t *product, + const uECC_word_t *mod, + wordcount_t num_words) { + uECC_word_t mod_multiple[2 * uECC_MAX_WORDS]; + uECC_word_t tmp[2 * uECC_MAX_WORDS]; + uECC_word_t *v[2] = {tmp, product}; + uECC_word_t index; + + /* Shift mod so its highest set bit is at the maximum position. */ + bitcount_t shift = (num_words * 2 * uECC_WORD_BITS) - uECC_vli_numBits(mod, num_words); + wordcount_t word_shift = shift / uECC_WORD_BITS; + wordcount_t bit_shift = shift % uECC_WORD_BITS; + uECC_word_t carry = 0; + uECC_vli_clear(mod_multiple, word_shift); + if (bit_shift > 0) { + for(index = 0; index < (uECC_word_t)num_words; ++index) { + mod_multiple[word_shift + index] = (mod[index] << bit_shift) | carry; + carry = mod[index] >> (uECC_WORD_BITS - bit_shift); + } + } else { + uECC_vli_set(mod_multiple + word_shift, mod, num_words); + } + + for (index = 1; shift >= 0; --shift) { + uECC_word_t borrow = 0; + wordcount_t i; + for (i = 0; i < num_words * 2; ++i) { + uECC_word_t diff = v[index][i] - mod_multiple[i] - borrow; + if (diff != v[index][i]) { + borrow = (diff > v[index][i]); + } + v[1 - index][i] = diff; + } + index = !(index ^ borrow); /* Swap the index if there was no borrow */ + uECC_vli_rshift1(mod_multiple, num_words); + mod_multiple[num_words - 1] |= mod_multiple[num_words] << (uECC_WORD_BITS - 1); + uECC_vli_rshift1(mod_multiple + num_words, num_words); + } + uECC_vli_set(result, v[index], num_words); +} + +/* Computes result = (left * right) % mod. */ +uECC_VLI_API void uECC_vli_modMult(uECC_word_t *result, + const uECC_word_t *left, + const uECC_word_t *right, + const uECC_word_t *mod, + wordcount_t num_words) { + uECC_word_t product[2 * uECC_MAX_WORDS]; + uECC_vli_mult(product, left, right, num_words); + uECC_vli_mmod(result, product, mod, num_words); +} + +uECC_VLI_API void uECC_vli_modMult_fast(uECC_word_t *result, + const uECC_word_t *left, + const uECC_word_t *right, + uECC_Curve curve) { + uECC_word_t product[2 * uECC_MAX_WORDS]; + uECC_vli_mult(product, left, right, curve->num_words); +#if (uECC_OPTIMIZATION_LEVEL > 0) + curve->mmod_fast(result, product); +#else + uECC_vli_mmod(result, product, curve->p, curve->num_words); +#endif +} + +#if uECC_SQUARE_FUNC + +#if uECC_ENABLE_VLI_API +/* Computes result = left^2 % mod. */ +uECC_VLI_API void uECC_vli_modSquare(uECC_word_t *result, + const uECC_word_t *left, + const uECC_word_t *mod, + wordcount_t num_words) { + uECC_word_t product[2 * uECC_MAX_WORDS]; + uECC_vli_square(product, left, num_words); + uECC_vli_mmod(result, product, mod, num_words); +} +#endif /* uECC_ENABLE_VLI_API */ + +uECC_VLI_API void uECC_vli_modSquare_fast(uECC_word_t *result, + const uECC_word_t *left, + uECC_Curve curve) { + uECC_word_t product[2 * uECC_MAX_WORDS]; + uECC_vli_square(product, left, curve->num_words); +#if (uECC_OPTIMIZATION_LEVEL > 0) + curve->mmod_fast(result, product); +#else + uECC_vli_mmod(result, product, curve->p, curve->num_words); +#endif +} + +#else /* uECC_SQUARE_FUNC */ + +#if uECC_ENABLE_VLI_API +uECC_VLI_API void uECC_vli_modSquare(uECC_word_t *result, + const uECC_word_t *left, + const uECC_word_t *mod, + wordcount_t num_words) { + uECC_vli_modMult(result, left, left, mod, num_words); +} +#endif /* uECC_ENABLE_VLI_API */ + +uECC_VLI_API void uECC_vli_modSquare_fast(uECC_word_t *result, + const uECC_word_t *left, + uECC_Curve curve) { + uECC_vli_modMult_fast(result, left, left, curve); +} + +#endif /* uECC_SQUARE_FUNC */ + +#define EVEN(vli) (!(vli[0] & 1)) +static void vli_modInv_update(uECC_word_t *uv, + const uECC_word_t *mod, + wordcount_t num_words) { + uECC_word_t carry = 0; + if (!EVEN(uv)) { + carry = uECC_vli_add(uv, uv, mod, num_words); + } + uECC_vli_rshift1(uv, num_words); + if (carry) { + uv[num_words - 1] |= HIGH_BIT_SET; + } +} + +/* Computes result = (1 / input) % mod. All VLIs are the same size. + See "From Euclid's GCD to Montgomery Multiplication to the Great Divide" */ +uECC_VLI_API void uECC_vli_modInv(uECC_word_t *result, + const uECC_word_t *input, + const uECC_word_t *mod, + wordcount_t num_words) { + uECC_word_t a[uECC_MAX_WORDS], b[uECC_MAX_WORDS], u[uECC_MAX_WORDS], v[uECC_MAX_WORDS]; + cmpresult_t cmpResult; + + if (uECC_vli_isZero(input, num_words)) { + uECC_vli_clear(result, num_words); + return; + } + + uECC_vli_set(a, input, num_words); + uECC_vli_set(b, mod, num_words); + uECC_vli_clear(u, num_words); + u[0] = 1; + uECC_vli_clear(v, num_words); + while ((cmpResult = uECC_vli_cmp_unsafe(a, b, num_words)) != 0) { + if (EVEN(a)) { + uECC_vli_rshift1(a, num_words); + vli_modInv_update(u, mod, num_words); + } else if (EVEN(b)) { + uECC_vli_rshift1(b, num_words); + vli_modInv_update(v, mod, num_words); + } else if (cmpResult > 0) { + uECC_vli_sub(a, a, b, num_words); + uECC_vli_rshift1(a, num_words); + if (uECC_vli_cmp_unsafe(u, v, num_words) < 0) { + uECC_vli_add(u, u, mod, num_words); + } + uECC_vli_sub(u, u, v, num_words); + vli_modInv_update(u, mod, num_words); + } else { + uECC_vli_sub(b, b, a, num_words); + uECC_vli_rshift1(b, num_words); + if (uECC_vli_cmp_unsafe(v, u, num_words) < 0) { + uECC_vli_add(v, v, mod, num_words); + } + uECC_vli_sub(v, v, u, num_words); + vli_modInv_update(v, mod, num_words); + } + } + uECC_vli_set(result, u, num_words); +} + +/* ------ Point operations ------ */ + +#include "curve-specific.inc" + +/* Returns 1 if 'point' is the point at infinity, 0 otherwise. */ +#define EccPoint_isZero(point, curve) uECC_vli_isZero((point), (curve)->num_words * 2) + +/* Point multiplication algorithm using Montgomery's ladder with co-Z coordinates. +From http://eprint.iacr.org/2011/338.pdf +*/ + +/* Modify (x1, y1) => (x1 * z^2, y1 * z^3) */ +static void apply_z(uECC_word_t * X1, + uECC_word_t * Y1, + const uECC_word_t * const Z, + uECC_Curve curve) { + uECC_word_t t1[uECC_MAX_WORDS]; + + uECC_vli_modSquare_fast(t1, Z, curve); /* z^2 */ + uECC_vli_modMult_fast(X1, X1, t1, curve); /* x1 * z^2 */ + uECC_vli_modMult_fast(t1, t1, Z, curve); /* z^3 */ + uECC_vli_modMult_fast(Y1, Y1, t1, curve); /* y1 * z^3 */ +} + +/* P = (x1, y1) => 2P, (x2, y2) => P' */ +static void XYcZ_initial_double(uECC_word_t * X1, + uECC_word_t * Y1, + uECC_word_t * X2, + uECC_word_t * Y2, + const uECC_word_t * const initial_Z, + uECC_Curve curve) { + uECC_word_t z[uECC_MAX_WORDS]; + wordcount_t num_words = curve->num_words; + if (initial_Z) { + uECC_vli_set(z, initial_Z, num_words); + } else { + uECC_vli_clear(z, num_words); + z[0] = 1; + } + + uECC_vli_set(X2, X1, num_words); + uECC_vli_set(Y2, Y1, num_words); + + apply_z(X1, Y1, z, curve); + curve->double_jacobian(X1, Y1, z, curve); + apply_z(X2, Y2, z, curve); +} + +/* Input P = (x1, y1, Z), Q = (x2, y2, Z) + Output P' = (x1', y1', Z3), P + Q = (x3, y3, Z3) + or P => P', Q => P + Q +*/ +static void XYcZ_add(uECC_word_t * X1, + uECC_word_t * Y1, + uECC_word_t * X2, + uECC_word_t * Y2, + uECC_Curve curve) { + /* t1 = X1, t2 = Y1, t3 = X2, t4 = Y2 */ + uECC_word_t t5[uECC_MAX_WORDS]; + wordcount_t num_words = curve->num_words; + + uECC_vli_modSub(t5, X2, X1, curve->p, num_words); /* t5 = x2 - x1 */ + uECC_vli_modSquare_fast(t5, t5, curve); /* t5 = (x2 - x1)^2 = A */ + uECC_vli_modMult_fast(X1, X1, t5, curve); /* t1 = x1*A = B */ + uECC_vli_modMult_fast(X2, X2, t5, curve); /* t3 = x2*A = C */ + uECC_vli_modSub(Y2, Y2, Y1, curve->p, num_words); /* t4 = y2 - y1 */ + uECC_vli_modSquare_fast(t5, Y2, curve); /* t5 = (y2 - y1)^2 = D */ + + uECC_vli_modSub(t5, t5, X1, curve->p, num_words); /* t5 = D - B */ + uECC_vli_modSub(t5, t5, X2, curve->p, num_words); /* t5 = D - B - C = x3 */ + uECC_vli_modSub(X2, X2, X1, curve->p, num_words); /* t3 = C - B */ + uECC_vli_modMult_fast(Y1, Y1, X2, curve); /* t2 = y1*(C - B) */ + uECC_vli_modSub(X2, X1, t5, curve->p, num_words); /* t3 = B - x3 */ + uECC_vli_modMult_fast(Y2, Y2, X2, curve); /* t4 = (y2 - y1)*(B - x3) */ + uECC_vli_modSub(Y2, Y2, Y1, curve->p, num_words); /* t4 = y3 */ + + uECC_vli_set(X2, t5, num_words); +} + +/* Input P = (x1, y1, Z), Q = (x2, y2, Z) + Output P + Q = (x3, y3, Z3), P - Q = (x3', y3', Z3) + or P => P - Q, Q => P + Q +*/ +static void XYcZ_addC(uECC_word_t * X1, + uECC_word_t * Y1, + uECC_word_t * X2, + uECC_word_t * Y2, + uECC_Curve curve) { + /* t1 = X1, t2 = Y1, t3 = X2, t4 = Y2 */ + uECC_word_t t5[uECC_MAX_WORDS]; + uECC_word_t t6[uECC_MAX_WORDS]; + uECC_word_t t7[uECC_MAX_WORDS]; + wordcount_t num_words = curve->num_words; + + uECC_vli_modSub(t5, X2, X1, curve->p, num_words); /* t5 = x2 - x1 */ + uECC_vli_modSquare_fast(t5, t5, curve); /* t5 = (x2 - x1)^2 = A */ + uECC_vli_modMult_fast(X1, X1, t5, curve); /* t1 = x1*A = B */ + uECC_vli_modMult_fast(X2, X2, t5, curve); /* t3 = x2*A = C */ + uECC_vli_modAdd(t5, Y2, Y1, curve->p, num_words); /* t5 = y2 + y1 */ + uECC_vli_modSub(Y2, Y2, Y1, curve->p, num_words); /* t4 = y2 - y1 */ + + uECC_vli_modSub(t6, X2, X1, curve->p, num_words); /* t6 = C - B */ + uECC_vli_modMult_fast(Y1, Y1, t6, curve); /* t2 = y1 * (C - B) = E */ + uECC_vli_modAdd(t6, X1, X2, curve->p, num_words); /* t6 = B + C */ + uECC_vli_modSquare_fast(X2, Y2, curve); /* t3 = (y2 - y1)^2 = D */ + uECC_vli_modSub(X2, X2, t6, curve->p, num_words); /* t3 = D - (B + C) = x3 */ + + uECC_vli_modSub(t7, X1, X2, curve->p, num_words); /* t7 = B - x3 */ + uECC_vli_modMult_fast(Y2, Y2, t7, curve); /* t4 = (y2 - y1)*(B - x3) */ + uECC_vli_modSub(Y2, Y2, Y1, curve->p, num_words); /* t4 = (y2 - y1)*(B - x3) - E = y3 */ + + uECC_vli_modSquare_fast(t7, t5, curve); /* t7 = (y2 + y1)^2 = F */ + uECC_vli_modSub(t7, t7, t6, curve->p, num_words); /* t7 = F - (B + C) = x3' */ + uECC_vli_modSub(t6, t7, X1, curve->p, num_words); /* t6 = x3' - B */ + uECC_vli_modMult_fast(t6, t6, t5, curve); /* t6 = (y2+y1)*(x3' - B) */ + uECC_vli_modSub(Y1, t6, Y1, curve->p, num_words); /* t2 = (y2+y1)*(x3' - B) - E = y3' */ + + uECC_vli_set(X1, t7, num_words); +} + +/* result may overlap point. */ +static void EccPoint_mult(uECC_word_t * result, + const uECC_word_t * point, + const uECC_word_t * scalar, + const uECC_word_t * initial_Z, + bitcount_t num_bits, + uECC_Curve curve) { + /* R0 and R1 */ + uECC_word_t Rx[2][uECC_MAX_WORDS]; + uECC_word_t Ry[2][uECC_MAX_WORDS]; + uECC_word_t z[uECC_MAX_WORDS]; + bitcount_t i; + uECC_word_t nb; + wordcount_t num_words = curve->num_words; + + uECC_vli_set(Rx[1], point, num_words); + uECC_vli_set(Ry[1], point + num_words, num_words); + + XYcZ_initial_double(Rx[1], Ry[1], Rx[0], Ry[0], initial_Z, curve); + + for (i = num_bits - 2; i > 0; --i) { + nb = !uECC_vli_testBit(scalar, i); + XYcZ_addC(Rx[1 - nb], Ry[1 - nb], Rx[nb], Ry[nb], curve); + XYcZ_add(Rx[nb], Ry[nb], Rx[1 - nb], Ry[1 - nb], curve); + } + + nb = !uECC_vli_testBit(scalar, 0); + XYcZ_addC(Rx[1 - nb], Ry[1 - nb], Rx[nb], Ry[nb], curve); + + /* Find final 1/Z value. */ + uECC_vli_modSub(z, Rx[1], Rx[0], curve->p, num_words); /* X1 - X0 */ + uECC_vli_modMult_fast(z, z, Ry[1 - nb], curve); /* Yb * (X1 - X0) */ + uECC_vli_modMult_fast(z, z, point, curve); /* xP * Yb * (X1 - X0) */ + uECC_vli_modInv(z, z, curve->p, num_words); /* 1 / (xP * Yb * (X1 - X0)) */ + /* yP / (xP * Yb * (X1 - X0)) */ + uECC_vli_modMult_fast(z, z, point + num_words, curve); + uECC_vli_modMult_fast(z, z, Rx[1 - nb], curve); /* Xb * yP / (xP * Yb * (X1 - X0)) */ + /* End 1/Z calculation */ + + XYcZ_add(Rx[nb], Ry[nb], Rx[1 - nb], Ry[1 - nb], curve); + apply_z(Rx[0], Ry[0], z, curve); + + uECC_vli_set(result, Rx[0], num_words); + uECC_vli_set(result + num_words, Ry[0], num_words); +} + +static uECC_word_t regularize_k(const uECC_word_t * const k, + uECC_word_t *k0, + uECC_word_t *k1, + uECC_Curve curve) { + wordcount_t num_n_words = BITS_TO_WORDS(curve->num_n_bits); + bitcount_t num_n_bits = curve->num_n_bits; + uECC_word_t carry = uECC_vli_add(k0, k, curve->n, num_n_words) || + (num_n_bits < ((bitcount_t)num_n_words * uECC_WORD_SIZE * 8) && + uECC_vli_testBit(k0, num_n_bits)); + uECC_vli_add(k1, k0, curve->n, num_n_words); + return carry; +} + +static uECC_word_t EccPoint_compute_public_key(uECC_word_t *result, + uECC_word_t *private_key, + uECC_Curve curve) { + uECC_word_t tmp1[uECC_MAX_WORDS]; + uECC_word_t tmp2[uECC_MAX_WORDS]; + uECC_word_t *p2[2] = {tmp1, tmp2}; + uECC_word_t carry; + + /* Regularize the bitcount for the private key so that attackers cannot use a side channel + attack to learn the number of leading zeros. */ + carry = regularize_k(private_key, tmp1, tmp2, curve); + + EccPoint_mult(result, curve->G, p2[!carry], 0, curve->num_n_bits + 1, curve); + + if (EccPoint_isZero(result, curve)) { + return 0; + } + return 1; +} + +#if uECC_WORD_SIZE == 1 + +uECC_VLI_API void uECC_vli_nativeToBytes(uint8_t *bytes, + int num_bytes, + const uint8_t *native) { + wordcount_t i; + for (i = 0; i < num_bytes; ++i) { + bytes[i] = native[(num_bytes - 1) - i]; + } +} + +uECC_VLI_API void uECC_vli_bytesToNative(uint8_t *native, + const uint8_t *bytes, + int num_bytes) { + uECC_vli_nativeToBytes(native, num_bytes, bytes); +} + +#else + +uECC_VLI_API void uECC_vli_nativeToBytes(uint8_t *bytes, + int num_bytes, + const uECC_word_t *native) { + wordcount_t i; + for (i = 0; i < num_bytes; ++i) { + unsigned b = num_bytes - 1 - i; + bytes[i] = (uint8_t)(native[b / uECC_WORD_SIZE] >> (8 * (b % uECC_WORD_SIZE))); + } +} + +uECC_VLI_API void uECC_vli_bytesToNative(uECC_word_t *native, + const uint8_t *bytes, + int num_bytes) { + wordcount_t i; + uECC_vli_clear(native, (num_bytes + (uECC_WORD_SIZE - 1)) / uECC_WORD_SIZE); + for (i = 0; i < num_bytes; ++i) { + unsigned b = num_bytes - 1 - i; + native[b / uECC_WORD_SIZE] |= + (uECC_word_t)bytes[i] << (8 * (b % uECC_WORD_SIZE)); + } +} + +#endif /* uECC_WORD_SIZE */ + +/* Generates a random integer in the range 0 < random < top. + Both random and top have num_words words. */ +uECC_VLI_API int uECC_generate_random_int(uECC_word_t *random, + const uECC_word_t *top, + wordcount_t num_words) { + uECC_word_t mask = (uECC_word_t)-1; + uECC_word_t tries; + bitcount_t num_bits = uECC_vli_numBits(top, num_words); + + if (!g_rng_function) { + return 0; + } + + for (tries = 0; tries < uECC_RNG_MAX_TRIES; ++tries) { + if (!g_rng_function((uint8_t *)random, num_words * uECC_WORD_SIZE)) { + return 0; + } + random[num_words - 1] &= mask >> ((bitcount_t)(num_words * uECC_WORD_SIZE * 8 - num_bits)); + if (!uECC_vli_isZero(random, num_words) && + uECC_vli_cmp(top, random, num_words) == 1) { + return 1; + } + } + return 0; +} + +int uECC_make_key(uint8_t *public_key, + uint8_t *private_key, + uECC_Curve curve) { +#if uECC_VLI_NATIVE_LITTLE_ENDIAN + uECC_word_t *_private = (uECC_word_t *)private_key; + uECC_word_t *_public = (uECC_word_t *)public_key; +#else + uECC_word_t _private[uECC_MAX_WORDS]; + uECC_word_t _public[uECC_MAX_WORDS * 2]; +#endif + uECC_word_t tries; + + for (tries = 0; tries < uECC_RNG_MAX_TRIES; ++tries) { + if (!uECC_generate_random_int(_private, curve->n, BITS_TO_WORDS(curve->num_n_bits))) { + return 0; + } + + if (EccPoint_compute_public_key(_public, _private, curve)) { +#if uECC_VLI_NATIVE_LITTLE_ENDIAN == 0 + uECC_vli_nativeToBytes(private_key, BITS_TO_BYTES(curve->num_n_bits), _private); + uECC_vli_nativeToBytes(public_key, curve->num_bytes, _public); + uECC_vli_nativeToBytes( + public_key + curve->num_bytes, curve->num_bytes, _public + curve->num_words); +#endif + return 1; + } + } + return 0; +} + +int uECC_shared_secret(const uint8_t *public_key, + const uint8_t *private_key, + uint8_t *secret, + uECC_Curve curve) { + uECC_word_t _public[uECC_MAX_WORDS * 2]; + uECC_word_t _private[uECC_MAX_WORDS]; + + uECC_word_t tmp[uECC_MAX_WORDS]; + uECC_word_t *p2[2] = {_private, tmp}; + uECC_word_t *initial_Z = 0; + uECC_word_t carry; + wordcount_t num_words = curve->num_words; + wordcount_t num_bytes = curve->num_bytes; + +#if uECC_VLI_NATIVE_LITTLE_ENDIAN + bcopy((uint8_t *) _private, private_key, num_bytes); + bcopy((uint8_t *) _public, public_key, num_bytes*2); +#else + uECC_vli_bytesToNative(_private, private_key, BITS_TO_BYTES(curve->num_n_bits)); + uECC_vli_bytesToNative(_public, public_key, num_bytes); + uECC_vli_bytesToNative(_public + num_words, public_key + num_bytes, num_bytes); +#endif + + /* Regularize the bitcount for the private key so that attackers cannot use a side channel + attack to learn the number of leading zeros. */ + carry = regularize_k(_private, _private, tmp, curve); + + /* If an RNG function was specified, try to get a random initial Z value to improve + protection against side-channel attacks. */ + if (g_rng_function) { + if (!uECC_generate_random_int(p2[carry], curve->p, num_words)) { + return 0; + } + initial_Z = p2[carry]; + } + + EccPoint_mult(_public, _public, p2[!carry], initial_Z, curve->num_n_bits + 1, curve); +#if uECC_VLI_NATIVE_LITTLE_ENDIAN + bcopy((uint8_t *) secret, (uint8_t *) _public, num_bytes); +#else + uECC_vli_nativeToBytes(secret, num_bytes, _public); +#endif + return !EccPoint_isZero(_public, curve); +} + +#if uECC_SUPPORT_COMPRESSED_POINT +void uECC_compress(const uint8_t *public_key, uint8_t *compressed, uECC_Curve curve) { + wordcount_t i; + for (i = 0; i < curve->num_bytes; ++i) { + compressed[i+1] = public_key[i]; + } +#if uECC_VLI_NATIVE_LITTLE_ENDIAN + compressed[0] = 2 + (public_key[curve->num_bytes] & 0x01); +#else + compressed[0] = 2 + (public_key[curve->num_bytes * 2 - 1] & 0x01); +#endif +} + +void uECC_decompress(const uint8_t *compressed, uint8_t *public_key, uECC_Curve curve) { +#if uECC_VLI_NATIVE_LITTLE_ENDIAN + uECC_word_t *point = (uECC_word_t *)public_key; +#else + uECC_word_t point[uECC_MAX_WORDS * 2]; +#endif + uECC_word_t *y = point + curve->num_words; +#if uECC_VLI_NATIVE_LITTLE_ENDIAN + bcopy(public_key, compressed+1, curve->num_bytes); +#else + uECC_vli_bytesToNative(point, compressed + 1, curve->num_bytes); +#endif + curve->x_side(y, point, curve); + curve->mod_sqrt(y, curve); + + if ((y[0] & 0x01) != (compressed[0] & 0x01)) { + uECC_vli_sub(y, curve->p, y, curve->num_words); + } + +#if uECC_VLI_NATIVE_LITTLE_ENDIAN == 0 + uECC_vli_nativeToBytes(public_key, curve->num_bytes, point); + uECC_vli_nativeToBytes(public_key + curve->num_bytes, curve->num_bytes, y); +#endif +} +#endif /* uECC_SUPPORT_COMPRESSED_POINT */ + +int uECC_valid_point(const uECC_word_t *point, uECC_Curve curve) { + uECC_word_t tmp1[uECC_MAX_WORDS]; + uECC_word_t tmp2[uECC_MAX_WORDS]; + wordcount_t num_words = curve->num_words; + + /* The point at infinity is invalid. */ + if (EccPoint_isZero(point, curve)) { + return 0; + } + + /* x and y must be smaller than p. */ + if (uECC_vli_cmp_unsafe(curve->p, point, num_words) != 1 || + uECC_vli_cmp_unsafe(curve->p, point + num_words, num_words) != 1) { + return 0; + } + + uECC_vli_modSquare_fast(tmp1, point + num_words, curve); + curve->x_side(tmp2, point, curve); /* tmp2 = x^3 + ax + b */ + + /* Make sure that y^2 == x^3 + ax + b */ + return (int)(uECC_vli_equal(tmp1, tmp2, num_words)); +} + +int uECC_valid_public_key(const uint8_t *public_key, uECC_Curve curve) { +#if uECC_VLI_NATIVE_LITTLE_ENDIAN + uECC_word_t *_public = (uECC_word_t *)public_key; +#else + uECC_word_t _public[uECC_MAX_WORDS * 2]; +#endif + +#if uECC_VLI_NATIVE_LITTLE_ENDIAN == 0 + uECC_vli_bytesToNative(_public, public_key, curve->num_bytes); + uECC_vli_bytesToNative( + _public + curve->num_words, public_key + curve->num_bytes, curve->num_bytes); +#endif + return uECC_valid_point(_public, curve); +} + +int uECC_compute_public_key(const uint8_t *private_key, uint8_t *public_key, uECC_Curve curve) { +#if uECC_VLI_NATIVE_LITTLE_ENDIAN + uECC_word_t *_private = (uECC_word_t *)private_key; + uECC_word_t *_public = (uECC_word_t *)public_key; +#else + uECC_word_t _private[uECC_MAX_WORDS]; + uECC_word_t _public[uECC_MAX_WORDS * 2]; +#endif + +#if uECC_VLI_NATIVE_LITTLE_ENDIAN == 0 + uECC_vli_bytesToNative(_private, private_key, BITS_TO_BYTES(curve->num_n_bits)); +#endif + + /* Make sure the private key is in the range [1, n-1]. */ + if (uECC_vli_isZero(_private, BITS_TO_WORDS(curve->num_n_bits))) { + return 0; + } + + if (uECC_vli_cmp(curve->n, _private, BITS_TO_WORDS(curve->num_n_bits)) != 1) { + return 0; + } + + /* Compute public key. */ + if (!EccPoint_compute_public_key(_public, _private, curve)) { + return 0; + } + +#if uECC_VLI_NATIVE_LITTLE_ENDIAN == 0 + uECC_vli_nativeToBytes(public_key, curve->num_bytes, _public); + uECC_vli_nativeToBytes( + public_key + curve->num_bytes, curve->num_bytes, _public + curve->num_words); +#endif + return 1; +} + + +/* -------- ECDSA code -------- */ + +static void bits2int(uECC_word_t *native, + const uint8_t *bits, + unsigned bits_size, + uECC_Curve curve) { + unsigned num_n_bytes = BITS_TO_BYTES(curve->num_n_bits); + unsigned num_n_words = BITS_TO_WORDS(curve->num_n_bits); + int shift; + uECC_word_t carry; + uECC_word_t *ptr; + + if (bits_size > num_n_bytes) { + bits_size = num_n_bytes; + } + + uECC_vli_clear(native, num_n_words); +#if uECC_VLI_NATIVE_LITTLE_ENDIAN + bcopy((uint8_t *) native, bits, bits_size); +#else + uECC_vli_bytesToNative(native, bits, bits_size); +#endif + if (bits_size * 8 <= (unsigned)curve->num_n_bits) { + return; + } + shift = bits_size * 8 - curve->num_n_bits; + carry = 0; + ptr = native + num_n_words; + while (ptr-- > native) { + uECC_word_t temp = *ptr; + *ptr = (temp >> shift) | carry; + carry = temp << (uECC_WORD_BITS - shift); + } + + /* Reduce mod curve_n */ + if (uECC_vli_cmp_unsafe(curve->n, native, num_n_words) != 1) { + uECC_vli_sub(native, native, curve->n, num_n_words); + } +} + +static int uECC_sign_with_k(const uint8_t *private_key, + const uint8_t *message_hash, + unsigned hash_size, + uECC_word_t *k, + uint8_t *signature, + uECC_Curve curve) { + + uECC_word_t tmp[uECC_MAX_WORDS]; + uECC_word_t s[uECC_MAX_WORDS]; + uECC_word_t *k2[2] = {tmp, s}; +#if uECC_VLI_NATIVE_LITTLE_ENDIAN + uECC_word_t *p = (uECC_word_t *)signature; +#else + uECC_word_t p[uECC_MAX_WORDS * 2]; +#endif + uECC_word_t carry; + wordcount_t num_words = curve->num_words; + wordcount_t num_n_words = BITS_TO_WORDS(curve->num_n_bits); + bitcount_t num_n_bits = curve->num_n_bits; + + /* Make sure 0 < k < curve_n */ + if (uECC_vli_isZero(k, num_words) || uECC_vli_cmp(curve->n, k, num_n_words) != 1) { + return 0; + } + + carry = regularize_k(k, tmp, s, curve); + EccPoint_mult(p, curve->G, k2[!carry], 0, num_n_bits + 1, curve); + if (uECC_vli_isZero(p, num_words)) { + return 0; + } + + /* If an RNG function was specified, get a random number + to prevent side channel analysis of k. */ + if (!g_rng_function) { + uECC_vli_clear(tmp, num_n_words); + tmp[0] = 1; + } else if (!uECC_generate_random_int(tmp, curve->n, num_n_words)) { + return 0; + } + + /* Prevent side channel analysis of uECC_vli_modInv() to determine + bits of k / the private key by premultiplying by a random number */ + uECC_vli_modMult(k, k, tmp, curve->n, num_n_words); /* k' = rand * k */ + uECC_vli_modInv(k, k, curve->n, num_n_words); /* k = 1 / k' */ + uECC_vli_modMult(k, k, tmp, curve->n, num_n_words); /* k = 1 / k */ + +#if uECC_VLI_NATIVE_LITTLE_ENDIAN == 0 + uECC_vli_nativeToBytes(signature, curve->num_bytes, p); /* store r */ +#endif + +#if uECC_VLI_NATIVE_LITTLE_ENDIAN + bcopy((uint8_t *) tmp, private_key, BITS_TO_BYTES(curve->num_n_bits)); +#else + uECC_vli_bytesToNative(tmp, private_key, BITS_TO_BYTES(curve->num_n_bits)); /* tmp = d */ +#endif + + s[num_n_words - 1] = 0; + uECC_vli_set(s, p, num_words); + uECC_vli_modMult(s, tmp, s, curve->n, num_n_words); /* s = r*d */ + + bits2int(tmp, message_hash, hash_size, curve); + uECC_vli_modAdd(s, tmp, s, curve->n, num_n_words); /* s = e + r*d */ + uECC_vli_modMult(s, s, k, curve->n, num_n_words); /* s = (e + r*d) / k */ + if (uECC_vli_numBits(s, num_n_words) > (bitcount_t)curve->num_bytes * 8) { + return 0; + } +#if uECC_VLI_NATIVE_LITTLE_ENDIAN + bcopy((uint8_t *) signature + curve->num_bytes, (uint8_t *) s, curve->num_bytes); +#else + uECC_vli_nativeToBytes(signature + curve->num_bytes, curve->num_bytes, s); +#endif + return 1; +} + +int uECC_sign(const uint8_t *private_key, + const uint8_t *message_hash, + unsigned hash_size, + uint8_t *signature, + uECC_Curve curve) { + uECC_word_t k[uECC_MAX_WORDS]; + uECC_word_t tries; + + for (tries = 0; tries < uECC_RNG_MAX_TRIES; ++tries) { + if (!uECC_generate_random_int(k, curve->n, BITS_TO_WORDS(curve->num_n_bits))) { + return 0; + } + + if (uECC_sign_with_k(private_key, message_hash, hash_size, k, signature, curve)) { + return 1; + } + } + return 0; +} + +/* Compute an HMAC using K as a key (as in RFC 6979). Note that K is always + the same size as the hash result size. */ +static void HMAC_init(const uECC_HashContext *hash_context, const uint8_t *K) { + uint8_t *pad = hash_context->tmp + 2 * hash_context->result_size; + unsigned i; + for (i = 0; i < hash_context->result_size; ++i) + pad[i] = K[i] ^ 0x36; + for (; i < hash_context->block_size; ++i) + pad[i] = 0x36; + + hash_context->init_hash(hash_context); + hash_context->update_hash(hash_context, pad, hash_context->block_size); +} + +static void HMAC_update(const uECC_HashContext *hash_context, + const uint8_t *message, + unsigned message_size) { + hash_context->update_hash(hash_context, message, message_size); +} + +static void HMAC_finish(const uECC_HashContext *hash_context, + const uint8_t *K, + uint8_t *result) { + uint8_t *pad = hash_context->tmp + 2 * hash_context->result_size; + unsigned i; + for (i = 0; i < hash_context->result_size; ++i) + pad[i] = K[i] ^ 0x5c; + for (; i < hash_context->block_size; ++i) + pad[i] = 0x5c; + + hash_context->finish_hash(hash_context, result); + + hash_context->init_hash(hash_context); + hash_context->update_hash(hash_context, pad, hash_context->block_size); + hash_context->update_hash(hash_context, result, hash_context->result_size); + hash_context->finish_hash(hash_context, result); +} + +/* V = HMAC_K(V) */ +static void update_V(const uECC_HashContext *hash_context, uint8_t *K, uint8_t *V) { + HMAC_init(hash_context, K); + HMAC_update(hash_context, V, hash_context->result_size); + HMAC_finish(hash_context, K, V); +} + +/* Deterministic signing, similar to RFC 6979. Differences are: + * We just use H(m) directly rather than bits2octets(H(m)) + (it is not reduced modulo curve_n). + * We generate a value for k (aka T) directly rather than converting endianness. + + Layout of hash_context->tmp: | | (1 byte overlapped 0x00 or 0x01) / */ +int uECC_sign_deterministic(const uint8_t *private_key, + const uint8_t *message_hash, + unsigned hash_size, + const uECC_HashContext *hash_context, + uint8_t *signature, + uECC_Curve curve) { + uint8_t *K = hash_context->tmp; + uint8_t *V = K + hash_context->result_size; + wordcount_t num_bytes = curve->num_bytes; + wordcount_t num_n_words = BITS_TO_WORDS(curve->num_n_bits); + bitcount_t num_n_bits = curve->num_n_bits; + uECC_word_t tries; + unsigned i; + for (i = 0; i < hash_context->result_size; ++i) { + V[i] = 0x01; + K[i] = 0; + } + + /* K = HMAC_K(V || 0x00 || int2octets(x) || h(m)) */ + HMAC_init(hash_context, K); + V[hash_context->result_size] = 0x00; + HMAC_update(hash_context, V, hash_context->result_size + 1); + HMAC_update(hash_context, private_key, num_bytes); + HMAC_update(hash_context, message_hash, hash_size); + HMAC_finish(hash_context, K, K); + + update_V(hash_context, K, V); + + /* K = HMAC_K(V || 0x01 || int2octets(x) || h(m)) */ + HMAC_init(hash_context, K); + V[hash_context->result_size] = 0x01; + HMAC_update(hash_context, V, hash_context->result_size + 1); + HMAC_update(hash_context, private_key, num_bytes); + HMAC_update(hash_context, message_hash, hash_size); + HMAC_finish(hash_context, K, K); + + update_V(hash_context, K, V); + + for (tries = 0; tries < uECC_RNG_MAX_TRIES; ++tries) { + uECC_word_t T[uECC_MAX_WORDS]; + uint8_t *T_ptr = (uint8_t *)T; + wordcount_t T_bytes = 0; + for (;;) { + update_V(hash_context, K, V); + for (i = 0; i < hash_context->result_size; ++i) { + T_ptr[T_bytes++] = V[i]; + if (T_bytes >= num_n_words * uECC_WORD_SIZE) { + goto filled; + } + } + } + filled: + if ((bitcount_t)num_n_words * uECC_WORD_SIZE * 8 > num_n_bits) { + uECC_word_t mask = (uECC_word_t)-1; + T[num_n_words - 1] &= + mask >> ((bitcount_t)(num_n_words * uECC_WORD_SIZE * 8 - num_n_bits)); + } + + if (uECC_sign_with_k(private_key, message_hash, hash_size, T, signature, curve)) { + return 1; + } + + /* K = HMAC_K(V || 0x00) */ + HMAC_init(hash_context, K); + V[hash_context->result_size] = 0x00; + HMAC_update(hash_context, V, hash_context->result_size + 1); + HMAC_finish(hash_context, K, K); + + update_V(hash_context, K, V); + } + return 0; +} + +static bitcount_t smax(bitcount_t a, bitcount_t b) { + return (a > b ? a : b); +} + +int uECC_verify(const uint8_t *public_key, + const uint8_t *message_hash, + unsigned hash_size, + const uint8_t *signature, + uECC_Curve curve) { + uECC_word_t u1[uECC_MAX_WORDS], u2[uECC_MAX_WORDS]; + uECC_word_t z[uECC_MAX_WORDS]; + uECC_word_t sum[uECC_MAX_WORDS * 2]; + uECC_word_t rx[uECC_MAX_WORDS]; + uECC_word_t ry[uECC_MAX_WORDS]; + uECC_word_t tx[uECC_MAX_WORDS]; + uECC_word_t ty[uECC_MAX_WORDS]; + uECC_word_t tz[uECC_MAX_WORDS]; + const uECC_word_t *points[4]; + const uECC_word_t *point; + bitcount_t num_bits; + bitcount_t i; +#if uECC_VLI_NATIVE_LITTLE_ENDIAN + uECC_word_t *_public = (uECC_word_t *)public_key; +#else + uECC_word_t _public[uECC_MAX_WORDS * 2]; +#endif + uECC_word_t r[uECC_MAX_WORDS], s[uECC_MAX_WORDS]; + wordcount_t num_words = curve->num_words; + wordcount_t num_n_words = BITS_TO_WORDS(curve->num_n_bits); + + rx[num_n_words - 1] = 0; + r[num_n_words - 1] = 0; + s[num_n_words - 1] = 0; + +#if uECC_VLI_NATIVE_LITTLE_ENDIAN + bcopy((uint8_t *) r, signature, curve->num_bytes); + bcopy((uint8_t *) s, signature + curve->num_bytes, curve->num_bytes); +#else + uECC_vli_bytesToNative(_public, public_key, curve->num_bytes); + uECC_vli_bytesToNative( + _public + num_words, public_key + curve->num_bytes, curve->num_bytes); + uECC_vli_bytesToNative(r, signature, curve->num_bytes); + uECC_vli_bytesToNative(s, signature + curve->num_bytes, curve->num_bytes); +#endif + + /* r, s must not be 0. */ + if (uECC_vli_isZero(r, num_words) || uECC_vli_isZero(s, num_words)) { + return 0; + } + + /* r, s must be < n. */ + if (uECC_vli_cmp_unsafe(curve->n, r, num_n_words) != 1 || + uECC_vli_cmp_unsafe(curve->n, s, num_n_words) != 1) { + return 0; + } + + /* Calculate u1 and u2. */ + uECC_vli_modInv(z, s, curve->n, num_n_words); /* z = 1/s */ + u1[num_n_words - 1] = 0; + bits2int(u1, message_hash, hash_size, curve); + uECC_vli_modMult(u1, u1, z, curve->n, num_n_words); /* u1 = e/s */ + uECC_vli_modMult(u2, r, z, curve->n, num_n_words); /* u2 = r/s */ + + /* Calculate sum = G + Q. */ + uECC_vli_set(sum, _public, num_words); + uECC_vli_set(sum + num_words, _public + num_words, num_words); + uECC_vli_set(tx, curve->G, num_words); + uECC_vli_set(ty, curve->G + num_words, num_words); + uECC_vli_modSub(z, sum, tx, curve->p, num_words); /* z = x2 - x1 */ + XYcZ_add(tx, ty, sum, sum + num_words, curve); + uECC_vli_modInv(z, z, curve->p, num_words); /* z = 1/z */ + apply_z(sum, sum + num_words, z, curve); + + /* Use Shamir's trick to calculate u1*G + u2*Q */ + points[0] = 0; + points[1] = curve->G; + points[2] = _public; + points[3] = sum; + num_bits = smax(uECC_vli_numBits(u1, num_n_words), + uECC_vli_numBits(u2, num_n_words)); + + point = points[(!!uECC_vli_testBit(u1, num_bits - 1)) | + ((!!uECC_vli_testBit(u2, num_bits - 1)) << 1)]; + uECC_vli_set(rx, point, num_words); + uECC_vli_set(ry, point + num_words, num_words); + uECC_vli_clear(z, num_words); + z[0] = 1; + + for (i = num_bits - 2; i >= 0; --i) { + uECC_word_t index; + curve->double_jacobian(rx, ry, z, curve); + + index = (!!uECC_vli_testBit(u1, i)) | ((!!uECC_vli_testBit(u2, i)) << 1); + point = points[index]; + if (point) { + uECC_vli_set(tx, point, num_words); + uECC_vli_set(ty, point + num_words, num_words); + apply_z(tx, ty, z, curve); + uECC_vli_modSub(tz, rx, tx, curve->p, num_words); /* Z = x2 - x1 */ + XYcZ_add(tx, ty, rx, ry, curve); + uECC_vli_modMult_fast(z, z, tz, curve); + } + } + + uECC_vli_modInv(z, z, curve->p, num_words); /* Z = 1/Z */ + apply_z(rx, ry, z, curve); + + /* v = x1 (mod n) */ + if (uECC_vli_cmp_unsafe(curve->n, rx, num_n_words) != 1) { + uECC_vli_sub(rx, rx, curve->n, num_n_words); + } + + /* Accept only if v == r. */ + return (int)(uECC_vli_equal(rx, r, num_words)); +} + +#if uECC_ENABLE_VLI_API + +unsigned uECC_curve_num_words(uECC_Curve curve) { + return curve->num_words; +} + +unsigned uECC_curve_num_bytes(uECC_Curve curve) { + return curve->num_bytes; +} + +unsigned uECC_curve_num_bits(uECC_Curve curve) { + return curve->num_bytes * 8; +} + +unsigned uECC_curve_num_n_words(uECC_Curve curve) { + return BITS_TO_WORDS(curve->num_n_bits); +} + +unsigned uECC_curve_num_n_bytes(uECC_Curve curve) { + return BITS_TO_BYTES(curve->num_n_bits); +} + +unsigned uECC_curve_num_n_bits(uECC_Curve curve) { + return curve->num_n_bits; +} + +const uECC_word_t *uECC_curve_p(uECC_Curve curve) { + return curve->p; +} + +const uECC_word_t *uECC_curve_n(uECC_Curve curve) { + return curve->n; +} + +const uECC_word_t *uECC_curve_G(uECC_Curve curve) { + return curve->G; +} + +const uECC_word_t *uECC_curve_b(uECC_Curve curve) { + return curve->b; +} + +#if uECC_SUPPORT_COMPRESSED_POINT +void uECC_vli_mod_sqrt(uECC_word_t *a, uECC_Curve curve) { + curve->mod_sqrt(a, curve); +} +#endif + +void uECC_vli_mmod_fast(uECC_word_t *result, uECC_word_t *product, uECC_Curve curve) { +#if (uECC_OPTIMIZATION_LEVEL > 0) + curve->mmod_fast(result, product); +#else + uECC_vli_mmod(result, product, curve->p, curve->num_words); +#endif +} + +void uECC_point_mult(uECC_word_t *result, + const uECC_word_t *point, + const uECC_word_t *scalar, + uECC_Curve curve) { + uECC_word_t tmp1[uECC_MAX_WORDS]; + uECC_word_t tmp2[uECC_MAX_WORDS]; + uECC_word_t *p2[2] = {tmp1, tmp2}; + uECC_word_t carry = regularize_k(scalar, tmp1, tmp2, curve); + + EccPoint_mult(result, point, p2[!carry], 0, curve->num_n_bits + 1, curve); +} + +#endif /* uECC_ENABLE_VLI_API */ diff --git a/deps/picoquic/picotls/deps/micro-ecc/uECC.h b/deps/picoquic/picotls/deps/micro-ecc/uECC.h new file mode 100644 index 0000000..9911763 --- /dev/null +++ b/deps/picoquic/picotls/deps/micro-ecc/uECC.h @@ -0,0 +1,362 @@ +/* Copyright 2014, Kenneth MacKay. Licensed under the BSD 2-clause license. */ + +#ifndef _UECC_H_ +#define _UECC_H_ + +#include + +/* Platform selection options. +If uECC_PLATFORM is not defined, the code will try to guess it based on compiler macros. +Possible values for uECC_PLATFORM are defined below: */ +#define uECC_arch_other 0 +#define uECC_x86 1 +#define uECC_x86_64 2 +#define uECC_arm 3 +#define uECC_arm_thumb 4 +#define uECC_arm_thumb2 5 +#define uECC_arm64 6 +#define uECC_avr 7 + +/* If desired, you can define uECC_WORD_SIZE as appropriate for your platform (1, 4, or 8 bytes). +If uECC_WORD_SIZE is not explicitly defined then it will be automatically set based on your +platform. */ + +/* Optimization level; trade speed for code size. + Larger values produce code that is faster but larger. + Currently supported values are 0 - 4; 0 is unusably slow for most applications. + Optimization level 4 currently only has an effect ARM platforms where more than one + curve is enabled. */ +#ifndef uECC_OPTIMIZATION_LEVEL + #define uECC_OPTIMIZATION_LEVEL 2 +#endif + +/* uECC_SQUARE_FUNC - If enabled (defined as nonzero), this will cause a specific function to be +used for (scalar) squaring instead of the generic multiplication function. This can make things +faster somewhat faster, but increases the code size. */ +#ifndef uECC_SQUARE_FUNC + #define uECC_SQUARE_FUNC 0 +#endif + +/* uECC_VLI_NATIVE_LITTLE_ENDIAN - If enabled (defined as nonzero), this will switch to native +little-endian format for *all* arrays passed in and out of the public API. This includes public +and private keys, shared secrets, signatures and message hashes. +Using this switch reduces the amount of call stack memory used by uECC, since less intermediate +translations are required. +Note that this will *only* work on native little-endian processors and it will treat the uint8_t +arrays passed into the public API as word arrays, therefore requiring the provided byte arrays +to be word aligned on architectures that do not support unaligned accesses. */ +#ifndef uECC_VLI_NATIVE_LITTLE_ENDIAN + #define uECC_VLI_NATIVE_LITTLE_ENDIAN 0 +#endif + +/* Curve support selection. Set to 0 to remove that curve. */ +#ifndef uECC_SUPPORTS_secp160r1 + #define uECC_SUPPORTS_secp160r1 1 +#endif +#ifndef uECC_SUPPORTS_secp192r1 + #define uECC_SUPPORTS_secp192r1 1 +#endif +#ifndef uECC_SUPPORTS_secp224r1 + #define uECC_SUPPORTS_secp224r1 1 +#endif +#ifndef uECC_SUPPORTS_secp256r1 + #define uECC_SUPPORTS_secp256r1 1 +#endif +#ifndef uECC_SUPPORTS_secp256k1 + #define uECC_SUPPORTS_secp256k1 1 +#endif + +/* Specifies whether compressed point format is supported. + Set to 0 to disable point compression/decompression functions. */ +#ifndef uECC_SUPPORT_COMPRESSED_POINT + #define uECC_SUPPORT_COMPRESSED_POINT 1 +#endif + +struct uECC_Curve_t; +typedef const struct uECC_Curve_t * uECC_Curve; + +#ifdef __cplusplus +extern "C" +{ +#endif + +#if uECC_SUPPORTS_secp160r1 +uECC_Curve uECC_secp160r1(void); +#endif +#if uECC_SUPPORTS_secp192r1 +uECC_Curve uECC_secp192r1(void); +#endif +#if uECC_SUPPORTS_secp224r1 +uECC_Curve uECC_secp224r1(void); +#endif +#if uECC_SUPPORTS_secp256r1 +uECC_Curve uECC_secp256r1(void); +#endif +#if uECC_SUPPORTS_secp256k1 +uECC_Curve uECC_secp256k1(void); +#endif + +/* uECC_RNG_Function type +The RNG function should fill 'size' random bytes into 'dest'. It should return 1 if +'dest' was filled with random data, or 0 if the random data could not be generated. +The filled-in values should be either truly random, or from a cryptographically-secure PRNG. + +A correctly functioning RNG function must be set (using uECC_set_rng()) before calling +uECC_make_key() or uECC_sign(). + +Setting a correctly functioning RNG function improves the resistance to side-channel attacks +for uECC_shared_secret() and uECC_sign_deterministic(). + +A correct RNG function is set by default when building for Windows, Linux, or OS X. +If you are building on another POSIX-compliant system that supports /dev/random or /dev/urandom, +you can define uECC_POSIX to use the predefined RNG. For embedded platforms there is no predefined +RNG function; you must provide your own. +*/ +typedef int (*uECC_RNG_Function)(uint8_t *dest, unsigned size); + +/* uECC_set_rng() function. +Set the function that will be used to generate random bytes. The RNG function should +return 1 if the random data was generated, or 0 if the random data could not be generated. + +On platforms where there is no predefined RNG function (eg embedded platforms), this must +be called before uECC_make_key() or uECC_sign() are used. + +Inputs: + rng_function - The function that will be used to generate random bytes. +*/ +void uECC_set_rng(uECC_RNG_Function rng_function); + +/* uECC_get_rng() function. + +Returns the function that will be used to generate random bytes. +*/ +uECC_RNG_Function uECC_get_rng(void); + +/* uECC_curve_private_key_size() function. + +Returns the size of a private key for the curve in bytes. +*/ +int uECC_curve_private_key_size(uECC_Curve curve); + +/* uECC_curve_public_key_size() function. + +Returns the size of a public key for the curve in bytes. +*/ +int uECC_curve_public_key_size(uECC_Curve curve); + +/* uECC_make_key() function. +Create a public/private key pair. + +Outputs: + public_key - Will be filled in with the public key. Must be at least 2 * the curve size + (in bytes) long. For example, if the curve is secp256r1, public_key must be 64 + bytes long. + private_key - Will be filled in with the private key. Must be as long as the curve order; this + is typically the same as the curve size, except for secp160r1. For example, if the + curve is secp256r1, private_key must be 32 bytes long. + + For secp160r1, private_key must be 21 bytes long! Note that the first byte will + almost always be 0 (there is about a 1 in 2^80 chance of it being non-zero). + +Returns 1 if the key pair was generated successfully, 0 if an error occurred. +*/ +int uECC_make_key(uint8_t *public_key, uint8_t *private_key, uECC_Curve curve); + +/* uECC_shared_secret() function. +Compute a shared secret given your secret key and someone else's public key. +Note: It is recommended that you hash the result of uECC_shared_secret() before using it for +symmetric encryption or HMAC. + +Inputs: + public_key - The public key of the remote party. + private_key - Your private key. + +Outputs: + secret - Will be filled in with the shared secret value. Must be the same size as the + curve size; for example, if the curve is secp256r1, secret must be 32 bytes long. + +Returns 1 if the shared secret was generated successfully, 0 if an error occurred. +*/ +int uECC_shared_secret(const uint8_t *public_key, + const uint8_t *private_key, + uint8_t *secret, + uECC_Curve curve); + +#if uECC_SUPPORT_COMPRESSED_POINT +/* uECC_compress() function. +Compress a public key. + +Inputs: + public_key - The public key to compress. + +Outputs: + compressed - Will be filled in with the compressed public key. Must be at least + (curve size + 1) bytes long; for example, if the curve is secp256r1, + compressed must be 33 bytes long. +*/ +void uECC_compress(const uint8_t *public_key, uint8_t *compressed, uECC_Curve curve); + +/* uECC_decompress() function. +Decompress a compressed public key. + +Inputs: + compressed - The compressed public key. + +Outputs: + public_key - Will be filled in with the decompressed public key. +*/ +void uECC_decompress(const uint8_t *compressed, uint8_t *public_key, uECC_Curve curve); +#endif /* uECC_SUPPORT_COMPRESSED_POINT */ + +/* uECC_valid_public_key() function. +Check to see if a public key is valid. + +Note that you are not required to check for a valid public key before using any other uECC +functions. However, you may wish to avoid spending CPU time computing a shared secret or +verifying a signature using an invalid public key. + +Inputs: + public_key - The public key to check. + +Returns 1 if the public key is valid, 0 if it is invalid. +*/ +int uECC_valid_public_key(const uint8_t *public_key, uECC_Curve curve); + +/* uECC_compute_public_key() function. +Compute the corresponding public key for a private key. + +Inputs: + private_key - The private key to compute the public key for + +Outputs: + public_key - Will be filled in with the corresponding public key + +Returns 1 if the key was computed successfully, 0 if an error occurred. +*/ +int uECC_compute_public_key(const uint8_t *private_key, uint8_t *public_key, uECC_Curve curve); + +/* uECC_sign() function. +Generate an ECDSA signature for a given hash value. + +Usage: Compute a hash of the data you wish to sign (SHA-2 is recommended) and pass it in to +this function along with your private key. + +Inputs: + private_key - Your private key. + message_hash - The hash of the message to sign. + hash_size - The size of message_hash in bytes. + +Outputs: + signature - Will be filled in with the signature value. Must be at least 2 * curve size long. + For example, if the curve is secp256r1, signature must be 64 bytes long. + +Returns 1 if the signature generated successfully, 0 if an error occurred. +*/ +int uECC_sign(const uint8_t *private_key, + const uint8_t *message_hash, + unsigned hash_size, + uint8_t *signature, + uECC_Curve curve); + +/* uECC_HashContext structure. +This is used to pass in an arbitrary hash function to uECC_sign_deterministic(). +The structure will be used for multiple hash computations; each time a new hash +is computed, init_hash() will be called, followed by one or more calls to +update_hash(), and finally a call to finish_hash() to produce the resulting hash. + +The intention is that you will create a structure that includes uECC_HashContext +followed by any hash-specific data. For example: + +typedef struct SHA256_HashContext { + uECC_HashContext uECC; + SHA256_CTX ctx; +} SHA256_HashContext; + +void init_SHA256(uECC_HashContext *base) { + SHA256_HashContext *context = (SHA256_HashContext *)base; + SHA256_Init(&context->ctx); +} + +void update_SHA256(uECC_HashContext *base, + const uint8_t *message, + unsigned message_size) { + SHA256_HashContext *context = (SHA256_HashContext *)base; + SHA256_Update(&context->ctx, message, message_size); +} + +void finish_SHA256(uECC_HashContext *base, uint8_t *hash_result) { + SHA256_HashContext *context = (SHA256_HashContext *)base; + SHA256_Final(hash_result, &context->ctx); +} + +... when signing ... +{ + uint8_t tmp[32 + 32 + 64]; + SHA256_HashContext ctx = {{&init_SHA256, &update_SHA256, &finish_SHA256, 64, 32, tmp}}; + uECC_sign_deterministic(key, message_hash, &ctx.uECC, signature); +} +*/ +typedef struct uECC_HashContext { + void (*init_hash)(const struct uECC_HashContext *context); + void (*update_hash)(const struct uECC_HashContext *context, + const uint8_t *message, + unsigned message_size); + void (*finish_hash)(const struct uECC_HashContext *context, uint8_t *hash_result); + unsigned block_size; /* Hash function block size in bytes, eg 64 for SHA-256. */ + unsigned result_size; /* Hash function result size in bytes, eg 32 for SHA-256. */ + uint8_t *tmp; /* Must point to a buffer of at least (2 * result_size + block_size) bytes. */ +} uECC_HashContext; + +/* uECC_sign_deterministic() function. +Generate an ECDSA signature for a given hash value, using a deterministic algorithm +(see RFC 6979). You do not need to set the RNG using uECC_set_rng() before calling +this function; however, if the RNG is defined it will improve resistance to side-channel +attacks. + +Usage: Compute a hash of the data you wish to sign (SHA-2 is recommended) and pass it to +this function along with your private key and a hash context. Note that the message_hash +does not need to be computed with the same hash function used by hash_context. + +Inputs: + private_key - Your private key. + message_hash - The hash of the message to sign. + hash_size - The size of message_hash in bytes. + hash_context - A hash context to use. + +Outputs: + signature - Will be filled in with the signature value. + +Returns 1 if the signature generated successfully, 0 if an error occurred. +*/ +int uECC_sign_deterministic(const uint8_t *private_key, + const uint8_t *message_hash, + unsigned hash_size, + const uECC_HashContext *hash_context, + uint8_t *signature, + uECC_Curve curve); + +/* uECC_verify() function. +Verify an ECDSA signature. + +Usage: Compute the hash of the signed data using the same hash as the signer and +pass it to this function along with the signer's public key and the signature values (r and s). + +Inputs: + public_key - The signer's public key. + message_hash - The hash of the signed data. + hash_size - The size of message_hash in bytes. + signature - The signature value. + +Returns 1 if the signature is valid, 0 if it is invalid. +*/ +int uECC_verify(const uint8_t *public_key, + const uint8_t *message_hash, + unsigned hash_size, + const uint8_t *signature, + uECC_Curve curve); + +#ifdef __cplusplus +} /* end of extern "C" */ +#endif + +#endif /* _UECC_H_ */ diff --git a/deps/picoquic/picotls/deps/micro-ecc/uECC_vli.h b/deps/picoquic/picotls/deps/micro-ecc/uECC_vli.h new file mode 100644 index 0000000..864cc33 --- /dev/null +++ b/deps/picoquic/picotls/deps/micro-ecc/uECC_vli.h @@ -0,0 +1,172 @@ +/* Copyright 2015, Kenneth MacKay. Licensed under the BSD 2-clause license. */ + +#ifndef _UECC_VLI_H_ +#define _UECC_VLI_H_ + +#include "uECC.h" +#include "types.h" + +/* Functions for raw large-integer manipulation. These are only available + if uECC.c is compiled with uECC_ENABLE_VLI_API defined to 1. */ +#ifndef uECC_ENABLE_VLI_API + #define uECC_ENABLE_VLI_API 0 +#endif + +#ifdef __cplusplus +extern "C" +{ +#endif + +#if uECC_ENABLE_VLI_API + +void uECC_vli_clear(uECC_word_t *vli, wordcount_t num_words); + +/* Constant-time comparison to zero - secure way to compare long integers */ +/* Returns 1 if vli == 0, 0 otherwise. */ +uECC_word_t uECC_vli_isZero(const uECC_word_t *vli, wordcount_t num_words); + +/* Returns nonzero if bit 'bit' of vli is set. */ +uECC_word_t uECC_vli_testBit(const uECC_word_t *vli, bitcount_t bit); + +/* Counts the number of bits required to represent vli. */ +bitcount_t uECC_vli_numBits(const uECC_word_t *vli, const wordcount_t max_words); + +/* Sets dest = src. */ +void uECC_vli_set(uECC_word_t *dest, const uECC_word_t *src, wordcount_t num_words); + +/* Constant-time comparison function - secure way to compare long integers */ +/* Returns one if left == right, zero otherwise */ +uECC_word_t uECC_vli_equal(const uECC_word_t *left, + const uECC_word_t *right, + wordcount_t num_words); + +/* Constant-time comparison function - secure way to compare long integers */ +/* Returns sign of left - right, in constant time. */ +cmpresult_t uECC_vli_cmp(const uECC_word_t *left, const uECC_word_t *right, wordcount_t num_words); + +/* Computes vli = vli >> 1. */ +void uECC_vli_rshift1(uECC_word_t *vli, wordcount_t num_words); + +/* Computes result = left + right, returning carry. Can modify in place. */ +uECC_word_t uECC_vli_add(uECC_word_t *result, + const uECC_word_t *left, + const uECC_word_t *right, + wordcount_t num_words); + +/* Computes result = left - right, returning borrow. Can modify in place. */ +uECC_word_t uECC_vli_sub(uECC_word_t *result, + const uECC_word_t *left, + const uECC_word_t *right, + wordcount_t num_words); + +/* Computes result = left * right. Result must be 2 * num_words long. */ +void uECC_vli_mult(uECC_word_t *result, + const uECC_word_t *left, + const uECC_word_t *right, + wordcount_t num_words); + +/* Computes result = left^2. Result must be 2 * num_words long. */ +void uECC_vli_square(uECC_word_t *result, const uECC_word_t *left, wordcount_t num_words); + +/* Computes result = (left + right) % mod. + Assumes that left < mod and right < mod, and that result does not overlap mod. */ +void uECC_vli_modAdd(uECC_word_t *result, + const uECC_word_t *left, + const uECC_word_t *right, + const uECC_word_t *mod, + wordcount_t num_words); + +/* Computes result = (left - right) % mod. + Assumes that left < mod and right < mod, and that result does not overlap mod. */ +void uECC_vli_modSub(uECC_word_t *result, + const uECC_word_t *left, + const uECC_word_t *right, + const uECC_word_t *mod, + wordcount_t num_words); + +/* Computes result = product % mod, where product is 2N words long. + Currently only designed to work for mod == curve->p or curve_n. */ +void uECC_vli_mmod(uECC_word_t *result, + uECC_word_t *product, + const uECC_word_t *mod, + wordcount_t num_words); + +/* Calculates result = product (mod curve->p), where product is up to + 2 * curve->num_words long. */ +void uECC_vli_mmod_fast(uECC_word_t *result, uECC_word_t *product, uECC_Curve curve); + +/* Computes result = (left * right) % mod. + Currently only designed to work for mod == curve->p or curve_n. */ +void uECC_vli_modMult(uECC_word_t *result, + const uECC_word_t *left, + const uECC_word_t *right, + const uECC_word_t *mod, + wordcount_t num_words); + +/* Computes result = (left * right) % curve->p. */ +void uECC_vli_modMult_fast(uECC_word_t *result, + const uECC_word_t *left, + const uECC_word_t *right, + uECC_Curve curve); + +/* Computes result = left^2 % mod. + Currently only designed to work for mod == curve->p or curve_n. */ +void uECC_vli_modSquare(uECC_word_t *result, + const uECC_word_t *left, + const uECC_word_t *mod, + wordcount_t num_words); + +/* Computes result = left^2 % curve->p. */ +void uECC_vli_modSquare_fast(uECC_word_t *result, const uECC_word_t *left, uECC_Curve curve); + +/* Computes result = (1 / input) % mod.*/ +void uECC_vli_modInv(uECC_word_t *result, + const uECC_word_t *input, + const uECC_word_t *mod, + wordcount_t num_words); + +#if uECC_SUPPORT_COMPRESSED_POINT +/* Calculates a = sqrt(a) (mod curve->p) */ +void uECC_vli_mod_sqrt(uECC_word_t *a, uECC_Curve curve); +#endif + +/* Converts an integer in uECC native format to big-endian bytes. */ +void uECC_vli_nativeToBytes(uint8_t *bytes, int num_bytes, const uECC_word_t *native); +/* Converts big-endian bytes to an integer in uECC native format. */ +void uECC_vli_bytesToNative(uECC_word_t *native, const uint8_t *bytes, int num_bytes); + +unsigned uECC_curve_num_words(uECC_Curve curve); +unsigned uECC_curve_num_bytes(uECC_Curve curve); +unsigned uECC_curve_num_bits(uECC_Curve curve); +unsigned uECC_curve_num_n_words(uECC_Curve curve); +unsigned uECC_curve_num_n_bytes(uECC_Curve curve); +unsigned uECC_curve_num_n_bits(uECC_Curve curve); + +const uECC_word_t *uECC_curve_p(uECC_Curve curve); +const uECC_word_t *uECC_curve_n(uECC_Curve curve); +const uECC_word_t *uECC_curve_G(uECC_Curve curve); +const uECC_word_t *uECC_curve_b(uECC_Curve curve); + +int uECC_valid_point(const uECC_word_t *point, uECC_Curve curve); + +/* Multiplies a point by a scalar. Points are represented by the X coordinate followed by + the Y coordinate in the same array, both coordinates are curve->num_words long. Note + that scalar must be curve->num_n_words long (NOT curve->num_words). */ +void uECC_point_mult(uECC_word_t *result, + const uECC_word_t *point, + const uECC_word_t *scalar, + uECC_Curve curve); + +/* Generates a random integer in the range 0 < random < top. + Both random and top have num_words words. */ +int uECC_generate_random_int(uECC_word_t *random, + const uECC_word_t *top, + wordcount_t num_words); + +#endif /* uECC_ENABLE_VLI_API */ + +#ifdef __cplusplus +} /* end of extern "C" */ +#endif + +#endif /* _UECC_VLI_H_ */ diff --git a/deps/picoquic/picotls/include/picotls.h b/deps/picoquic/picotls/include/picotls.h new file mode 100644 index 0000000..0ffe7d7 --- /dev/null +++ b/deps/picoquic/picotls/include/picotls.h @@ -0,0 +1,1525 @@ +/* + * Copyright (c) 2016 DeNA Co., Ltd., Kazuho Oku + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +#ifndef picotls_h +#define picotls_h + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef _WINDOWS +#include "wincompat.h" +#endif + +#include +#include +#include +#include + +#if __GNUC__ >= 3 +#define PTLS_LIKELY(x) __builtin_expect(!!(x), 1) +#define PTLS_UNLIKELY(x) __builtin_expect(!!(x), 0) +#define PTLS_BUILD_ASSERT_EXPR(cond) (sizeof(char[2 * !!(!__builtin_constant_p(cond) || (cond)) - 1]) != 0) +#define PTLS_BUILD_ASSERT(cond) ((void)PTLS_BUILD_ASSERT_EXPR(cond)) +#else +#define PTLS_LIKELY(x) (x) +#define PTLS_UNLIKELY(x) (x) +#define PTLS_BUILD_ASSERT(cond) 1 +#endif + +/* __builtin_types_compatible_p yields incorrect results when older versions of GCC is used; see #303. + * Clang with Xcode 9.4 or prior is known to not work correctly when a pointer is const-qualified; see + * https://github.com/h2o/quicly/pull/306#issuecomment-626037269. Older versions of clang upstream works fine, but we do not need + * best coverage. This macro is for preventing misuse going into the master branch, having it work one of the compilers supported in + * our CI is enough. + */ +#if ((defined(__clang__) && __clang_major__ >= 10) || __GNUC__ >= 6) && !defined(__cplusplus) +#define PTLS_ASSERT_IS_ARRAY_EXPR(a) PTLS_BUILD_ASSERT_EXPR(__builtin_types_compatible_p(__typeof__(a[0])[], __typeof__(a))) +#else +#define PTLS_ASSERT_IS_ARRAY_EXPR(a) 1 +#endif + +#define PTLS_ELEMENTSOF(x) (PTLS_ASSERT_IS_ARRAY_EXPR(x) * sizeof(x) / sizeof((x)[0])) + +#ifdef _WINDOWS +#define PTLS_THREADLOCAL __declspec(thread) +#else +#define PTLS_THREADLOCAL __thread +#endif + +#ifndef PTLS_FUZZ_HANDSHAKE +#define PTLS_FUZZ_HANDSHAKE 0 +#endif + +#define PTLS_HELLO_RANDOM_SIZE 32 + +#define PTLS_AES128_KEY_SIZE 16 +#define PTLS_AES256_KEY_SIZE 32 +#define PTLS_AES_BLOCK_SIZE 16 +#define PTLS_AES_IV_SIZE 16 +#define PTLS_AESGCM_IV_SIZE 12 +#define PTLS_AESGCM_TAG_SIZE 16 +#define PTLS_AESGCM_CONFIDENTIALITY_LIMIT 0x2000000 /* 2^25 */ +#define PTLS_AESGCM_INTEGRITY_LIMIT UINT64_C(0x40000000000000) /* 2^54 */ +#define PTLS_AESCCM_CONFIDENTIALITY_LIMIT 0xB504F3 /* 2^23.5 */ +#define PTLS_AESCCM_INTEGRITY_LIMIT 0xB504F3 /* 2^23.5 */ + +#define PTLS_CHACHA20_KEY_SIZE 32 +#define PTLS_CHACHA20_IV_SIZE 16 +#define PTLS_CHACHA20POLY1305_IV_SIZE 12 +#define PTLS_CHACHA20POLY1305_TAG_SIZE 16 +#define PTLS_CHACHA20POLY1305_CONFIDENTIALITY_LIMIT UINT64_MAX /* at least 2^64 */ +#define PTLS_CHACHA20POLY1305_INTEGRITY_LIMIT UINT64_C(0x1000000000) /* 2^36 */ + +#define PTLS_BLOWFISH_KEY_SIZE 16 +#define PTLS_BLOWFISH_BLOCK_SIZE 8 + +#define PTLS_SHA256_BLOCK_SIZE 64 +#define PTLS_SHA256_DIGEST_SIZE 32 + +#define PTLS_SHA384_BLOCK_SIZE 128 +#define PTLS_SHA384_DIGEST_SIZE 48 + +#define PTLS_MAX_SECRET_SIZE 32 +#define PTLS_MAX_IV_SIZE 16 +#define PTLS_MAX_DIGEST_SIZE 64 + +/* cipher-suites */ +#define PTLS_CIPHER_SUITE_AES_128_GCM_SHA256 0x1301 +#define PTLS_CIPHER_SUITE_AES_256_GCM_SHA384 0x1302 +#define PTLS_CIPHER_SUITE_CHACHA20_POLY1305_SHA256 0x1303 + +/* negotiated_groups */ +#define PTLS_GROUP_SECP256R1 23 +#define PTLS_GROUP_SECP384R1 24 +#define PTLS_GROUP_SECP521R1 25 +#define PTLS_GROUP_X25519 29 +#define PTLS_GROUP_X448 30 + +/* signature algorithms */ +#define PTLS_SIGNATURE_RSA_PKCS1_SHA1 0x0201 +#define PTLS_SIGNATURE_RSA_PKCS1_SHA256 0x0401 +#define PTLS_SIGNATURE_ECDSA_SECP256R1_SHA256 0x0403 +#define PTLS_SIGNATURE_ECDSA_SECP384R1_SHA384 0x0503 +#define PTLS_SIGNATURE_ECDSA_SECP521R1_SHA512 0x0603 +#define PTLS_SIGNATURE_RSA_PSS_RSAE_SHA256 0x0804 +#define PTLS_SIGNATURE_RSA_PSS_RSAE_SHA384 0x0805 +#define PTLS_SIGNATURE_RSA_PSS_RSAE_SHA512 0x0806 + +/* ESNI */ +#define PTLS_ESNI_VERSION_DRAFT03 0xff02 + +#define PTLS_ESNI_RESPONSE_TYPE_ACCEPT 0 +#define PTLS_ESNI_RESPONSE_TYPE_RETRY_REQUEST 1 + +/* error classes and macros */ +#define PTLS_ERROR_CLASS_SELF_ALERT 0 +#define PTLS_ERROR_CLASS_PEER_ALERT 0x100 +#define PTLS_ERROR_CLASS_INTERNAL 0x200 + +#define PTLS_ERROR_GET_CLASS(e) ((e) & ~0xff) +#define PTLS_ALERT_TO_SELF_ERROR(e) ((e) + PTLS_ERROR_CLASS_SELF_ALERT) +#define PTLS_ALERT_TO_PEER_ERROR(e) ((e) + PTLS_ERROR_CLASS_PEER_ALERT) +#define PTLS_ERROR_TO_ALERT(e) ((e)&0xff) + +/* the HKDF prefix */ +#define PTLS_HKDF_EXPAND_LABEL_PREFIX "tls13 " + +/* alerts */ +#define PTLS_ALERT_LEVEL_WARNING 1 +#define PTLS_ALERT_LEVEL_FATAL 2 + +#define PTLS_ALERT_CLOSE_NOTIFY 0 +#define PTLS_ALERT_UNEXPECTED_MESSAGE 10 +#define PTLS_ALERT_BAD_RECORD_MAC 20 +#define PTLS_ALERT_HANDSHAKE_FAILURE 40 +#define PTLS_ALERT_BAD_CERTIFICATE 42 +#define PTLS_ALERT_CERTIFICATE_REVOKED 44 +#define PTLS_ALERT_CERTIFICATE_EXPIRED 45 +#define PTLS_ALERT_CERTIFICATE_UNKNOWN 46 +#define PTLS_ALERT_ILLEGAL_PARAMETER 47 +#define PTLS_ALERT_UNKNOWN_CA 48 +#define PTLS_ALERT_DECODE_ERROR 50 +#define PTLS_ALERT_DECRYPT_ERROR 51 +#define PTLS_ALERT_PROTOCOL_VERSION 70 +#define PTLS_ALERT_INTERNAL_ERROR 80 +#define PTLS_ALERT_USER_CANCELED 90 +#define PTLS_ALERT_MISSING_EXTENSION 109 +#define PTLS_ALERT_UNRECOGNIZED_NAME 112 +#define PTLS_ALERT_CERTIFICATE_REQUIRED 116 +#define PTLS_ALERT_NO_APPLICATION_PROTOCOL 120 + +/* internal errors */ +#define PTLS_ERROR_NO_MEMORY (PTLS_ERROR_CLASS_INTERNAL + 1) +#define PTLS_ERROR_IN_PROGRESS (PTLS_ERROR_CLASS_INTERNAL + 2) +#define PTLS_ERROR_LIBRARY (PTLS_ERROR_CLASS_INTERNAL + 3) +#define PTLS_ERROR_INCOMPATIBLE_KEY (PTLS_ERROR_CLASS_INTERNAL + 4) +#define PTLS_ERROR_SESSION_NOT_FOUND (PTLS_ERROR_CLASS_INTERNAL + 5) +#define PTLS_ERROR_STATELESS_RETRY (PTLS_ERROR_CLASS_INTERNAL + 6) +#define PTLS_ERROR_NOT_AVAILABLE (PTLS_ERROR_CLASS_INTERNAL + 7) +#define PTLS_ERROR_COMPRESSION_FAILURE (PTLS_ERROR_CLASS_INTERNAL + 8) +#define PTLS_ERROR_ESNI_RETRY (PTLS_ERROR_CLASS_INTERNAL + 8) +#define PTLS_ERROR_REJECT_EARLY_DATA (PTLS_ERROR_CLASS_INTERNAL + 9) +#define PTLS_ERROR_DELEGATE (PTLS_ERROR_CLASS_INTERNAL + 10) + +#define PTLS_ERROR_INCORRECT_BASE64 (PTLS_ERROR_CLASS_INTERNAL + 50) +#define PTLS_ERROR_PEM_LABEL_NOT_FOUND (PTLS_ERROR_CLASS_INTERNAL + 51) +#define PTLS_ERROR_BER_INCORRECT_ENCODING (PTLS_ERROR_CLASS_INTERNAL + 52) +#define PTLS_ERROR_BER_MALFORMED_TYPE (PTLS_ERROR_CLASS_INTERNAL + 53) +#define PTLS_ERROR_BER_MALFORMED_LENGTH (PTLS_ERROR_CLASS_INTERNAL + 54) +#define PTLS_ERROR_BER_EXCESSIVE_LENGTH (PTLS_ERROR_CLASS_INTERNAL + 55) +#define PTLS_ERROR_BER_ELEMENT_TOO_SHORT (PTLS_ERROR_CLASS_INTERNAL + 56) +#define PTLS_ERROR_BER_UNEXPECTED_EOC (PTLS_ERROR_CLASS_INTERNAL + 57) +#define PTLS_ERROR_DER_INDEFINITE_LENGTH (PTLS_ERROR_CLASS_INTERNAL + 58) +#define PTLS_ERROR_INCORRECT_ASN1_SYNTAX (PTLS_ERROR_CLASS_INTERNAL + 59) +#define PTLS_ERROR_INCORRECT_PEM_KEY_VERSION (PTLS_ERROR_CLASS_INTERNAL + 60) +#define PTLS_ERROR_INCORRECT_PEM_ECDSA_KEY_VERSION (PTLS_ERROR_CLASS_INTERNAL + 61) +#define PTLS_ERROR_INCORRECT_PEM_ECDSA_CURVE (PTLS_ERROR_CLASS_INTERNAL + 62) +#define PTLS_ERROR_INCORRECT_PEM_ECDSA_KEYSIZE (PTLS_ERROR_CLASS_INTERNAL + 63) +#define PTLS_ERROR_INCORRECT_ASN1_ECDSA_KEY_SYNTAX (PTLS_ERROR_CLASS_INTERNAL + 64) + +#define PTLS_HANDSHAKE_TYPE_CLIENT_HELLO 1 +#define PTLS_HANDSHAKE_TYPE_SERVER_HELLO 2 +#define PTLS_HANDSHAKE_TYPE_NEW_SESSION_TICKET 4 +#define PTLS_HANDSHAKE_TYPE_END_OF_EARLY_DATA 5 +#define PTLS_HANDSHAKE_TYPE_ENCRYPTED_EXTENSIONS 8 +#define PTLS_HANDSHAKE_TYPE_CERTIFICATE 11 +#define PTLS_HANDSHAKE_TYPE_CERTIFICATE_REQUEST 13 +#define PTLS_HANDSHAKE_TYPE_CERTIFICATE_VERIFY 15 +#define PTLS_HANDSHAKE_TYPE_FINISHED 20 +#define PTLS_HANDSHAKE_TYPE_KEY_UPDATE 24 +#define PTLS_HANDSHAKE_TYPE_COMPRESSED_CERTIFICATE 25 +#define PTLS_HANDSHAKE_TYPE_MESSAGE_HASH 254 + +#define PTLS_ZERO_DIGEST_SHA256 \ + { \ + 0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, 0x9a, 0xfb, 0xf4, 0xc8, 0x99, 0x6f, 0xb9, 0x24, 0x27, 0xae, 0x41, 0xe4, \ + 0x64, 0x9b, 0x93, 0x4c, 0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55 \ + } + +#define PTLS_ZERO_DIGEST_SHA384 \ + { \ + 0x38, 0xb0, 0x60, 0xa7, 0x51, 0xac, 0x96, 0x38, 0x4c, 0xd9, 0x32, 0x7e, 0xb1, 0xb1, 0xe3, 0x6a, 0x21, 0xfd, 0xb7, 0x11, \ + 0x14, 0xbe, 0x07, 0x43, 0x4c, 0x0c, 0xc7, 0xbf, 0x63, 0xf6, 0xe1, 0xda, 0x27, 0x4e, 0xde, 0xbf, 0xe7, 0x6f, 0x65, \ + 0xfb, 0xd5, 0x1a, 0xd2, 0xf1, 0x48, 0x98, 0xb9, 0x5b \ + } + +typedef struct st_ptls_t ptls_t; +typedef struct st_ptls_context_t ptls_context_t; +typedef struct st_ptls_key_schedule_t ptls_key_schedule_t; + +/** + * represents a sequence of octets + */ +typedef struct st_ptls_iovec_t { + uint8_t *base; + size_t len; +} ptls_iovec_t; + +/** + * used for storing output + */ +typedef struct st_ptls_buffer_t { + uint8_t *base; + size_t capacity; + size_t off; + int is_allocated; +} ptls_buffer_t; + +/** + * key exchange context built by ptls_key_exchange_algorithm::create. + */ +typedef struct st_ptls_key_exchange_context_t { + /** + * the underlying algorithm + */ + const struct st_ptls_key_exchange_algorithm_t *algo; + /** + * the public key + */ + ptls_iovec_t pubkey; + /** + * If `release` is set, the callee frees resources allocated to the context and set *keyex to NULL + */ + int (*on_exchange)(struct st_ptls_key_exchange_context_t **keyex, int release, ptls_iovec_t *secret, ptls_iovec_t peerkey); +} ptls_key_exchange_context_t; + +/** + * A key exchange algorithm. + */ +typedef const struct st_ptls_key_exchange_algorithm_t { + /** + * ID defined by the TLS specification + */ + uint16_t id; + /** + * creates a context for asynchronous key exchange. The function is called when ClientHello is generated. The on_exchange + * callback of the created context is called when the client receives ServerHello. + */ + int (*create)(const struct st_ptls_key_exchange_algorithm_t *algo, ptls_key_exchange_context_t **ctx); + /** + * implements synchronous key exchange. Called when receiving a ServerHello. + */ + int (*exchange)(const struct st_ptls_key_exchange_algorithm_t *algo, ptls_iovec_t *pubkey, ptls_iovec_t *secret, + ptls_iovec_t peerkey); + /** + * crypto-specific data + */ + intptr_t data; +} ptls_key_exchange_algorithm_t; + +/** + * context of a symmetric cipher + */ +typedef struct st_ptls_cipher_context_t { + const struct st_ptls_cipher_algorithm_t *algo; + /* field above this line must not be altered by the crypto binding */ + void (*do_dispose)(struct st_ptls_cipher_context_t *ctx); + void (*do_init)(struct st_ptls_cipher_context_t *ctx, const void *iv); + void (*do_transform)(struct st_ptls_cipher_context_t *ctx, void *output, const void *input, size_t len); +} ptls_cipher_context_t; + +/** + * a symmetric cipher + */ +typedef const struct st_ptls_cipher_algorithm_t { + const char *name; + size_t key_size; + size_t block_size; + size_t iv_size; + size_t context_size; + int (*setup_crypto)(ptls_cipher_context_t *ctx, int is_enc, const void *key); +} ptls_cipher_algorithm_t; + +typedef struct st_ptls_aead_supplementary_encryption_t { + ptls_cipher_context_t *ctx; + const void *input; + uint8_t output[16]; +} ptls_aead_supplementary_encryption_t; + +/** + * AEAD context. AEAD implementations are allowed to stuff data at the end of the struct. The size of the memory allocated for the + * struct is governed by ptls_aead_algorithm_t::context_size. + */ +typedef struct st_ptls_aead_context_t { + const struct st_ptls_aead_algorithm_t *algo; + /* field above this line must not be altered by the crypto binding */ + void (*dispose_crypto)(struct st_ptls_aead_context_t *ctx); + void (*do_encrypt_init)(struct st_ptls_aead_context_t *ctx, uint64_t seq, const void *aad, size_t aadlen); + size_t (*do_encrypt_update)(struct st_ptls_aead_context_t *ctx, void *output, const void *input, size_t inlen); + size_t (*do_encrypt_final)(struct st_ptls_aead_context_t *ctx, void *output); + void (*do_encrypt)(struct st_ptls_aead_context_t *ctx, void *output, const void *input, size_t inlen, uint64_t seq, + const void *aad, size_t aadlen, ptls_aead_supplementary_encryption_t *supp); + size_t (*do_decrypt)(struct st_ptls_aead_context_t *ctx, void *output, const void *input, size_t inlen, uint64_t seq, + const void *aad, size_t aadlen); +} ptls_aead_context_t; + +/** + * An AEAD cipher. + */ +typedef const struct st_ptls_aead_algorithm_t { + /** + * name (following the convention of `openssl ciphers -v ALL`) + */ + const char *name; + /** + * confidentiality_limit (max records / packets sent before re-key) + */ + const uint64_t confidentiality_limit; + /** + * integrity_limit (max decryption failure records / packets before re-key) + */ + const uint64_t integrity_limit; + /** + * the underlying key stream + */ + ptls_cipher_algorithm_t *ctr_cipher; + /** + * the underlying ecb cipher (might not be available) + */ + ptls_cipher_algorithm_t *ecb_cipher; + /** + * key size + */ + size_t key_size; + /** + * size of the IV + */ + size_t iv_size; + /** + * size of the tag + */ + size_t tag_size; + /** + * size of memory allocated for ptls_aead_context_t. AEAD implementations can set this value to something greater than + * sizeof(ptls_aead_context_t) and stuff additional data at the bottom of the struct. + */ + size_t context_size; + /** + * callback that sets up the crypto + */ + int (*setup_crypto)(ptls_aead_context_t *ctx, int is_enc, const void *key, const void *iv); +} ptls_aead_algorithm_t; + +/** + * + */ +typedef enum en_ptls_hash_final_mode_t { + /** + * obtains the digest and frees the context + */ + PTLS_HASH_FINAL_MODE_FREE = 0, + /** + * obtains the digest and reset the context to initial state + */ + PTLS_HASH_FINAL_MODE_RESET = 1, + /** + * obtains the digest while leaving the context as-is + */ + PTLS_HASH_FINAL_MODE_SNAPSHOT = 2 +} ptls_hash_final_mode_t; + +/** + * A hash context. + */ +typedef struct st_ptls_hash_context_t { + /** + * feeds additional data into the hash context + */ + void (*update)(struct st_ptls_hash_context_t *ctx, const void *src, size_t len); + /** + * returns the digest and performs necessary operation specified by mode + */ + void (*final)(struct st_ptls_hash_context_t *ctx, void *md, ptls_hash_final_mode_t mode); + /** + * creates a copy of the hash context + */ + struct st_ptls_hash_context_t *(*clone_)(struct st_ptls_hash_context_t *src); +} ptls_hash_context_t; + +/** + * A hash algorithm and its properties. + */ +typedef const struct st_ptls_hash_algorithm_t { + /** + * block size + */ + size_t block_size; + /** + * digest size + */ + size_t digest_size; + /** + * constructor that creates the hash context + */ + ptls_hash_context_t *(*create)(void); + /** + * digest of zero-length octets + */ + uint8_t empty_digest[PTLS_MAX_DIGEST_SIZE]; +} ptls_hash_algorithm_t; + +typedef const struct st_ptls_cipher_suite_t { + uint16_t id; + ptls_aead_algorithm_t *aead; + ptls_hash_algorithm_t *hash; +} ptls_cipher_suite_t; + +struct st_ptls_traffic_protection_t; + +typedef struct st_ptls_message_emitter_t { + ptls_buffer_t *buf; + struct st_ptls_traffic_protection_t *enc; + size_t record_header_length; + int (*begin_message)(struct st_ptls_message_emitter_t *self); + int (*commit_message)(struct st_ptls_message_emitter_t *self); +} ptls_message_emitter_t; + +/** + * holds ESNIKeys and the private key (instantiated by ptls_esni_parse, freed using ptls_esni_dispose) + */ +typedef struct st_ptls_esni_context_t { + ptls_key_exchange_context_t **key_exchanges; + struct { + ptls_cipher_suite_t *cipher_suite; + uint8_t record_digest[PTLS_MAX_DIGEST_SIZE]; + } * cipher_suites; + uint16_t padded_length; + uint64_t not_before; + uint64_t not_after; + uint16_t version; +} ptls_esni_context_t; + +/** + * holds the ESNI secret, as exchanged during the handshake + */ + +#define PTLS_ESNI_NONCE_SIZE 16 + +typedef struct st_ptls_esni_secret_t { + ptls_iovec_t secret; + uint8_t nonce[PTLS_ESNI_NONCE_SIZE]; + uint8_t esni_contents_hash[PTLS_MAX_DIGEST_SIZE]; + struct { + ptls_key_exchange_algorithm_t *key_share; + ptls_cipher_suite_t *cipher; + ptls_iovec_t pubkey; + uint8_t record_digest[PTLS_MAX_DIGEST_SIZE]; + uint16_t padded_length; + } client; + uint16_t version; +} ptls_esni_secret_t; + +#define PTLS_CALLBACK_TYPE0(ret, name) \ + typedef struct st_ptls_##name##_t { \ + ret (*cb)(struct st_ptls_##name##_t * self); \ + } ptls_##name##_t + +#define PTLS_CALLBACK_TYPE(ret, name, ...) \ + typedef struct st_ptls_##name##_t { \ + ret (*cb)(struct st_ptls_##name##_t * self, __VA_ARGS__); \ + } ptls_##name##_t + +/** + * arguments passsed to the on_client_hello callback + */ +typedef struct st_ptls_on_client_hello_parameters_t { + /** + * SNI value received from the client. The value is {NULL, 0} if the extension was absent. + */ + ptls_iovec_t server_name; + /** + * Raw value of the client_hello message. + */ + ptls_iovec_t raw_message; + /** + * + */ + struct { + ptls_iovec_t *list; + size_t count; + } negotiated_protocols; + struct { + const uint16_t *list; + size_t count; + } signature_algorithms; + struct { + const uint16_t *list; + size_t count; + } certificate_compression_algorithms; + struct { + const uint16_t *list; + size_t count; + } cipher_suites; + /** + * if ESNI was used + */ + unsigned esni : 1; + /** + * set to 1 if ClientHello is too old (or too new) to be handled by picotls + */ + unsigned incompatible_version : 1; +} ptls_on_client_hello_parameters_t; + +/** + * returns current time in milliseconds (ptls_get_time can be used to return the physical time) + */ +PTLS_CALLBACK_TYPE0(uint64_t, get_time); +/** + * after receiving ClientHello, the core calls the optional callback to give a chance to the swap the context depending on the input + * values. The callback is required to call `ptls_set_server_name` if an SNI extension needs to be sent to the client. + */ +PTLS_CALLBACK_TYPE(int, on_client_hello, ptls_t *tls, ptls_on_client_hello_parameters_t *params); +/** + * callback to generate the certificate message. `ptls_context::certificates` are set when the callback is set to NULL. + */ +PTLS_CALLBACK_TYPE(int, emit_certificate, ptls_t *tls, ptls_message_emitter_t *emitter, ptls_key_schedule_t *key_sched, + ptls_iovec_t context, int push_status_request, const uint16_t *compress_algos, size_t num_compress_algos); +/** + * when gerenating CertificateVerify, the core calls the callback to sign the handshake context using the certificate. + */ +PTLS_CALLBACK_TYPE(int, sign_certificate, ptls_t *tls, uint16_t *selected_algorithm, ptls_buffer_t *output, ptls_iovec_t input, + const uint16_t *algorithms, size_t num_algorithms); +/** + * after receiving Certificate, the core calls the callback to verify the certificate chain and to obtain a pointer to a + * callback that should be used for verifying CertificateVerify. If an error occurs between a successful return from this + * callback to the invocation of the verify_sign callback, verify_sign is called with both data and sign set to an empty buffer. + * The implementor of the callback should use that as the opportunity to free any temporary data allocated for the verify_sign + * callback. + */ +PTLS_CALLBACK_TYPE(int, verify_certificate, ptls_t *tls, + int (**verify_sign)(void *verify_ctx, ptls_iovec_t data, ptls_iovec_t sign), void **verify_data, + ptls_iovec_t *certs, size_t num_certs); +/** + * Encrypt-and-signs (or verify-and-decrypts) a ticket (server-only). + * When used for encryption (i.e., is_encrypt being set), the function should return 0 if successful, or else a non-zero value. + * When used for decryption, the function should return 0 (successful), PTLS_ERROR_REJECT_EARLY_DATA (successful, but 0-RTT is + * forbidden), or any other value to indicate failure. + */ +PTLS_CALLBACK_TYPE(int, encrypt_ticket, ptls_t *tls, int is_encrypt, ptls_buffer_t *dst, ptls_iovec_t src); +/** + * saves a ticket (client-only) + */ +PTLS_CALLBACK_TYPE(int, save_ticket, ptls_t *tls, ptls_iovec_t input); +/** + * event logging (incl. secret logging) + */ +typedef struct st_ptls_log_event_t { + void (*cb)(struct st_ptls_log_event_t *self, ptls_t *tls, const char *type, const char *fmt, ...) + __attribute__((format(printf, 4, 5))); +} ptls_log_event_t; +/** + * reference counting + */ +PTLS_CALLBACK_TYPE(void, update_open_count, ssize_t delta); +/** + * applications that have their own record layer can set this function to derive their own traffic keys from the traffic secret. + * The cipher-suite that is being associated to the connection can be obtained by calling the ptls_get_cipher function. + */ +PTLS_CALLBACK_TYPE(int, update_traffic_key, ptls_t *tls, int is_enc, size_t epoch, const void *secret); +/** + * callback for every extension detected during decoding + */ +PTLS_CALLBACK_TYPE(int, on_extension, ptls_t *tls, uint8_t hstype, uint16_t exttype, ptls_iovec_t extdata); +/** + * + */ +typedef struct st_ptls_decompress_certificate_t { + /** + * list of supported algorithms terminated by UINT16_MAX + */ + const uint16_t *supported_algorithms; + /** + * callback that decompresses the message + */ + int (*cb)(struct st_ptls_decompress_certificate_t *self, ptls_t *tls, uint16_t algorithm, ptls_iovec_t output, + ptls_iovec_t input); +} ptls_decompress_certificate_t; +/** + * provides access to the ESNI shared secret (Zx). API is subject to change. + */ +PTLS_CALLBACK_TYPE(int, update_esni_key, ptls_t *tls, ptls_iovec_t secret, ptls_hash_algorithm_t *hash, + const void *hashed_esni_contents); + +/** + * the configuration + */ +struct st_ptls_context_t { + /** + * PRNG to be used + */ + void (*random_bytes)(void *buf, size_t len); + /** + * + */ + ptls_get_time_t *get_time; + /** + * list of supported key-exchange algorithms terminated by NULL + */ + ptls_key_exchange_algorithm_t **key_exchanges; + /** + * list of supported cipher-suites terminated by NULL + */ + ptls_cipher_suite_t **cipher_suites; + /** + * list of certificates + */ + struct { + ptls_iovec_t *list; + size_t count; + } certificates; + /** + * list of ESNI data terminated by NULL + */ + ptls_esni_context_t **esni; + /** + * + */ + ptls_on_client_hello_t *on_client_hello; + /** + * + */ + ptls_emit_certificate_t *emit_certificate; + /** + * + */ + ptls_sign_certificate_t *sign_certificate; + /** + * + */ + ptls_verify_certificate_t *verify_certificate; + /** + * lifetime of a session ticket (server-only) + */ + uint32_t ticket_lifetime; + /** + * maximum permitted size of early data (server-only) + */ + uint32_t max_early_data_size; + /** + * maximum size of the message buffer (default: 0 = unlimited = 3 + 2^24 bytes) + */ + size_t max_buffer_size; + /** + * the field is obsolete; should be set to NULL for QUIC draft-17. Note also that even though everybody did, it was incorrect + * to set the value to "quic " in the earlier versions of the draft. + */ + const char *hkdf_label_prefix__obsolete; + /** + * if set, psk handshakes use (ec)dhe + */ + unsigned require_dhe_on_psk : 1; + /** + * if exporter master secrets should be recorded + */ + unsigned use_exporter : 1; + /** + * if ChangeCipherSpec record should be sent during handshake. If the client sends CCS, the server sends one in response + * regardless of the value of this flag. See RFC 8446 Appendix D.3. + */ + unsigned send_change_cipher_spec : 1; + /** + * if set, the server requests client certificates + * to authenticate the client. + */ + unsigned require_client_authentication : 1; + /** + * if set, EOED will not be emitted or accepted + */ + unsigned omit_end_of_early_data : 1; + /** + * + */ + ptls_encrypt_ticket_t *encrypt_ticket; + /** + * + */ + ptls_save_ticket_t *save_ticket; + /** + * + */ + ptls_log_event_t *log_event; + /** + * + */ + ptls_update_open_count_t *update_open_count; + /** + * + */ + ptls_update_traffic_key_t *update_traffic_key; + /** + * + */ + ptls_decompress_certificate_t *decompress_certificate; + /** + * + */ + ptls_update_esni_key_t *update_esni_key; + /** + * + */ + ptls_on_extension_t *on_extension; +}; + +typedef struct st_ptls_raw_extension_t { + uint16_t type; + ptls_iovec_t data; +} ptls_raw_extension_t; + +typedef enum en_ptls_early_data_acceptance_t { + PTLS_EARLY_DATA_ACCEPTANCE_UNKNOWN = 0, + PTLS_EARLY_DATA_REJECTED, + PTLS_EARLY_DATA_ACCEPTED +} ptls_early_data_acceptance_t; + +/** + * optional arguments to client-driven handshake + */ +#ifdef _WINDOWS +/* suppress warning C4201: nonstandard extension used: nameless struct/union */ +#pragma warning(push) +#pragma warning(disable : 4201) +#endif +typedef struct st_ptls_handshake_properties_t { + union { + struct { + /** + * list of protocols offered through ALPN + */ + struct { + const ptls_iovec_t *list; + size_t count; + } negotiated_protocols; + /** + * session ticket sent to the application via save_ticket callback + */ + ptls_iovec_t session_ticket; + /** + * pointer to store the maximum size of early-data that can be sent immediately. If set to non-NULL, the first call to + * ptls_handshake (or ptls_handle_message) will set `*max_early_data` to the value obtained from the session ticket, or + * to zero if early-data cannot be sent. If NULL, early data will not be used. + */ + size_t *max_early_data_size; + /** + * If early-data has been accepted by peer, or if the state is still unknown. The state changes anytime after handshake + * keys become available. Applications can peek the tri-state variable every time it calls `ptls_hanshake` or + * `ptls_handle_message` to determine the result at the earliest moment. This is an output parameter. + */ + ptls_early_data_acceptance_t early_data_acceptance; + /** + * negotiate the key exchange method before sending key_share + */ + unsigned negotiate_before_key_exchange : 1; + /** + * ESNIKeys (the value of the TXT record, after being base64-"decoded") + */ + ptls_iovec_t esni_keys; + } client; + struct { + /** + * psk binder being selected (len is set to zero if none) + */ + struct { + uint8_t base[PTLS_MAX_DIGEST_SIZE]; + size_t len; + } selected_psk_binder; + /** + * parameters related to use of the Cookie extension + */ + struct { + /** + * HMAC key to protect the integrity of the cookie. The key should be as long as the digest size of the first + * ciphersuite specified in ptls_context_t (i.e. the hash algorithm of the best ciphersuite that can be chosen). + */ + const void *key; + /** + * additional data to be used for verifying the cookie + */ + ptls_iovec_t additional_data; + } cookie; + /** + * if HRR should always be sent + */ + unsigned enforce_retry : 1; + /** + * if retry should be stateless (cookie.key MUST be set when this option is used) + */ + unsigned retry_uses_cookie : 1; + } server; + }; + /** + * an optional list of additional extensions to send either in CH or EE, terminated by type == UINT16_MAX + */ + ptls_raw_extension_t *additional_extensions; + /** + * an optional callback that returns a boolean value indicating if a particular extension should be collected + */ + int (*collect_extension)(ptls_t *tls, struct st_ptls_handshake_properties_t *properties, uint16_t type); + /** + * an optional callback that reports the extensions being collected + */ + int (*collected_extensions)(ptls_t *tls, struct st_ptls_handshake_properties_t *properties, ptls_raw_extension_t *extensions); +} ptls_handshake_properties_t; +#ifdef _WINDOWS +#pragma warning(pop) +#endif +#ifdef _WINDOWS +/* suppress warning C4293: >> shift count negative or too big */ +#pragma warning(disable : 4293) +#endif +/** + * builds a new ptls_iovec_t instance using the supplied parameters + */ +static ptls_iovec_t ptls_iovec_init(const void *p, size_t len); +/** + * initializes a buffer, setting the default destination to the small buffer provided as the argument. + */ +static void ptls_buffer_init(ptls_buffer_t *buf, void *smallbuf, size_t smallbuf_size); +/** + * disposes a buffer, freeing resources allocated by the buffer itself (if any) + */ +static void ptls_buffer_dispose(ptls_buffer_t *buf); +/** + * internal + */ +void ptls_buffer__release_memory(ptls_buffer_t *buf); +/** + * reserves space for additional amount of memory + */ +int ptls_buffer_reserve(ptls_buffer_t *buf, size_t delta); +/** + * internal + */ +int ptls_buffer__do_pushv(ptls_buffer_t *buf, const void *src, size_t len); +/** + * internal + */ +int ptls_buffer__adjust_quic_blocksize(ptls_buffer_t *buf, size_t body_size); +/** + * internal + */ +int ptls_buffer__adjust_asn1_blocksize(ptls_buffer_t *buf, size_t body_size); +/** + * pushes an unsigned bigint + */ +int ptls_buffer_push_asn1_ubigint(ptls_buffer_t *buf, const void *bignum, size_t size); +/** + * encodes a quic varint (maximum length is PTLS_ENCODE_QUICINT_CAPACITY) + */ +static uint8_t *ptls_encode_quicint(uint8_t *p, uint64_t v); +#define PTLS_ENCODE_QUICINT_CAPACITY 8 + +#define ptls_buffer_pushv(buf, src, len) \ + do { \ + if ((ret = ptls_buffer__do_pushv((buf), (src), (len))) != 0) \ + goto Exit; \ + } while (0) + +#define ptls_buffer_push(buf, ...) \ + do { \ + if ((ret = ptls_buffer__do_pushv((buf), (uint8_t[]){__VA_ARGS__}, sizeof((uint8_t[]){__VA_ARGS__}))) != 0) \ + goto Exit; \ + } while (0) + +#define ptls_buffer_push16(buf, v) \ + do { \ + uint16_t _v = (v); \ + ptls_buffer_push(buf, (uint8_t)(_v >> 8), (uint8_t)_v); \ + } while (0) + +#define ptls_buffer_push24(buf, v) \ + do { \ + uint32_t _v = (v); \ + ptls_buffer_push(buf, (uint8_t)(_v >> 16), (uint8_t)(_v >> 8), (uint8_t)_v); \ + } while (0) + +#define ptls_buffer_push32(buf, v) \ + do { \ + uint32_t _v = (v); \ + ptls_buffer_push(buf, (uint8_t)(_v >> 24), (uint8_t)(_v >> 16), (uint8_t)(_v >> 8), (uint8_t)_v); \ + } while (0) + +#define ptls_buffer_push64(buf, v) \ + do { \ + uint64_t _v = (v); \ + ptls_buffer_push(buf, (uint8_t)(_v >> 56), (uint8_t)(_v >> 48), (uint8_t)(_v >> 40), (uint8_t)(_v >> 32), \ + (uint8_t)(_v >> 24), (uint8_t)(_v >> 16), (uint8_t)(_v >> 8), (uint8_t)_v); \ + } while (0) + +#define ptls_buffer_push_quicint(buf, v) \ + do { \ + if ((ret = ptls_buffer_reserve((buf), PTLS_ENCODE_QUICINT_CAPACITY)) != 0) \ + goto Exit; \ + uint8_t *d = ptls_encode_quicint((buf)->base + (buf)->off, (v)); \ + (buf)->off = d - (buf)->base; \ + } while (0) + +#define ptls_buffer_push_block(buf, _capacity, block) \ + do { \ + size_t capacity = (_capacity); \ + ptls_buffer_pushv((buf), (uint8_t *)"\0\0\0\0\0\0\0", capacity != -1 ? capacity : 1); \ + size_t body_start = (buf)->off; \ + do { \ + block \ + } while (0); \ + size_t body_size = (buf)->off - body_start; \ + if (capacity != -1) { \ + for (; capacity != 0; --capacity) \ + (buf)->base[body_start - capacity] = (uint8_t)(body_size >> (8 * (capacity - 1))); \ + } else { \ + if ((ret = ptls_buffer__adjust_quic_blocksize((buf), body_size)) != 0) \ + goto Exit; \ + } \ + } while (0) + +#define ptls_buffer_push_asn1_block(buf, block) \ + do { \ + ptls_buffer_push((buf), 0xff); /* dummy */ \ + size_t body_start = (buf)->off; \ + do { \ + block \ + } while (0); \ + size_t body_size = (buf)->off - body_start; \ + if (body_size < 128) { \ + (buf)->base[body_start - 1] = (uint8_t)body_size; \ + } else { \ + if ((ret = ptls_buffer__adjust_asn1_blocksize((buf), body_size)) != 0) \ + goto Exit; \ + } \ + } while (0) + +#define ptls_buffer_push_asn1_sequence(buf, block) \ + do { \ + ptls_buffer_push((buf), 0x30); \ + ptls_buffer_push_asn1_block((buf), block); \ + } while (0) + +#define ptls_buffer_push_message_body(buf, key_sched, type, block) \ + do { \ + ptls_buffer_t *_buf = (buf); \ + ptls_key_schedule_t *_key_sched = (key_sched); \ + size_t mess_start = _buf->off; \ + ptls_buffer_push(_buf, (type)); \ + ptls_buffer_push_block(_buf, 3, block); \ + if (_key_sched != NULL) \ + ptls__key_schedule_update_hash(_key_sched, _buf->base + mess_start, _buf->off - mess_start); \ + } while (0) + +#define ptls_push_message(emitter, key_sched, type, block) \ + do { \ + ptls_message_emitter_t *_emitter = (emitter); \ + if ((ret = _emitter->begin_message(_emitter)) != 0) \ + goto Exit; \ + ptls_buffer_push_message_body(_emitter->buf, (key_sched), (type), block); \ + if ((ret = _emitter->commit_message(_emitter)) != 0) \ + goto Exit; \ + } while (0) + +int ptls_decode16(uint16_t *value, const uint8_t **src, const uint8_t *end); +int ptls_decode24(uint32_t *value, const uint8_t **src, const uint8_t *end); +int ptls_decode32(uint32_t *value, const uint8_t **src, const uint8_t *end); +int ptls_decode64(uint64_t *value, const uint8_t **src, const uint8_t *end); +uint64_t ptls_decode_quicint(const uint8_t **src, const uint8_t *end); + +#define ptls_decode_open_block(src, end, capacity, block) \ + do { \ + size_t _capacity = (capacity); \ + size_t _block_size; \ + if (_capacity == -1) { \ + uint64_t _block_size64; \ + const uint8_t *_src = (src); \ + if ((_block_size64 = ptls_decode_quicint(&_src, end)) == UINT64_MAX || \ + (sizeof(size_t) < 8 && (_block_size64 >> (8 * sizeof(size_t))) != 0)) { \ + ret = PTLS_ALERT_DECODE_ERROR; \ + goto Exit; \ + } \ + (src) = _src; \ + _block_size = (size_t)_block_size64; \ + } else { \ + if (_capacity > (size_t)(end - (src))) { \ + ret = PTLS_ALERT_DECODE_ERROR; \ + goto Exit; \ + } \ + _block_size = 0; \ + do { \ + _block_size = _block_size << 8 | *(src)++; \ + } while (--_capacity != 0); \ + } \ + if (_block_size > (size_t)(end - (src))) { \ + ret = PTLS_ALERT_DECODE_ERROR; \ + goto Exit; \ + } \ + do { \ + const uint8_t *const end = (src) + _block_size; \ + do { \ + block \ + } while (0); \ + if ((src) != end) { \ + ret = PTLS_ALERT_DECODE_ERROR; \ + goto Exit; \ + } \ + } while (0); \ + } while (0) + +#define ptls_decode_assert_block_close(src, end) \ + do { \ + if ((src) != end) { \ + ret = PTLS_ALERT_DECODE_ERROR; \ + goto Exit; \ + } \ + } while (0); + +#define ptls_decode_block(src, end, capacity, block) \ + do { \ + ptls_decode_open_block((src), end, capacity, block); \ + ptls_decode_assert_block_close((src), end); \ + } while (0) + +/** + * create a client object to handle new TLS connection + */ +ptls_t *ptls_client_new(ptls_context_t *ctx); +/** + * create a server object to handle new TLS connection + */ +ptls_t *ptls_server_new(ptls_context_t *ctx); +/** + * creates a object handle new TLS connection + */ +static ptls_t *ptls_new(ptls_context_t *ctx, int is_server); +/** + * releases all resources associated to the object + */ +void ptls_free(ptls_t *tls); +/** + * returns address of the crypto callbacks that the connection is using + */ +ptls_context_t *ptls_get_context(ptls_t *tls); +/** + * updates the context of a connection. Can be called from `on_client_hello` callback. + */ +void ptls_set_context(ptls_t *tls, ptls_context_t *ctx); +/** + * returns the client-random + */ +ptls_iovec_t ptls_get_client_random(ptls_t *tls); +/** + * returns the cipher-suite being used + */ +ptls_cipher_suite_t *ptls_get_cipher(ptls_t *tls); +/** + * returns the server-name (NULL if SNI is not used or failed to negotiate) + */ +const char *ptls_get_server_name(ptls_t *tls); +/** + * sets the server-name associated to the TLS connection. If server_name_len is zero, then strlen(server_name) is called to + * determine the length of the name. + * On the client-side, the value is used for certificate validation. The value will be also sent as an SNI extension, if it looks + * like a DNS name. + * On the server-side, it can be called from on_client_hello to indicate the acceptance of the SNI extension to the client. + */ +int ptls_set_server_name(ptls_t *tls, const char *server_name, size_t server_name_len); +/** + * returns the negotiated protocol (or NULL) + */ +const char *ptls_get_negotiated_protocol(ptls_t *tls); +/** + * sets the negotiated protocol. If protocol_len is zero, strlen(protocol) is called to determine the length of the protocol name. + */ +int ptls_set_negotiated_protocol(ptls_t *tls, const char *protocol, size_t protocol_len); +/** + * returns if the handshake has been completed + */ +int ptls_handshake_is_complete(ptls_t *tls); +/** + * returns if a PSK (or PSK-DHE) handshake was performed + */ +int ptls_is_psk_handshake(ptls_t *tls); +/** + * returns a pointer to user data pointer (client is reponsible for freeing the associated data prior to calling ptls_free) + */ +void **ptls_get_data_ptr(ptls_t *tls); +/** + * + */ +int ptls_skip_tracing(ptls_t *tls); +/** + * + */ +void ptls_set_skip_tracing(ptls_t *tls, int skip_tracing); +/** + * proceeds with the handshake, optionally taking some input from peer. The function returns zero in case the handshake completed + * successfully. PTLS_ERROR_IN_PROGRESS is returned in case the handshake is incomplete. Otherwise, an error value is returned. The + * contents of sendbuf should be sent to the client, regardless of whether if an error is returned. inlen is an argument used for + * both input and output. As an input, the arguments takes the size of the data available as input. Upon return the value is updated + * to the number of bytes consumed by the handshake. In case the returned value is PTLS_ERROR_IN_PROGRESS there is a guarantee that + * all the input are consumed (i.e. the value of inlen does not change). + */ +int ptls_handshake(ptls_t *tls, ptls_buffer_t *sendbuf, const void *input, size_t *inlen, ptls_handshake_properties_t *args); +/** + * decrypts the first record within given buffer + */ +int ptls_receive(ptls_t *tls, ptls_buffer_t *plaintextbuf, const void *input, size_t *len); +/** + * encrypts given buffer into multiple TLS records + */ +int ptls_send(ptls_t *tls, ptls_buffer_t *sendbuf, const void *input, size_t inlen); +/** + * updates the send traffic key (as well as asks the peer to update) + */ +int ptls_update_key(ptls_t *tls, int request_update); +/** + * Returns if the context is a server context. + */ +int ptls_is_server(ptls_t *tls); +/** + * returns per-record overhead + */ +size_t ptls_get_record_overhead(ptls_t *tls); +/** + * sends an alert + */ +int ptls_send_alert(ptls_t *tls, ptls_buffer_t *sendbuf, uint8_t level, uint8_t description); +/** + * + */ +int ptls_export_secret(ptls_t *tls, void *output, size_t outlen, const char *label, ptls_iovec_t context_value, int is_early); +/** + * build the body of a Certificate message. Can be called with tls set to NULL in order to create a precompressed message. + */ +int ptls_build_certificate_message(ptls_buffer_t *buf, ptls_iovec_t request_context, ptls_iovec_t *certificates, + size_t num_certificates, ptls_iovec_t ocsp_status); +/** + * + */ +int ptls_calc_hash(ptls_hash_algorithm_t *algo, void *output, const void *src, size_t len); +/** + * + */ +ptls_hash_context_t *ptls_hmac_create(ptls_hash_algorithm_t *algo, const void *key, size_t key_size); +/** + * + */ +int ptls_hkdf_extract(ptls_hash_algorithm_t *hash, void *output, ptls_iovec_t salt, ptls_iovec_t ikm); +/** + * + */ +int ptls_hkdf_expand(ptls_hash_algorithm_t *hash, void *output, size_t outlen, ptls_iovec_t prk, ptls_iovec_t info); +/** + * + */ +int ptls_hkdf_expand_label(ptls_hash_algorithm_t *algo, void *output, size_t outlen, ptls_iovec_t secret, const char *label, + ptls_iovec_t hash_value, const char *label_prefix); +/** + * instantiates a symmetric cipher + */ +ptls_cipher_context_t *ptls_cipher_new(ptls_cipher_algorithm_t *algo, int is_enc, const void *key); +/** + * destroys a symmetric cipher + */ +void ptls_cipher_free(ptls_cipher_context_t *ctx); +/** + * initializes the IV; this function must be called prior to calling ptls_cipher_encrypt + */ +static void ptls_cipher_init(ptls_cipher_context_t *ctx, const void *iv); +/** + * Encrypts given text. The function must be used in a way that the output length would be equal to the input length. For example, + * when using a block cipher in ECB mode, `len` must be a multiple of the block size when using a block cipher. The length can be + * of any value when using a stream cipher or a block cipher in CTR mode. + */ +static void ptls_cipher_encrypt(ptls_cipher_context_t *ctx, void *output, const void *input, size_t len); +/** + * instantiates an AEAD cipher given a secret, which is expanded using hkdf to a set of key and iv + * @param aead + * @param hash + * @param is_enc 1 if creating a context for encryption, 0 if creating a context for decryption + * @param secret the secret. The size must be the digest length of the hash algorithm + * @return pointer to an AEAD context if successful, otherwise NULL + */ +ptls_aead_context_t *ptls_aead_new(ptls_aead_algorithm_t *aead, ptls_hash_algorithm_t *hash, int is_enc, const void *secret, + const char *label_prefix); +/** + * instantiates an AEAD cipher given key and iv + * @param aead + * @param is_enc 1 if creating a context for encryption, 0 if creating a context for decryption + * @return pointer to an AEAD context if successful, otherwise NULL + */ +ptls_aead_context_t *ptls_aead_new_direct(ptls_aead_algorithm_t *aead, int is_enc, const void *key, const void *iv); +/** + * destroys an AEAD cipher context + */ +void ptls_aead_free(ptls_aead_context_t *ctx); +/** + * + */ +static size_t ptls_aead_encrypt(ptls_aead_context_t *ctx, void *output, const void *input, size_t inlen, uint64_t seq, + const void *aad, size_t aadlen); +static void ptls_aead_encrypt_s(ptls_aead_context_t *ctx, void *output, const void *input, size_t inlen, uint64_t seq, + const void *aad, size_t aadlen, ptls_aead_supplementary_encryption_t *supp); +/** + * initializes the internal state of the encryptor + */ +static void ptls_aead_encrypt_init(ptls_aead_context_t *ctx, uint64_t seq, const void *aad, size_t aadlen); +/** + * encrypts the input and updates the GCM state + * @return number of bytes emitted to output + */ +static size_t ptls_aead_encrypt_update(ptls_aead_context_t *ctx, void *output, const void *input, size_t inlen); +/** + * emits buffered data (if any) and the GCM tag + * @return number of bytes emitted to output + */ +static size_t ptls_aead_encrypt_final(ptls_aead_context_t *ctx, void *output); +/** + * decrypts an AEAD record + * @return number of bytes emitted to output if successful, or SIZE_MAX if the input is invalid (e.g. broken MAC) + */ +static size_t ptls_aead_decrypt(ptls_aead_context_t *ctx, void *output, const void *input, size_t inlen, uint64_t seq, + const void *aad, size_t aadlen); +/** + * Return the current read epoch. + */ +size_t ptls_get_read_epoch(ptls_t *tls); +/** + * Runs the handshake by dealing directly with handshake messages. Callers MUST delay supplying input to this function until the + * epoch of the input becomes equal to the value returned by `ptls_get_read_epoch()`. + * @param tls the TLS context + * @param sendbuf buffer to which the output will be written + * @param epoch_offsets start and end offset of the messages in each epoch. For example, when the server emits ServerHello between + * offset 0 and 38, the following handshake messages between offset 39 and 348, and a post-handshake message + * between 349 and 451, epoch_offsets will be {0,39,39,349,452} and the length of the sendbuf will be 452. + * This argument is an I/O argument. Applications can either reset sendbuf to empty and epoch_offsets and to + * all zero every time they invoke the function, or retain the values until the handshake completes so that + * data will be appended to sendbuf and epoch_offsets will be adjusted. + * @param in_epoch epoch of the input + * @param input input bytes (must be NULL when starting the handshake on the client side) + * @param inlen length of the input + * @param properties properties specific to the running handshake + * @return same as `ptls_handshake` + */ +int ptls_handle_message(ptls_t *tls, ptls_buffer_t *sendbuf, size_t epoch_offsets[5], size_t in_epoch, const void *input, + size_t inlen, ptls_handshake_properties_t *properties); +int ptls_client_handle_message(ptls_t *tls, ptls_buffer_t *sendbuf, size_t epoch_offsets[5], size_t in_epoch, const void *input, + size_t inlen, ptls_handshake_properties_t *properties); +int ptls_server_handle_message(ptls_t *tls, ptls_buffer_t *sendbuf, size_t epoch_offsets[5], size_t in_epoch, const void *input, + size_t inlen, ptls_handshake_properties_t *properties); +/** + * internal + */ +void ptls_aead__build_iv(ptls_aead_algorithm_t *algo, uint8_t *iv, const uint8_t *static_iv, uint64_t seq); +/** + * + */ +static void ptls_aead__do_encrypt(ptls_aead_context_t *ctx, void *output, const void *input, size_t inlen, uint64_t seq, + const void *aad, size_t aadlen, ptls_aead_supplementary_encryption_t *supp); +/** + * internal + */ +void ptls__key_schedule_update_hash(ptls_key_schedule_t *sched, const uint8_t *msg, size_t msglen); +/** + * clears memory + */ +extern void (*volatile ptls_clear_memory)(void *p, size_t len); +/** + * constant-time memcmp + */ +extern int (*volatile ptls_mem_equal)(const void *x, const void *y, size_t len); +/** + * + */ +static ptls_iovec_t ptls_iovec_init(const void *p, size_t len); +/** + * checks if a server name is an IP address. + */ +int ptls_server_name_is_ipaddr(const char *name); +/** + * loads a certificate chain to ptls_context_t::certificates. `certificate.list` and each element of the list is allocated by + * malloc. It is the responsibility of the user to free them when discarding the TLS context. + */ +int ptls_load_certificates(ptls_context_t *ctx, char const *cert_pem_file); +/** + * + */ +int ptls_esni_init_context(ptls_context_t *ctx, ptls_esni_context_t *esni, ptls_iovec_t esni_keys, + ptls_key_exchange_context_t **key_exchanges); +/** + * + */ +void ptls_esni_dispose_context(ptls_esni_context_t *esni); +/** + * Obtain the ESNI secrets negotiated during the handshake. + */ +ptls_esni_secret_t *ptls_get_esni_secret(ptls_t *ctx); +/** + * + */ +char *ptls_hexdump(char *dst, const void *src, size_t len); +/** + * the default get_time callback + */ +extern ptls_get_time_t ptls_get_time; +#if PICOTLS_USE_DTRACE +/** + * + */ +extern PTLS_THREADLOCAL unsigned ptls_default_skip_tracing; +#else +#define ptls_default_skip_tracing 0 +#endif + +/* inline functions */ + +inline ptls_t *ptls_new(ptls_context_t *ctx, int is_server) +{ + return is_server ? ptls_server_new(ctx) : ptls_client_new(ctx); +} + +inline ptls_iovec_t ptls_iovec_init(const void *p, size_t len) +{ + /* avoid the "return (ptls_iovec_t){(uint8_t *)p, len};" construct because it requires C99 + * and triggers a warning "C4204: nonstandard extension used: non-constant aggregate initializer" + * in Visual Studio */ + ptls_iovec_t r; + r.base = (uint8_t *)p; + r.len = len; + return r; +} + +inline void ptls_buffer_init(ptls_buffer_t *buf, void *smallbuf, size_t smallbuf_size) +{ + assert(smallbuf != NULL); + buf->base = (uint8_t *)smallbuf; + buf->off = 0; + buf->capacity = smallbuf_size; + buf->is_allocated = 0; +} + +inline void ptls_buffer_dispose(ptls_buffer_t *buf) +{ + ptls_buffer__release_memory(buf); + *buf = (ptls_buffer_t){NULL}; +} + +inline uint8_t *ptls_encode_quicint(uint8_t *p, uint64_t v) +{ + if (PTLS_UNLIKELY(v > 63)) { + if (PTLS_UNLIKELY(v > 16383)) { + unsigned sb; + if (PTLS_UNLIKELY(v > 1073741823)) { + assert(v <= 4611686018427387903); + *p++ = 0xc0 | (uint8_t)(v >> 56); + sb = 6 * 8; + } else { + *p++ = 0x80 | (uint8_t)(v >> 24); + sb = 2 * 8; + } + do { + *p++ = (uint8_t)(v >> sb); + } while ((sb -= 8) != 0); + } else { + *p++ = 0x40 | (uint8_t)((uint16_t)v >> 8); + } + } + *p++ = (uint8_t)v; + return p; +} + +inline void ptls_cipher_init(ptls_cipher_context_t *ctx, const void *iv) +{ + ctx->do_init(ctx, iv); +} + +inline void ptls_cipher_encrypt(ptls_cipher_context_t *ctx, void *output, const void *input, size_t len) +{ + ctx->do_transform(ctx, output, input, len); +} + +inline size_t ptls_aead_encrypt(ptls_aead_context_t *ctx, void *output, const void *input, size_t inlen, uint64_t seq, + const void *aad, size_t aadlen) +{ + ctx->do_encrypt(ctx, output, input, inlen, seq, aad, aadlen, NULL); + return inlen + ctx->algo->tag_size; +} + +inline void ptls_aead_encrypt_s(ptls_aead_context_t *ctx, void *output, const void *input, size_t inlen, uint64_t seq, + const void *aad, size_t aadlen, ptls_aead_supplementary_encryption_t *supp) +{ + ctx->do_encrypt(ctx, output, input, inlen, seq, aad, aadlen, supp); +} + +inline void ptls_aead_encrypt_init(ptls_aead_context_t *ctx, uint64_t seq, const void *aad, size_t aadlen) +{ + ctx->do_encrypt_init(ctx, seq, aad, aadlen); +} + +inline size_t ptls_aead_encrypt_update(ptls_aead_context_t *ctx, void *output, const void *input, size_t inlen) +{ + return ctx->do_encrypt_update(ctx, output, input, inlen); +} + +inline size_t ptls_aead_encrypt_final(ptls_aead_context_t *ctx, void *output) +{ + return ctx->do_encrypt_final(ctx, output); +} + +inline void ptls_aead__do_encrypt(ptls_aead_context_t *ctx, void *output, const void *input, size_t inlen, uint64_t seq, + const void *aad, size_t aadlen, ptls_aead_supplementary_encryption_t *supp) +{ + ctx->do_encrypt_init(ctx, seq, aad, aadlen); + ctx->do_encrypt_update(ctx, output, input, inlen); + ctx->do_encrypt_final(ctx, (uint8_t *)output + inlen); + + if (supp != NULL) { + ptls_cipher_init(supp->ctx, supp->input); + memset(supp->output, 0, sizeof(supp->output)); + ptls_cipher_encrypt(supp->ctx, supp->output, supp->output, sizeof(supp->output)); + } +} + +inline size_t ptls_aead_decrypt(ptls_aead_context_t *ctx, void *output, const void *input, size_t inlen, uint64_t seq, + const void *aad, size_t aadlen) +{ + return ctx->do_decrypt(ctx, output, input, inlen, seq, aad, aadlen); +} + +#define ptls_define_hash(name, ctx_type, init_func, update_func, final_func) \ + \ + struct name##_context_t { \ + ptls_hash_context_t super; \ + ctx_type ctx; \ + }; \ + \ + static void name##_update(ptls_hash_context_t *_ctx, const void *src, size_t len) \ + { \ + struct name##_context_t *ctx = (struct name##_context_t *)_ctx; \ + update_func(&ctx->ctx, src, len); \ + } \ + \ + static void name##_final(ptls_hash_context_t *_ctx, void *md, ptls_hash_final_mode_t mode) \ + { \ + struct name##_context_t *ctx = (struct name##_context_t *)_ctx; \ + if (mode == PTLS_HASH_FINAL_MODE_SNAPSHOT) { \ + ctx_type copy = ctx->ctx; \ + final_func(©, md); \ + ptls_clear_memory(©, sizeof(copy)); \ + return; \ + } \ + if (md != NULL) \ + final_func(&ctx->ctx, md); \ + switch (mode) { \ + case PTLS_HASH_FINAL_MODE_FREE: \ + ptls_clear_memory(&ctx->ctx, sizeof(ctx->ctx)); \ + free(ctx); \ + break; \ + case PTLS_HASH_FINAL_MODE_RESET: \ + init_func(&ctx->ctx); \ + break; \ + default: \ + assert(!"FIXME"); \ + break; \ + } \ + } \ + \ + static ptls_hash_context_t *name##_clone(ptls_hash_context_t *_src) \ + { \ + struct name##_context_t *dst, *src = (struct name##_context_t *)_src; \ + if ((dst = malloc(sizeof(*dst))) == NULL) \ + return NULL; \ + *dst = *src; \ + return &dst->super; \ + } \ + \ + static ptls_hash_context_t *name##_create(void) \ + { \ + struct name##_context_t *ctx; \ + if ((ctx = malloc(sizeof(*ctx))) == NULL) \ + return NULL; \ + ctx->super = (ptls_hash_context_t){name##_update, name##_final, name##_clone}; \ + init_func(&ctx->ctx); \ + return &ctx->super; \ + } + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/deps/picoquic/picotls/include/picotls/asn1.h b/deps/picoquic/picotls/include/picotls/asn1.h new file mode 100644 index 0000000..5314c58 --- /dev/null +++ b/deps/picoquic/picotls/include/picotls/asn1.h @@ -0,0 +1,64 @@ +/* +* Copyright (c) 2017 Christian Huitema +* +* Permission to use, copy, modify, and distribute this software for any +* purpose with or without fee is hereby granted, provided that the above +* copyright notice and this permission notice appear in all copies. +* +* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +#ifndef PTLS_ASN1_H +#define PTLS_ASN1_H + +// #include "picotls/minicrypto.h" + +/* +* The ASN.1 functions take a "log context" parameter of type ptls_minicrypto_log_ctx_t. +* +* The log function in that code can be instantiated for example as: +* +* void log_printf(void * ctx, const char * format, ...) +* { +* va_list argptr; +* va_start(argptr, format); +* vfprintf(stderr, format, argptr); +* } +* +* Using definitions from and +*/ + +typedef struct st_ptls_minicrypto_log_ctx_t { + void *ctx; + void (*fn)(void *ctx, const char *format, ...); +} ptls_minicrypto_log_ctx_t; + +size_t ptls_asn1_error_message(char const *error_label, size_t bytes_max, size_t byte_index, int level, + ptls_minicrypto_log_ctx_t *log_ctx); + +void ptls_asn1_dump_content(const uint8_t *bytes, size_t bytes_max, size_t byte_index, ptls_minicrypto_log_ctx_t *log_ctx); + +size_t ptls_asn1_read_type(const uint8_t *bytes, size_t bytes_max, int *structure_bit, int *type_class, uint32_t *type_number, + int *decode_error, int level, ptls_minicrypto_log_ctx_t *log_ctx); + +void ptls_asn1_print_type(int type_class, uint32_t type_number, int level, ptls_minicrypto_log_ctx_t *log_ctx); + +size_t ptls_asn1_read_length(const uint8_t *bytes, size_t bytes_max, size_t byte_index, uint32_t *length, int *indefinite_length, + size_t *last_byte, int *decode_error, int level, ptls_minicrypto_log_ctx_t *log_ctx); + +size_t ptls_asn1_get_expected_type_and_length(const uint8_t *bytes, size_t bytes_max, size_t byte_index, uint8_t expected_type, + uint32_t *length, int *indefinite_length, size_t *last_byte, int *decode_error, + ptls_minicrypto_log_ctx_t *log_ctx); + +size_t ptls_asn1_validation_recursive(const uint8_t *bytes, size_t bytes_max, int *decode_error, int level, + ptls_minicrypto_log_ctx_t *log_ctx); + +int ptls_asn1_validation(const uint8_t *bytes, size_t length, ptls_minicrypto_log_ctx_t *log_ctx); + +#endif diff --git a/deps/picoquic/picotls/include/picotls/certificate_compression.h b/deps/picoquic/picotls/include/picotls/certificate_compression.h new file mode 100644 index 0000000..30a73c4 --- /dev/null +++ b/deps/picoquic/picotls/include/picotls/certificate_compression.h @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2018 Fastly + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +#ifndef picotls_certificate_compression_h +#define picotls_certificate_compression_h + +#ifdef __cplusplus +extern "C" { +#endif + +#include "picotls.h" + +#define PTLS_CERTIFICATE_COMPRESSION_ALGORITHM_GZIP 1 +#define PTLS_CERTIFICATE_COMPRESSION_ALGORITHM_BROTLI 2 + +typedef struct st_ptls_emit_compressed_certificate_t { + ptls_emit_certificate_t super; + uint16_t algo; + struct st_ptls_compressed_certificate_entry_t { + uint32_t uncompressed_length; + ptls_iovec_t bytes; + } with_ocsp_status, without_ocsp_status; +} ptls_emit_compressed_certificate_t; + +extern ptls_decompress_certificate_t ptls_decompress_certificate; + +/** + * initializes a certificate emitter that precompresses a certificate chain (and ocsp status) + */ +int ptls_init_compressed_certificate(ptls_emit_compressed_certificate_t *ecc, ptls_iovec_t *certificates, size_t num_certificates, + ptls_iovec_t ocsp_status); +/** + * + */ +void ptls_dispose_compressed_certificate(ptls_emit_compressed_certificate_t *ecc); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/deps/picoquic/picotls/include/picotls/ffx.h b/deps/picoquic/picotls/include/picotls/ffx.h new file mode 100644 index 0000000..f3cf359 --- /dev/null +++ b/deps/picoquic/picotls/include/picotls/ffx.h @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2019 Christian Huitema + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef PTLS_FFX_H +#define PTLS_FFX_H + +/* + * Format preserving encryption using the FFX algorithm. + * + * We demonstrate here a simple encryption process derived + * from the FFX algorithms, which is effectively a specific + * mode of running a verified encryption code. The + * algorithm is Feistel cipher in which the S-boxes are + * defined by a symmetric encryption algorithm such as + * AES or ChaCha20. + * See "Ciphers with Arbitrary Finite Domains" by + * John Black and Phillip Rogaway, 2001 -- + * http://web.cs.ucdavis.edu/~rogaway/papers/subset.pdf + * + * An instantiation of the algorithm is defined by a + * series of parameters: + * - the context of the symmetric crypto algorithm, + * - key used for the symmetric algorithm, + * - number of rounds, + * - length of the block in bits + * + * We consider just two symmetric algorithms for now, + * ChaCha20 and AES128CTR. In theory, any symmetric algorithm + * operating on a 128 bit block would work, and crytographic + * hashes producing at least 128 bits of output could also + * be used. In practice, ChaCha20 and AES128 cover most of + * the use cases. + * + * The implementation will produce a result for any block + * length lower than 256, although values lower than 32 would + * not be recommended. + * + * The block to be encrypted is passed as a byte array of size + * (block_length + 7)/8. When the block_length is not a + * multiple of 8, the algorithm guarantees that the extra bits + * in the last byte are left untouched. For example, if the + * block length is 39, the least significant bit of the + * fifth byte will be copied from input to output. + * + * The number of rounds is left as a configuration parameter, + * which is constrained to be even by our implementation. The + * required number of passes varies with the application's + * constraints. The practical minimum is 4 passes. Demanding + * applications can use 8 passes, and the practical conservative + * value is 10, as specified by NIST for the FF1 variant of + * the same algorithm. This choice between 4, 8 or 10 is + * based on "Luby-Rackoff: 7 Rounds are Enough + * for 2^n(1-epsilon) Security" by Jacques Patarin, 2003 -- + * https://www.iacr.org/archive/crypto2003/27290510/27290510.pdf + * + * Encrypting short numbers, by nature, produces a codebook + * of limited size. In many applications, the short number is + * part of a larger object that is passed in clear text. In that + * case, NIST recommends using as much as possible of that clear + * text as an initialization vector, used as "tweak" in the + * FFX algorithm. See: + * https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-38G.pdf + */ + +typedef struct st_ptls_ffx_context_t { + ptls_cipher_context_t super; + ptls_cipher_context_t *enc_ctx; + int nb_rounds; + int is_enc; + size_t byte_length; + size_t nb_left; + size_t nb_right; + uint8_t mask_last_byte; + uint8_t tweaks[16]; +} ptls_ffx_context_t; + +/** + * The PTLS_FFX_CIPHER_ALGO macro will define a variant of the FFX algorithm by specifying + * the base algorithm (vraiable name of type ptls_cipher_algorithm_t), the bit length + * of the block, the selected number of blocks and the key size of the base algorithm, + * in bytes. + * + * The macro will automatically generate an algorithm name, of the form: + * ptls_ffx__b_r + * For example, selecting the algorithm "ptls_minicrypto_chacha20" with a block + * size of 53 bits and 8 rounds will generate the name: + * ptls_ffx_ptls_minicrypto_chacha20_b53_r8 + * This name is declared as a static variable. + * + * Once the FFX variant is defined, the name can be used to create a + * cipher context using ptls_cipher_new. The context can then be used + * through the function ptls_cipher_init, ptls_cipher_encrypt, and + * can be freed by calling ptls_cipher_free. + * + * The ptls_cipher_encrypt will encrypt a code word of the specified + * bit length, or decrypt it if the context was created with the + * option "is_enc = 0". The code word is represented as an array of + * bytes. If the bit length is not a multiple of 8, the remaining + * low level bits in the last byte will be left unchanged. + */ +#define PTLS_FFX_CIPHER_ALGO_NAME(base, bitlength, nbrounds) #base "-ffx-b" #bitlength "-r" #nbrounds +#define PTLS_FFX_CIPHER_ALGO(base, bitlength, nbrounds, keysize) \ + static int ptls_ffx_##base##_b##bitlength##_r##nbrounds##_setup(ptls_cipher_context_t *ctx, int is_enc, const void *key) \ + { \ + return ptls_ffx_setup_crypto(ctx, &base, is_enc, nbrounds, bitlength, key); \ + } \ + static ptls_cipher_algorithm_t ptls_ffx_##base##_b##bitlength##_r##nbrounds = { \ + PTLS_FFX_CIPHER_ALGO_NAME(base, bitlength, nbrounds), keysize, (bitlength + 7) / 8, 16, sizeof(ptls_ffx_context_t), \ + ptls_ffx_##base##_b##bitlength##_r##nbrounds##_setup}; + +/* + * The function ptls_ffx_new creates a cipher context for a specific FFX variant. + * It is equivalent to defining the variant with the PTLS_FFX_CIPHER_ALGO macro, + * then creating the context using ptls_cipher_new. + */ +ptls_cipher_context_t *ptls_ffx_new(ptls_cipher_algorithm_t *algo, int is_enc, int nb_rounds, size_t bit_length, const void *key); + +/** + * The function ptls_ffx_setup_crypto is called by ptls_cipher_new or + * ptls_ffx_new when initializing an FFX variant. It should not be + * called directly. + */ +int ptls_ffx_setup_crypto(ptls_cipher_context_t *_ctx, ptls_cipher_algorithm_t *algo, int is_enc, int nb_rounds, size_t bit_length, + const void *key); +#endif /* PTLS_FFX_H */ diff --git a/deps/picoquic/picotls/include/picotls/fusion.h b/deps/picoquic/picotls/include/picotls/fusion.h new file mode 100644 index 0000000..332dd93 --- /dev/null +++ b/deps/picoquic/picotls/include/picotls/fusion.h @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2020 Fastly, Kazuho Oku + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +#ifndef picotls_fusion_h +#define picotls_fusion_h + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include "../picotls.h" + +#define PTLS_FUSION_AES128_ROUNDS 10 +#define PTLS_FUSION_AES256_ROUNDS 14 + +typedef struct ptls_fusion_aesecb_context { + __m128i keys[PTLS_FUSION_AES256_ROUNDS + 1]; + unsigned rounds; +} ptls_fusion_aesecb_context_t; + +typedef struct ptls_fusion_aesgcm_context ptls_fusion_aesgcm_context_t; + +void ptls_fusion_aesecb_init(ptls_fusion_aesecb_context_t *ctx, int is_enc, const void *key, size_t key_size); +void ptls_fusion_aesecb_dispose(ptls_fusion_aesecb_context_t *ctx); +void ptls_fusion_aesecb_encrypt(ptls_fusion_aesecb_context_t *ctx, void *dst, const void *src); + +/** + * Creates an AES-GCM context. + * @param key the AES key (128 bits) + * @param capacity maximum size of AEAD record (i.e. AAD + encrypted payload) + */ +ptls_fusion_aesgcm_context_t *ptls_fusion_aesgcm_new(const void *key, size_t key_size, size_t capacity); +/** + * Updates the capacity. + */ +ptls_fusion_aesgcm_context_t *ptls_fusion_aesgcm_set_capacity(ptls_fusion_aesgcm_context_t *ctx, size_t capacity); +/** + * Destroys an AES-GCM context. + */ +void ptls_fusion_aesgcm_free(ptls_fusion_aesgcm_context_t *ctx); +/** + * Encrypts an AEAD block, and in parallel, optionally encrypts one block using AES-ECB. + * @param ctx context + * @param output output buffer + * @param input payload to be encrypted + * @param inlen size of the payload to be encrypted + * @param counter + * @param aad AAD + * @param aadlen size of AAD + * @param supp (optional) supplementary encryption context + */ +void ptls_fusion_aesgcm_encrypt(ptls_fusion_aesgcm_context_t *ctx, void *output, const void *input, size_t inlen, __m128i ctr, + const void *aad, size_t aadlen, ptls_aead_supplementary_encryption_t *supp); +/** + * Decrypts an AEAD block, an in parallel, optionally encrypts one block using AES-ECB. Returns if decryption was successful. + * @param iv initialization vector of 12 bytes + * @param output output buffer + * @param input payload to be decrypted + * @param inlen size of the payload to be decrypted + * @param aad AAD + * @param aadlen size of AAD + * @param tag the AEAD tag being received from peer + */ +int ptls_fusion_aesgcm_decrypt(ptls_fusion_aesgcm_context_t *ctx, void *output, const void *input, size_t inlen, __m128i ctr, + const void *aad, size_t aadlen, const void *tag); + +extern ptls_cipher_algorithm_t ptls_fusion_aes128ctr, ptls_fusion_aes256ctr; +extern ptls_aead_algorithm_t ptls_fusion_aes128gcm, ptls_fusion_aes256gcm; + +/** + * Returns a boolean indicating if fusion can be used. + */ +int ptls_fusion_is_supported_by_cpu(void); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/deps/picoquic/picotls/include/picotls/minicrypto.h b/deps/picoquic/picotls/include/picotls/minicrypto.h new file mode 100644 index 0000000..af0c714 --- /dev/null +++ b/deps/picoquic/picotls/include/picotls/minicrypto.h @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2016 DeNA Co., Ltd., Kazuho Oku + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +#ifndef picotls_minicrypto_h +#define picotls_minicrypto_h + +#ifdef __cplusplus +extern "C" { +#endif + +#include "picotls.h" + +#define SECP256R1_PRIVATE_KEY_SIZE 32 +#define SECP256R1_PUBLIC_KEY_SIZE 65 /* including the header */ +#define SECP256R1_SHARED_SECRET_SIZE 32 + +typedef struct st_ptls_minicrypto_secp256r1sha256_sign_certificate_t { + ptls_sign_certificate_t super; + uint8_t key[SECP256R1_PRIVATE_KEY_SIZE]; +} ptls_minicrypto_secp256r1sha256_sign_certificate_t; + +void ptls_minicrypto_random_bytes(void *buf, size_t len); + +int ptls_minicrypto_init_secp256r1sha256_sign_certificate(ptls_minicrypto_secp256r1sha256_sign_certificate_t *self, + ptls_iovec_t key); + +extern ptls_key_exchange_algorithm_t ptls_minicrypto_secp256r1, ptls_minicrypto_x25519; +extern ptls_key_exchange_algorithm_t *ptls_minicrypto_key_exchanges[]; +extern ptls_cipher_algorithm_t ptls_minicrypto_aes128ecb, ptls_minicrypto_aes256ecb, ptls_minicrypto_aes128ctr, + ptls_minicrypto_aes256ctr, ptls_minicrypto_chacha20; +extern ptls_aead_algorithm_t ptls_minicrypto_aes128gcm, ptls_minicrypto_aes256gcm, ptls_minicrypto_chacha20poly1305; +extern ptls_hash_algorithm_t ptls_minicrypto_sha256, ptls_minicrypto_sha384; +extern ptls_cipher_suite_t ptls_minicrypto_aes128gcmsha256, ptls_minicrypto_aes256gcmsha384, ptls_minicrypto_chacha20poly1305sha256; +extern ptls_cipher_suite_t *ptls_minicrypto_cipher_suites[]; + +typedef struct st_ptls_asn1_pkcs8_private_key_t { + ptls_iovec_t vec; + size_t algorithm_index; + uint32_t algorithm_length; + size_t parameters_index; + uint32_t parameters_length; + size_t key_data_index; + uint32_t key_data_length; +} ptls_asn1_pkcs8_private_key_t; + +int ptls_minicrypto_load_private_key(ptls_context_t *ctx, char const *pem_fname); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/deps/picoquic/picotls/include/picotls/openssl.h b/deps/picoquic/picotls/include/picotls/openssl.h new file mode 100644 index 0000000..2bfe1b9 --- /dev/null +++ b/deps/picoquic/picotls/include/picotls/openssl.h @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2016 DeNA Co., Ltd., Kazuho Oku + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +#ifndef picotls_openssl_h +#define picotls_openssl_h + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include +#include +#include +#include "../picotls.h" + +#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER) +#if !defined(OPENSSL_NO_CHACHA) && !defined(OPENSSL_NO_POLY1305) +#define PTLS_OPENSSL_HAVE_CHACHA20_POLY1305 1 +#endif +#endif + +extern ptls_key_exchange_algorithm_t ptls_openssl_secp256r1; +#ifdef NID_secp384r1 +#define PTLS_OPENSSL_HAVE_SECP384R1 1 +#define PTLS_OPENSSL_HAS_SECP384R1 1 /* deprecated; use HAVE_ */ +extern ptls_key_exchange_algorithm_t ptls_openssl_secp384r1; +#endif +#ifdef NID_secp521r1 +#define PTLS_OPENSSL_HAVE_SECP521R1 1 +#define PTLS_OPENSSL_HAS_SECP521R1 1 /* deprecated; use HAVE_ */ +extern ptls_key_exchange_algorithm_t ptls_openssl_secp521r1; +#endif +#if defined(NID_X25519) && !defined(LIBRESSL_VERSION_NUMBER) +#define PTLS_OPENSSL_HAVE_X25519 1 +#define PTLS_OPENSSL_HAS_X25519 1 /* deprecated; use HAVE_ */ +extern ptls_key_exchange_algorithm_t ptls_openssl_x25519; +#endif +#ifndef OPENSSL_NO_BF +#define PTLS_OPENSSL_HAVE_BF 1 +#endif + +extern ptls_key_exchange_algorithm_t *ptls_openssl_key_exchanges[]; + +extern ptls_cipher_algorithm_t ptls_openssl_aes128ecb; +extern ptls_cipher_algorithm_t ptls_openssl_aes128ctr; +extern ptls_aead_algorithm_t ptls_openssl_aes128gcm; +extern ptls_cipher_algorithm_t ptls_openssl_aes256ecb; +extern ptls_cipher_algorithm_t ptls_openssl_aes256ctr; +extern ptls_aead_algorithm_t ptls_openssl_aes256gcm; +extern ptls_hash_algorithm_t ptls_openssl_sha256; +extern ptls_hash_algorithm_t ptls_openssl_sha384; +extern ptls_cipher_suite_t ptls_openssl_aes128gcmsha256; +extern ptls_cipher_suite_t ptls_openssl_aes256gcmsha384; +extern ptls_cipher_suite_t *ptls_openssl_cipher_suites[]; + +#if defined(PTLS_OPENSSL_HAVE_CHACHA20_POLY1305) +extern ptls_cipher_algorithm_t ptls_openssl_chacha20; +extern ptls_aead_algorithm_t ptls_openssl_chacha20poly1305; +extern ptls_cipher_suite_t ptls_openssl_chacha20poly1305sha256; +#endif + +#if PTLS_OPENSSL_HAVE_BF +extern ptls_cipher_algorithm_t ptls_openssl_bfecb; +#endif + +void ptls_openssl_random_bytes(void *buf, size_t len); +/** + * constructs a key exchange context. pkey's reference count is incremented. + */ +int ptls_openssl_create_key_exchange(ptls_key_exchange_context_t **ctx, EVP_PKEY *pkey); + +struct st_ptls_openssl_signature_scheme_t { + uint16_t scheme_id; + const EVP_MD *scheme_md; +}; + +typedef struct st_ptls_openssl_sign_certificate_t { + ptls_sign_certificate_t super; + EVP_PKEY *key; + struct st_ptls_openssl_signature_scheme_t schemes[4]; /* terminated by .scheme_id == UINT16_MAX */ +} ptls_openssl_sign_certificate_t; + +int ptls_openssl_init_sign_certificate(ptls_openssl_sign_certificate_t *self, EVP_PKEY *key); +void ptls_openssl_dispose_sign_certificate(ptls_openssl_sign_certificate_t *self); +int ptls_openssl_load_certificates(ptls_context_t *ctx, X509 *cert, STACK_OF(X509) * chain); + +typedef struct st_ptls_openssl_verify_certificate_t { + ptls_verify_certificate_t super; + X509_STORE *cert_store; +} ptls_openssl_verify_certificate_t; + +int ptls_openssl_init_verify_certificate(ptls_openssl_verify_certificate_t *self, X509_STORE *store); +void ptls_openssl_dispose_verify_certificate(ptls_openssl_verify_certificate_t *self); +X509_STORE *ptls_openssl_create_default_certificate_store(void); + +int ptls_openssl_encrypt_ticket(ptls_buffer_t *dst, ptls_iovec_t src, + int (*cb)(unsigned char *, unsigned char *, EVP_CIPHER_CTX *, HMAC_CTX *, int)); +int ptls_openssl_decrypt_ticket(ptls_buffer_t *dst, ptls_iovec_t src, + int (*cb)(unsigned char *, unsigned char *, EVP_CIPHER_CTX *, HMAC_CTX *, int)); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/deps/picoquic/picotls/include/picotls/pembase64.h b/deps/picoquic/picotls/include/picotls/pembase64.h new file mode 100644 index 0000000..1b59910 --- /dev/null +++ b/deps/picoquic/picotls/include/picotls/pembase64.h @@ -0,0 +1,44 @@ +/* +* Copyright (c) 2017 Christian Huitema +* +* Permission to use, copy, modify, and distribute this software for any +* purpose with or without fee is hereby granted, provided that the above +* copyright notice and this permission notice appear in all copies. +* +* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +#ifndef PTLS_PEMBASE64_H +#define PTLS_PEMBASE64_H + +/* +* Base64 functions used in encoding and decoding of PEM files +*/ + +#define PTLS_BASE64_DECODE_DONE 0 +#define PTLS_BASE64_DECODE_IN_PROGRESS 1 +#define PTLS_BASE64_DECODE_FAILED -1 + +typedef struct st_ptls_base64_decode_state_t { + int nbc; + int nbo; + int status; + uint32_t v; +} ptls_base64_decode_state_t; + +int ptls_base64_encode(const uint8_t *data, size_t data_len, char *base64_text); + +size_t ptls_base64_howlong(size_t data_length); + +void ptls_base64_decode_init(ptls_base64_decode_state_t *state); +int ptls_base64_decode(const char *base64_text, ptls_base64_decode_state_t *state, ptls_buffer_t *buf); + +int ptls_load_pem_objects(char const *pem_fname, const char *label, ptls_iovec_t *list, size_t list_max, size_t *nb_objects); + +#endif /* PTLS_PEMBASE64_H */ diff --git a/deps/picoquic/picotls/include/picotls/ptlsbcrypt.h b/deps/picoquic/picotls/include/picotls/ptlsbcrypt.h new file mode 100644 index 0000000..5317641 --- /dev/null +++ b/deps/picoquic/picotls/include/picotls/ptlsbcrypt.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2016 DeNA Co., Ltd., Kazuho Oku + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +#ifndef picotls_bcrypt_h +#define picotls_bcrypt_h + +#ifdef __cplusplus +extern "C" { +#endif + +#include "../picotls.h" + +#ifdef _WINDOWS +#include + +extern ptls_cipher_algorithm_t ptls_bcrypt_aes128ecb; +extern ptls_cipher_algorithm_t ptls_bcrypt_aes256ecb; +extern ptls_cipher_algorithm_t ptls_bcrypt_aes128ctr; +extern ptls_cipher_algorithm_t ptls_bcrypt_aes256ctr; + +extern ptls_aead_algorithm_t ptls_bcrypt_aes128gcm; +extern ptls_aead_algorithm_t ptls_bcrypt_aes256gcm; + +extern ptls_hash_algorithm_t ptls_bcrypt_sha256; +extern ptls_hash_algorithm_t ptls_bcrypt_sha384; + +extern ptls_cipher_suite_t ptls_bcrypt_aes128gcmsha256; +extern ptls_cipher_suite_t ptls_bcrypt_aes256gcmsha384; +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* picotls_bcrypt_h */ diff --git a/deps/picoquic/picotls/lib/asn1.c b/deps/picoquic/picotls/lib/asn1.c new file mode 100644 index 0000000..f394a82 --- /dev/null +++ b/deps/picoquic/picotls/lib/asn1.c @@ -0,0 +1,297 @@ +/* + * Copyright (c) 2016 Christian Huitema + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * Basic ASN1 validation and optional print-out + */ + +#ifdef _WINDOWS +#include "wincompat.h" +#else +#include +#endif +#include +#include +#include +#include +#include "picotls.h" +#include "picotls/minicrypto.h" +#include "picotls/asn1.h" + +static char const *asn1_type_classes[4] = {"Universal", "Application", "Context-specific", "Private"}; + +static char const *asn1_universal_types[] = { + "End-of-Content", "BOOLEAN", "INTEGER", "BIT STRING", "OCTET STRING", "NULL", + "OBJECT IDENTIFIER", "Object Descriptor", "EXTERNAL", "REAL", "ENUMERATED", "EMBEDDED PDV", + "UTF8String", "RELATIVE-OID", "Reserved (16)", "Reserved (17)", "SEQUENCE", "SET", + "NumericString", "PrintableString", "T61String", "VideotexString", "IA5String", "UTCTime", + "GeneralizedTime", "GraphicString", "VisibleString", "GeneralString", "UniversalString", "CHARACTER STRING", + "BMPString"}; + +static size_t nb_asn1_universal_types = sizeof(asn1_universal_types) / sizeof(char const *); + +static void ptls_asn1_print_indent(int level, ptls_minicrypto_log_ctx_t *log_ctx) +{ + for (int indent = 0; indent <= level; indent++) { + log_ctx->fn(log_ctx->ctx, " "); + } +} + +size_t ptls_asn1_error_message(char const *error_label, size_t bytes_max, size_t byte_index, int level, + ptls_minicrypto_log_ctx_t *log_ctx) +{ + if (log_ctx != NULL) { + ptls_asn1_print_indent(level, log_ctx); + log_ctx->fn(log_ctx->ctx, "Error: %s (near position: %d (0x%x) out of %d)", error_label, (int)byte_index, + (uint32_t)byte_index, (int)bytes_max); + } + return byte_index; +} + +void ptls_asn1_dump_content(const uint8_t *bytes, size_t bytes_max, size_t byte_index, ptls_minicrypto_log_ctx_t *log_ctx) +{ + if (log_ctx != NULL && bytes_max > byte_index) { + size_t nb_bytes = bytes_max - byte_index; + + log_ctx->fn(log_ctx->ctx, " "); + + for (size_t i = 0; i < 16 && i < nb_bytes; i++) { + log_ctx->fn(log_ctx->ctx, "%02x", bytes[byte_index + i]); + } + + if (nb_bytes > 16) { + log_ctx->fn(log_ctx->ctx, "..."); + } + } +} + +size_t ptls_asn1_read_type(const uint8_t *bytes, size_t bytes_max, int *structure_bit, int *type_class, uint32_t *type_number, + int *decode_error, int level, ptls_minicrypto_log_ctx_t *log_ctx) +{ + /* Get the type byte */ + size_t byte_index = 1; + uint8_t first_byte = bytes[0]; + *structure_bit = (first_byte >> 5) & 1; + *type_class = (first_byte >> 6) & 3; + *type_number = first_byte & 31; + + if (*type_number == 31) { + uint32_t long_type = 0; + const uint32_t type_number_limit = 0x07FFFFFFF; + int next_byte; + int end_found = 0; + + while (byte_index < bytes_max && long_type <= type_number_limit) { + next_byte = bytes[byte_index++]; + long_type <<= 7; + long_type |= next_byte & 127; + if ((next_byte & 128) == 0) { + end_found = 1; + break; + } + } + + if (end_found) { + *type_number = long_type; + } else { + /* This is an error */ + byte_index = ptls_asn1_error_message("Incorrect type coding", bytes_max, byte_index, level, log_ctx); + *decode_error = PTLS_ERROR_BER_MALFORMED_TYPE; + } + } + + return byte_index; +} + +void ptls_asn1_print_type(int type_class, uint32_t type_number, int level, ptls_minicrypto_log_ctx_t *log_ctx) +{ + /* Print the type */ + ptls_asn1_print_indent(level, log_ctx); + if (type_class == 0 && type_number < nb_asn1_universal_types) { + log_ctx->fn(log_ctx->ctx, "%s", asn1_universal_types[type_number]); + } else if (type_class == 2) { + log_ctx->fn(log_ctx->ctx, "[%d]", type_number); + } else { + log_ctx->fn(log_ctx->ctx, "%s[%d]", asn1_type_classes[type_class], type_number); + } +} + +size_t ptls_asn1_read_length(const uint8_t *bytes, size_t bytes_max, size_t byte_index, uint32_t *length, int *indefinite_length, + size_t *last_byte, int *decode_error, int level, ptls_minicrypto_log_ctx_t *log_ctx) +{ + int length_of_length = 0; + + *indefinite_length = 0; + *length = 0; + *last_byte = bytes_max; + + if (byte_index < bytes_max) { + *length = bytes[byte_index++]; + if ((*length & 128) != 0) { + length_of_length = *length & 127; + *length = 0; + + if (byte_index + length_of_length >= bytes_max) { + /* This is an error */ + byte_index = ptls_asn1_error_message("Incorrect length coding", bytes_max, byte_index, level, log_ctx); + *decode_error = PTLS_ERROR_BER_MALFORMED_LENGTH; + } else { + for (int i = 0; i < length_of_length && byte_index < bytes_max; i++) { + *length <<= 8; + *length |= bytes[byte_index++]; + } + + if (length_of_length == 0) { + *last_byte = bytes_max; + *indefinite_length = 1; + } else { + *last_byte = byte_index + *length; + } + } + } else { + *last_byte = byte_index + *length; + } + + if (*decode_error == 0) { + /* TODO: verify that the length makes sense */ + if (*last_byte > bytes_max) { + byte_index = ptls_asn1_error_message("Length larger than message", bytes_max, byte_index, level, log_ctx); + *decode_error = PTLS_ERROR_BER_EXCESSIVE_LENGTH; + } + } + } + + return byte_index; +} + +size_t ptls_asn1_get_expected_type_and_length(const uint8_t *bytes, size_t bytes_max, size_t byte_index, uint8_t expected_type, + uint32_t *length, int *indefinite_length, size_t *last_byte, int *decode_error, + ptls_minicrypto_log_ctx_t *log_ctx) +{ + int is_indefinite = 0; + + /* Check that the expected type is present */ + if (bytes[byte_index] != expected_type) { + byte_index = ptls_asn1_error_message("Unexpected type", bytes_max, byte_index, 0, log_ctx); + *decode_error = PTLS_ERROR_INCORRECT_ASN1_SYNTAX; + } else { + /* get length of element */ + byte_index++; + byte_index = + ptls_asn1_read_length(bytes, bytes_max, byte_index, length, &is_indefinite, last_byte, decode_error, 0, log_ctx); + + if (indefinite_length != NULL) { + *indefinite_length = is_indefinite; + } else if (is_indefinite) { + byte_index = ptls_asn1_error_message("Incorrect length for DER", bytes_max, byte_index, 0, log_ctx); + *decode_error = PTLS_ERROR_DER_INDEFINITE_LENGTH; + } + } + + return byte_index; +} + +size_t ptls_asn1_validation_recursive(const uint8_t *bytes, size_t bytes_max, int *decode_error, int level, + ptls_minicrypto_log_ctx_t *log_ctx) +{ + /* Get the type byte */ + int structure_bit = 0; + int type_class = 0; + uint32_t type_number = 0; + uint32_t length = 0; + int indefinite_length = 0; + size_t last_byte = 0; + /* Decode the type */ + size_t byte_index = + ptls_asn1_read_type(bytes, bytes_max, &structure_bit, &type_class, &type_number, decode_error, level, log_ctx); + + if (*decode_error == 0 && log_ctx != NULL) { + ptls_asn1_print_type(type_class, type_number, level, log_ctx); + } + + /* Get the length */ + byte_index = + ptls_asn1_read_length(bytes, bytes_max, byte_index, &length, &indefinite_length, &last_byte, decode_error, level, log_ctx); + + if (last_byte <= bytes_max) { + if (structure_bit) { + /* If structured, recurse on a loop */ + if (log_ctx != NULL) { + log_ctx->fn(log_ctx->ctx, " {\n"); + } + + while (byte_index < last_byte) { + if (indefinite_length != 0 && bytes[byte_index] == 0) { + if (byte_index + 2 > bytes_max || bytes[byte_index + 1] != 0) { + byte_index = + ptls_asn1_error_message("EOC: unexpected end of content", bytes_max, byte_index, level + 1, log_ctx); + + *decode_error = PTLS_ERROR_BER_UNEXPECTED_EOC; + byte_index = bytes_max; + break; + } else { + if (log_ctx != NULL) { + ptls_asn1_print_indent(level, log_ctx); + log_ctx->fn(log_ctx->ctx, "EOC\n"); + } + byte_index += 2; + break; + } + } else { + byte_index += ptls_asn1_validation_recursive(bytes + byte_index, last_byte - byte_index, decode_error, + level + 1, log_ctx); + + if (*decode_error) { + byte_index = bytes_max; + break; + } + } + + if (log_ctx != NULL) { + if (byte_index < last_byte) { + log_ctx->fn(log_ctx->ctx, ","); + } + log_ctx->fn(log_ctx->ctx, "\n"); + } + } + + if (log_ctx != NULL) { + ptls_asn1_print_indent(level, log_ctx); + log_ctx->fn(log_ctx->ctx, "}"); + } + } else { + ptls_asn1_dump_content(bytes, last_byte, byte_index, log_ctx); + byte_index = last_byte; + } + } + + return byte_index; +} + +int ptls_asn1_validation(const uint8_t *bytes, size_t length, ptls_minicrypto_log_ctx_t *log_ctx) +{ + int decode_error = 0; + size_t decoded = ptls_asn1_validation_recursive(bytes, length, &decode_error, 0, log_ctx); + + if (decode_error == 0 && decoded < length) { + decode_error = PTLS_ERROR_BER_ELEMENT_TOO_SHORT; + if (log_ctx != NULL) { + log_ctx->fn(log_ctx->ctx, "Type too short, %d bytes only out of %d\n", (int)decoded, (int)length); + } + } + + return decode_error; +} diff --git a/deps/picoquic/picotls/lib/certificate_compression.c b/deps/picoquic/picotls/lib/certificate_compression.c new file mode 100644 index 0000000..36e77fd --- /dev/null +++ b/deps/picoquic/picotls/lib/certificate_compression.c @@ -0,0 +1,149 @@ +/* + * Copyright (c) 2018 Fastly + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +#include +#include +#include "brotli/decode.h" +#include "brotli/encode.h" +#include "picotls/certificate_compression.h" + +static inline int decompress_certificate(ptls_decompress_certificate_t *self, ptls_t *tls, uint16_t algorithm, ptls_iovec_t output, + ptls_iovec_t input) +{ + if (algorithm != PTLS_CERTIFICATE_COMPRESSION_ALGORITHM_BROTLI) + goto Fail; + + size_t decoded_size = output.len; + if (BrotliDecoderDecompress(input.len, input.base, &decoded_size, output.base) != BROTLI_DECODER_RESULT_SUCCESS) + goto Fail; + + if (decoded_size != output.len) + goto Fail; + + return 0; +Fail: + return PTLS_ALERT_BAD_CERTIFICATE; +} + +static const uint16_t algorithms[] = {PTLS_CERTIFICATE_COMPRESSION_ALGORITHM_BROTLI, UINT16_MAX}; + +ptls_decompress_certificate_t ptls_decompress_certificate = {algorithms, decompress_certificate}; + +static int emit_compressed_certificate(ptls_emit_certificate_t *_self, ptls_t *tls, ptls_message_emitter_t *emitter, + ptls_key_schedule_t *key_sched, ptls_iovec_t context, int push_status_request, + const uint16_t *compress_algos, size_t num_compress_algos) +{ + ptls_emit_compressed_certificate_t *self = (void *)_self; + struct st_ptls_compressed_certificate_entry_t *entry; + int ret; + + assert(context.len == 0 || !"precompressed mode can only be used for server certificates"); + + for (size_t i = 0; i != num_compress_algos; ++i) { + if (compress_algos[i] == PTLS_CERTIFICATE_COMPRESSION_ALGORITHM_BROTLI) + goto FoundBrotli; + } + /* brotli not found, delegate to the core */ + ret = PTLS_ERROR_DELEGATE; + goto Exit; + +FoundBrotli: + entry = &self->without_ocsp_status; + if (push_status_request && self->with_ocsp_status.uncompressed_length != 0) + entry = &self->with_ocsp_status; + + ptls_push_message(emitter, key_sched, PTLS_HANDSHAKE_TYPE_COMPRESSED_CERTIFICATE, { + ptls_buffer_push16(emitter->buf, PTLS_CERTIFICATE_COMPRESSION_ALGORITHM_BROTLI); + ptls_buffer_push24(emitter->buf, entry->uncompressed_length); + ptls_buffer_push_block(emitter->buf, 3, { ptls_buffer_pushv(emitter->buf, entry->bytes.base, entry->bytes.len); }); + }); + + ret = 0; + +Exit: + return ret; +} + +static int build_compressed(struct st_ptls_compressed_certificate_entry_t *entry, ptls_iovec_t *certificates, + size_t num_certificates, ptls_iovec_t ocsp_status) +{ + ptls_buffer_t uncompressed; + int ret; + + ptls_buffer_init(&uncompressed, "", 0); + + /* build uncompressed */ + if ((ret = ptls_build_certificate_message(&uncompressed, ptls_iovec_init(NULL, 0), certificates, num_certificates, + ocsp_status)) != 0) + goto Exit; + entry->uncompressed_length = (uint32_t)uncompressed.off; + + /* compress */ + entry->bytes.len = uncompressed.off - 1; + if ((entry->bytes.base = malloc(entry->bytes.len)) == NULL) { + ret = PTLS_ERROR_NO_MEMORY; + goto Exit; + } + if (BrotliEncoderCompress(BROTLI_MAX_QUALITY, BROTLI_DEFAULT_WINDOW, BROTLI_MODE_GENERIC, uncompressed.off, uncompressed.base, + &entry->bytes.len, entry->bytes.base) != BROTLI_TRUE) { + ret = PTLS_ERROR_COMPRESSION_FAILURE; + goto Exit; + } + + ret = 0; + +Exit: + if (ret != 0) { + free(entry->bytes.base); + *entry = (struct st_ptls_compressed_certificate_entry_t){0}; + } + ptls_buffer_dispose(&uncompressed); + return ret; +} + +int ptls_init_compressed_certificate(ptls_emit_compressed_certificate_t *self, ptls_iovec_t *certificates, size_t num_certificates, + ptls_iovec_t ocsp_status) +{ + int ret; + + *self = (ptls_emit_compressed_certificate_t){{emit_compressed_certificate}, PTLS_CERTIFICATE_COMPRESSION_ALGORITHM_BROTLI}; + + /* build entries */ + if ((ret = build_compressed(&self->without_ocsp_status, certificates, num_certificates, ptls_iovec_init(NULL, 0))) != 0) + goto Exit; + if (ocsp_status.len != 0) { + if ((ret = build_compressed(&self->with_ocsp_status, certificates, num_certificates, ocsp_status)) != 0) + goto Exit; + } + + ret = 0; + +Exit: + if (ret != 0) + ptls_dispose_compressed_certificate(self); + return ret; +} + +void ptls_dispose_compressed_certificate(ptls_emit_compressed_certificate_t *self) +{ + free(self->with_ocsp_status.bytes.base); + free(self->without_ocsp_status.bytes.base); +} diff --git a/deps/picoquic/picotls/lib/cifra.c b/deps/picoquic/picotls/lib/cifra.c new file mode 100644 index 0000000..b41d3bc --- /dev/null +++ b/deps/picoquic/picotls/lib/cifra.c @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2016 DeNA Co., Ltd., Kazuho Oku + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +#include +#include "picotls.h" +#include "picotls/minicrypto.h" + +ptls_cipher_suite_t *ptls_minicrypto_cipher_suites[] = {&ptls_minicrypto_aes256gcmsha384, &ptls_minicrypto_aes128gcmsha256, + &ptls_minicrypto_chacha20poly1305sha256, NULL}; diff --git a/deps/picoquic/picotls/lib/cifra/aes-common.h b/deps/picoquic/picotls/lib/cifra/aes-common.h new file mode 100644 index 0000000..0c393c5 --- /dev/null +++ b/deps/picoquic/picotls/lib/cifra/aes-common.h @@ -0,0 +1,178 @@ +/* + * Copyright (c) 2016 DeNA Co., Ltd., Kazuho Oku + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +#include +#include +#include "aes.h" +#include "modes.h" +#include "sha2.h" +#include "picotls.h" +#include "picotls/minicrypto.h" + +struct aesecb_context_t { + ptls_cipher_context_t super; + cf_aes_context aes; +}; + +static inline void aesecb_dispose(ptls_cipher_context_t *_ctx) +{ + struct aesecb_context_t *ctx = (struct aesecb_context_t *)_ctx; + ptls_clear_memory(ctx, sizeof(*ctx)); +} + +static inline void aesecb_encrypt(ptls_cipher_context_t *_ctx, void *output, const void *input, size_t len) +{ + struct aesecb_context_t *ctx = (struct aesecb_context_t *)_ctx; + assert(len % AES_BLOCKSZ == 0); + cf_aes_encrypt(&ctx->aes, input, output); +} + +static inline void aesecb_decrypt(ptls_cipher_context_t *_ctx, void *output, const void *input, size_t len) +{ + struct aesecb_context_t *ctx = (struct aesecb_context_t *)_ctx; + assert(len % AES_BLOCKSZ == 0); + cf_aes_decrypt(&ctx->aes, input, output); +} + +static inline int aesecb_setup_crypto(ptls_cipher_context_t *_ctx, int is_enc, const void *key) +{ + struct aesecb_context_t *ctx = (struct aesecb_context_t *)_ctx; + ctx->super.do_dispose = aesecb_dispose; + ctx->super.do_init = NULL; + ctx->super.do_transform = is_enc ? aesecb_encrypt : aesecb_decrypt; + cf_aes_init(&ctx->aes, key, ctx->super.algo->key_size); + return 0; +} + +struct aesctr_context_t { + ptls_cipher_context_t super; + cf_aes_context aes; + cf_ctr ctr; +}; + +static inline void aesctr_dispose(ptls_cipher_context_t *_ctx) +{ + struct aesctr_context_t *ctx = (struct aesctr_context_t *)_ctx; + ptls_clear_memory(ctx, sizeof(*ctx)); +} + +static inline void aesctr_init(ptls_cipher_context_t *_ctx, const void *iv) +{ + struct aesctr_context_t *ctx = (struct aesctr_context_t *)_ctx; + cf_ctr_init(&ctx->ctr, &cf_aes, &ctx->aes, iv); +} + +static inline void aesctr_transform(ptls_cipher_context_t *_ctx, void *output, const void *input, size_t len) +{ + struct aesctr_context_t *ctx = (struct aesctr_context_t *)_ctx; + cf_ctr_cipher(&ctx->ctr, input, output, len); +} + +static inline int aesctr_setup_crypto(ptls_cipher_context_t *_ctx, int is_enc, const void *key) +{ + struct aesctr_context_t *ctx = (struct aesctr_context_t *)_ctx; + ctx->super.do_dispose = aesctr_dispose; + ctx->super.do_init = aesctr_init; + ctx->super.do_transform = aesctr_transform; + cf_aes_init(&ctx->aes, key, ctx->super.algo->key_size); + return 0; +} + +struct aesgcm_context_t { + ptls_aead_context_t super; + cf_aes_context aes; + cf_gcm_ctx gcm; + uint8_t static_iv[PTLS_AESGCM_IV_SIZE]; +}; + +static inline void aesgcm_dispose_crypto(ptls_aead_context_t *_ctx) +{ + struct aesgcm_context_t *ctx = (struct aesgcm_context_t *)_ctx; + + /* clear all memory except super */ + ptls_clear_memory((uint8_t *)ctx + sizeof(ctx->super), sizeof(*ctx) - sizeof(ctx->super)); +} + +static inline void aesgcm_encrypt_init(ptls_aead_context_t *_ctx, uint64_t seq, const void *aad, size_t aadlen) +{ + struct aesgcm_context_t *ctx = (struct aesgcm_context_t *)_ctx; + uint8_t iv[PTLS_AES_BLOCK_SIZE]; + + ptls_aead__build_iv(ctx->super.algo, iv, ctx->static_iv, seq); + cf_gcm_encrypt_init(&cf_aes, &ctx->aes, &ctx->gcm, aad, aadlen, iv, PTLS_AESGCM_IV_SIZE); +} + +static inline size_t aesgcm_encrypt_update(ptls_aead_context_t *_ctx, void *output, const void *input, size_t inlen) +{ + struct aesgcm_context_t *ctx = (struct aesgcm_context_t *)_ctx; + + cf_gcm_encrypt_update(&ctx->gcm, input, inlen, output); + return inlen; +} + +static inline size_t aesgcm_encrypt_final(ptls_aead_context_t *_ctx, void *output) +{ + struct aesgcm_context_t *ctx = (struct aesgcm_context_t *)_ctx; + + cf_gcm_encrypt_final(&ctx->gcm, output, PTLS_AESGCM_TAG_SIZE); + return PTLS_AESGCM_TAG_SIZE; +} + +static inline size_t aesgcm_decrypt(ptls_aead_context_t *_ctx, void *output, const void *input, size_t inlen, uint64_t seq, + const void *aad, size_t aadlen) +{ + struct aesgcm_context_t *ctx = (struct aesgcm_context_t *)_ctx; + uint8_t iv[PTLS_AES_BLOCK_SIZE]; + + if (inlen < PTLS_AESGCM_TAG_SIZE) + return SIZE_MAX; + size_t tag_offset = inlen - PTLS_AESGCM_TAG_SIZE; + + ptls_aead__build_iv(ctx->super.algo, iv, ctx->static_iv, seq); + if (cf_gcm_decrypt(&cf_aes, &ctx->aes, input, tag_offset, aad, aadlen, iv, PTLS_AESGCM_IV_SIZE, (uint8_t *)input + tag_offset, + PTLS_AESGCM_TAG_SIZE, output) != 0) + return SIZE_MAX; + + return tag_offset; +} + +static inline int aead_aesgcm_setup_crypto(ptls_aead_context_t *_ctx, int is_enc, const void *key, const void *iv) +{ + struct aesgcm_context_t *ctx = (struct aesgcm_context_t *)_ctx; + + ctx->super.dispose_crypto = aesgcm_dispose_crypto; + if (is_enc) { + ctx->super.do_encrypt_init = aesgcm_encrypt_init; + ctx->super.do_encrypt_update = aesgcm_encrypt_update; + ctx->super.do_encrypt_final = aesgcm_encrypt_final; + ctx->super.do_encrypt = ptls_aead__do_encrypt; + ctx->super.do_decrypt = NULL; + } else { + ctx->super.do_encrypt_init = NULL; + ctx->super.do_encrypt_update = NULL; + ctx->super.do_encrypt_final = NULL; + ctx->super.do_decrypt = aesgcm_decrypt; + } + + cf_aes_init(&ctx->aes, key, ctx->super.algo->key_size); + memcpy(ctx->static_iv, iv, sizeof(ctx->static_iv)); + return 0; +} diff --git a/deps/picoquic/picotls/lib/cifra/aes128.c b/deps/picoquic/picotls/lib/cifra/aes128.c new file mode 100644 index 0000000..26bd9e4 --- /dev/null +++ b/deps/picoquic/picotls/lib/cifra/aes128.c @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2016 DeNA Co., Ltd., Kazuho Oku + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +#include "aes-common.h" + +static int aes128ecb_setup_crypto(ptls_cipher_context_t *ctx, int is_enc, const void *key) +{ + return aesecb_setup_crypto(ctx, is_enc, key); +} + +static int aes128ctr_setup_crypto(ptls_cipher_context_t *ctx, int is_enc, const void *key) +{ + return aesctr_setup_crypto(ctx, is_enc, key); +} + +static int aead_aes128gcm_setup_crypto(ptls_aead_context_t *ctx, int is_enc, const void *key, const void *iv) +{ + return aead_aesgcm_setup_crypto(ctx, is_enc, key, iv); +} + +ptls_define_hash(sha256, cf_sha256_context, cf_sha256_init, cf_sha256_update, cf_sha256_digest_final); + +ptls_hash_algorithm_t ptls_minicrypto_sha256 = {PTLS_SHA256_BLOCK_SIZE, PTLS_SHA256_DIGEST_SIZE, sha256_create, + PTLS_ZERO_DIGEST_SHA256}; + +ptls_cipher_algorithm_t ptls_minicrypto_aes128ecb = { + "AES128-ECB", PTLS_AES128_KEY_SIZE, PTLS_AES_BLOCK_SIZE, 0 /* iv size */, sizeof(struct aesecb_context_t), + aes128ecb_setup_crypto}; +ptls_cipher_algorithm_t ptls_minicrypto_aes128ctr = { + "AES128-CTR", PTLS_AES128_KEY_SIZE, 1 /* block size */, PTLS_AES_IV_SIZE, sizeof(struct aesctr_context_t), + aes128ctr_setup_crypto}; +ptls_aead_algorithm_t ptls_minicrypto_aes128gcm = {"AES128-GCM", + PTLS_AESGCM_CONFIDENTIALITY_LIMIT, + PTLS_AESGCM_INTEGRITY_LIMIT, + &ptls_minicrypto_aes128ctr, + &ptls_minicrypto_aes128ecb, + PTLS_AES128_KEY_SIZE, + PTLS_AESGCM_IV_SIZE, + PTLS_AESGCM_TAG_SIZE, + sizeof(struct aesgcm_context_t), + aead_aes128gcm_setup_crypto}; +ptls_cipher_suite_t ptls_minicrypto_aes128gcmsha256 = {PTLS_CIPHER_SUITE_AES_128_GCM_SHA256, &ptls_minicrypto_aes128gcm, + &ptls_minicrypto_sha256}; diff --git a/deps/picoquic/picotls/lib/cifra/aes256.c b/deps/picoquic/picotls/lib/cifra/aes256.c new file mode 100644 index 0000000..d0568d5 --- /dev/null +++ b/deps/picoquic/picotls/lib/cifra/aes256.c @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2016 DeNA Co., Ltd., Kazuho Oku + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +#include "aes-common.h" + +static int aes256ecb_setup_crypto(ptls_cipher_context_t *ctx, int is_enc, const void *key) +{ + return aesecb_setup_crypto(ctx, is_enc, key); +} + +static int aes256ctr_setup_crypto(ptls_cipher_context_t *ctx, int is_enc, const void *key) +{ + return aesctr_setup_crypto(ctx, is_enc, key); +} + +static int aead_aes256gcm_setup_crypto(ptls_aead_context_t *ctx, int is_enc, const void *key, const void *iv) +{ + return aead_aesgcm_setup_crypto(ctx, is_enc, key, iv); +} + +ptls_define_hash(sha384, cf_sha512_context, cf_sha384_init, cf_sha384_update, cf_sha384_digest_final); + +ptls_hash_algorithm_t ptls_minicrypto_sha384 = {PTLS_SHA384_BLOCK_SIZE, PTLS_SHA384_DIGEST_SIZE, sha384_create, + PTLS_ZERO_DIGEST_SHA384}; + +ptls_cipher_algorithm_t ptls_minicrypto_aes256ecb = { + "AES256-ECB", PTLS_AES256_KEY_SIZE, PTLS_AES_BLOCK_SIZE, 0 /* iv size */, sizeof(struct aesecb_context_t), + aes256ecb_setup_crypto}; +ptls_cipher_algorithm_t ptls_minicrypto_aes256ctr = { + "AES256-CTR", PTLS_AES256_KEY_SIZE, 1 /* block size */, PTLS_AES_IV_SIZE, sizeof(struct aesctr_context_t), + aes256ctr_setup_crypto}; +ptls_aead_algorithm_t ptls_minicrypto_aes256gcm = {"AES256-GCM", + PTLS_AESGCM_CONFIDENTIALITY_LIMIT, + PTLS_AESGCM_INTEGRITY_LIMIT, + &ptls_minicrypto_aes256ctr, + &ptls_minicrypto_aes256ecb, + PTLS_AES256_KEY_SIZE, + PTLS_AESGCM_IV_SIZE, + PTLS_AESGCM_TAG_SIZE, + sizeof(struct aesgcm_context_t), + aead_aes256gcm_setup_crypto}; +ptls_cipher_suite_t ptls_minicrypto_aes256gcmsha384 = {PTLS_CIPHER_SUITE_AES_256_GCM_SHA384, &ptls_minicrypto_aes256gcm, + &ptls_minicrypto_sha384}; diff --git a/deps/picoquic/picotls/lib/cifra/chacha20.c b/deps/picoquic/picotls/lib/cifra/chacha20.c new file mode 100644 index 0000000..57a6c39 --- /dev/null +++ b/deps/picoquic/picotls/lib/cifra/chacha20.c @@ -0,0 +1,219 @@ +/* + * Copyright (c) 2016 DeNA Co., Ltd., Kazuho Oku + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +#include +#include "bitops.h" +#include "../deps/cifra/src/ext/handy.h" +#include "poly1305.h" +#include "salsa20.h" +#include "sha2.h" +#include "picotls.h" +#include "picotls/minicrypto.h" + +struct chacha20_context_t { + ptls_cipher_context_t super; + cf_chacha20_ctx chacha; + uint8_t key[PTLS_CHACHA20_KEY_SIZE]; +}; + +static void chacha20_dispose(ptls_cipher_context_t *_ctx) +{ + struct chacha20_context_t *ctx = (struct chacha20_context_t *)_ctx; + ptls_clear_memory(ctx, sizeof(*ctx)); +} + +static void chacha20_init(ptls_cipher_context_t *_ctx, const void *iv) +{ + struct chacha20_context_t *ctx = (struct chacha20_context_t *)_ctx; + ctx->chacha.nblock = 0; + ctx->chacha.ncounter = 0; + memcpy(ctx->chacha.nonce, iv, sizeof ctx->chacha.nonce); +} + +static void chacha20_transform(ptls_cipher_context_t *_ctx, void *output, const void *input, size_t len) +{ + struct chacha20_context_t *ctx = (struct chacha20_context_t *)_ctx; + cf_chacha20_cipher(&ctx->chacha, input, output, len); +} + +static int chacha20_setup_crypto(ptls_cipher_context_t *_ctx, int is_enc, const void *key) +{ + struct chacha20_context_t *ctx = (struct chacha20_context_t *)_ctx; + ctx->super.do_dispose = chacha20_dispose; + ctx->super.do_init = chacha20_init; + ctx->super.do_transform = chacha20_transform; + cf_chacha20_init(&ctx->chacha, key, PTLS_CHACHA20_KEY_SIZE, (const uint8_t *)"01234567" /* not used */); + return 0; +} + +struct chacha20poly1305_context_t { + ptls_aead_context_t super; + uint8_t key[PTLS_CHACHA20_KEY_SIZE]; + uint8_t static_iv[PTLS_CHACHA20POLY1305_IV_SIZE]; + cf_chacha20_ctx chacha; + cf_poly1305 poly; + size_t aadlen; + size_t textlen; +}; + +static void chacha20poly1305_dispose_crypto(ptls_aead_context_t *_ctx) +{ + struct chacha20poly1305_context_t *ctx = (struct chacha20poly1305_context_t *)_ctx; + + /* clear all memory except super */ + ptls_clear_memory(&ctx->key, sizeof(*ctx) - offsetof(struct chacha20poly1305_context_t, key)); +} + +static const uint8_t zeros64[64] = {0}; + +static void chacha20poly1305_encrypt_pad(cf_poly1305 *poly, size_t n) +{ + if (n % 16 != 0) + cf_poly1305_update(poly, zeros64, 16 - (n % 16)); +} + +static void chacha20poly1305_finalize(struct chacha20poly1305_context_t *ctx, uint8_t *tag) +{ + uint8_t lenbuf[16]; + + chacha20poly1305_encrypt_pad(&ctx->poly, ctx->textlen); + + write64_le(ctx->aadlen, lenbuf); + write64_le(ctx->textlen, lenbuf + 8); + cf_poly1305_update(&ctx->poly, lenbuf, sizeof(lenbuf)); + + cf_poly1305_finish(&ctx->poly, tag); +} + +static void chacha20poly1305_init(ptls_aead_context_t *_ctx, uint64_t seq, const void *aad, size_t aadlen) +{ + struct chacha20poly1305_context_t *ctx = (struct chacha20poly1305_context_t *)_ctx; + uint8_t tmpbuf[64]; + + /* init chacha */ + memset(tmpbuf, 0, 16 - PTLS_CHACHA20POLY1305_IV_SIZE); + ptls_aead__build_iv(ctx->super.algo, tmpbuf + 16 - PTLS_CHACHA20POLY1305_IV_SIZE, ctx->static_iv, seq); + cf_chacha20_init_custom(&ctx->chacha, ctx->key, sizeof(ctx->key), tmpbuf, 4); + + /* init poly1305 (by using first 16 bytes of the key stream of the first block) */ + cf_chacha20_cipher(&ctx->chacha, zeros64, tmpbuf, 64); + cf_poly1305_init(&ctx->poly, tmpbuf, tmpbuf + 16); + + ptls_clear_memory(tmpbuf, sizeof(tmpbuf)); + + /* aad */ + if (aadlen != 0) { + cf_poly1305_update(&ctx->poly, aad, aadlen); + chacha20poly1305_encrypt_pad(&ctx->poly, aadlen); + } + + ctx->aadlen = aadlen; + ctx->textlen = 0; +} + +static size_t chacha20poly1305_encrypt_update(ptls_aead_context_t *_ctx, void *output, const void *input, size_t inlen) +{ + struct chacha20poly1305_context_t *ctx = (struct chacha20poly1305_context_t *)_ctx; + + cf_chacha20_cipher(&ctx->chacha, input, output, inlen); + cf_poly1305_update(&ctx->poly, output, inlen); + ctx->textlen += inlen; + + return inlen; +} + +static size_t chacha20poly1305_encrypt_final(ptls_aead_context_t *_ctx, void *output) +{ + struct chacha20poly1305_context_t *ctx = (struct chacha20poly1305_context_t *)_ctx; + + chacha20poly1305_finalize(ctx, output); + + ptls_clear_memory(&ctx->chacha, sizeof(ctx->chacha)); + return PTLS_CHACHA20POLY1305_TAG_SIZE; +} + +static size_t chacha20poly1305_decrypt(ptls_aead_context_t *_ctx, void *output, const void *input, size_t inlen, uint64_t seq, + const void *aad, size_t aadlen) +{ + struct chacha20poly1305_context_t *ctx = (struct chacha20poly1305_context_t *)_ctx; + uint8_t tag[PTLS_CHACHA20POLY1305_TAG_SIZE]; + size_t ret; + + if (inlen < sizeof(tag)) + return SIZE_MAX; + + chacha20poly1305_init(&ctx->super, seq, aad, aadlen); + + cf_poly1305_update(&ctx->poly, input, inlen - sizeof(tag)); + ctx->textlen = inlen - sizeof(tag); + + chacha20poly1305_finalize(ctx, tag); + if (mem_eq(tag, (const uint8_t *)input + inlen - sizeof(tag), sizeof(tag))) { + cf_chacha20_cipher(&ctx->chacha, input, output, inlen - sizeof(tag)); + ret = inlen - sizeof(tag); + } else { + ret = SIZE_MAX; + } + + ptls_clear_memory(tag, sizeof(tag)); + ptls_clear_memory(&ctx->poly, sizeof(ctx->poly)); + + return ret; +} + +static int aead_chacha20poly1305_setup_crypto(ptls_aead_context_t *_ctx, int is_enc, const void *key, const void *iv) +{ + struct chacha20poly1305_context_t *ctx = (struct chacha20poly1305_context_t *)_ctx; + + ctx->super.dispose_crypto = chacha20poly1305_dispose_crypto; + if (is_enc) { + ctx->super.do_encrypt_init = chacha20poly1305_init; + ctx->super.do_encrypt_update = chacha20poly1305_encrypt_update; + ctx->super.do_encrypt_final = chacha20poly1305_encrypt_final; + ctx->super.do_encrypt = ptls_aead__do_encrypt; + ctx->super.do_decrypt = NULL; + } else { + ctx->super.do_encrypt_init = NULL; + ctx->super.do_encrypt_update = NULL; + ctx->super.do_encrypt_final = NULL; + ctx->super.do_decrypt = chacha20poly1305_decrypt; + } + + memcpy(ctx->key, key, sizeof(ctx->key)); + memcpy(ctx->static_iv, iv, sizeof(ctx->static_iv)); + return 0; +} + +ptls_cipher_algorithm_t ptls_minicrypto_chacha20 = { + "CHACHA20", PTLS_CHACHA20_KEY_SIZE, 1 /* block size */, PTLS_CHACHA20_IV_SIZE, sizeof(struct chacha20_context_t), + chacha20_setup_crypto}; +ptls_aead_algorithm_t ptls_minicrypto_chacha20poly1305 = {"CHACHA20-POLY1305", + PTLS_CHACHA20POLY1305_CONFIDENTIALITY_LIMIT, + PTLS_CHACHA20POLY1305_INTEGRITY_LIMIT, + &ptls_minicrypto_chacha20, + NULL, + PTLS_CHACHA20_KEY_SIZE, + PTLS_CHACHA20POLY1305_IV_SIZE, + PTLS_CHACHA20POLY1305_TAG_SIZE, + sizeof(struct chacha20poly1305_context_t), + aead_chacha20poly1305_setup_crypto}; +ptls_cipher_suite_t ptls_minicrypto_chacha20poly1305sha256 = {PTLS_CIPHER_SUITE_CHACHA20_POLY1305_SHA256, + &ptls_minicrypto_chacha20poly1305, &ptls_minicrypto_sha256}; diff --git a/deps/picoquic/picotls/lib/cifra/random.c b/deps/picoquic/picotls/lib/cifra/random.c new file mode 100644 index 0000000..9d26634 --- /dev/null +++ b/deps/picoquic/picotls/lib/cifra/random.c @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2016-2019 DeNA Co., Ltd., Kazuho Oku, Christian Huitema + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +#ifndef _XOPEN_SOURCE +#define _XOPEN_SOURCE 700 /* required for glibc to use getaddrinfo, etc. */ +#endif +#include +#include +#include +#include +#ifdef _WINDOWS +#include "wincompat.h" +#else +#include +#endif +#include "drbg.h" +#include "picotls.h" +#include "picotls/minicrypto.h" +#include +#ifdef _WINDOWS +#ifdef _WINDOWS_XP + /* The modern BCrypt API is only available on Windows Vista and later versions. + * If compiling on Windows XP, we need to use the olded "wincrypt" API */ +#include + +static void read_entropy(uint8_t *entropy, size_t size) +{ + HCRYPTPROV hCryptProv = 0; + BOOL ret = FALSE; + + if (CryptAcquireContext(&hCryptProv, NULL, NULL, PROV_RSA_FULL, 0)) { + ret = CryptGenRandom(hCryptProv, (DWORD)size, entropy); + (void)CryptReleaseContext(hCryptProv, 0); + } + + if (ret == FALSE) { + perror("ptls_minicrypto_random_bytes: could not use CryptGenRandom"); + abort(); + } +} +#else + /* The old "Wincrypt" API requires access to default security containers. + * This can cause access control errors on some systems. We prefer + * to use the modern BCrypt API when available */ +#include + + static void read_entropy(uint8_t *entropy, size_t size) + { + NTSTATUS nts = 0; + BCRYPT_ALG_HANDLE hAlgorithm = 0; + + nts = BCryptOpenAlgorithmProvider(&hAlgorithm, BCRYPT_RNG_ALGORITHM, NULL, 0); + + if (BCRYPT_SUCCESS(nts)) { + nts = BCryptGenRandom(hAlgorithm, (PUCHAR)entropy, (ULONG)size, 0); + + (void)BCryptCloseAlgorithmProvider(hAlgorithm, 0); + } + + if (!BCRYPT_SUCCESS(nts)) { + perror("ptls_minicrypto_random_bytes: could not open BCrypt RNG Algorithm"); + abort(); + } +} +#endif +#else +static void read_entropy(uint8_t *entropy, size_t size) +{ + int fd; + + if ((fd = open("/dev/urandom", O_RDONLY | O_CLOEXEC)) == -1) { + if ((fd = open("/dev/random", O_RDONLY | O_CLOEXEC)) == -1) { + perror("ptls_minicrypto_random_bytes: could not open neither /dev/random or /dev/urandom"); + abort(); + } + } + + while (size != 0) { + ssize_t rret; + while ((rret = read(fd, entropy, size)) == -1 && errno == EINTR) + ; + if (rret < 0) { + perror("ptls_minicrypto_random_bytes"); + abort(); + } + entropy += rret; + size -= rret; + } + + close(fd); +} +#endif + +void ptls_minicrypto_random_bytes(void *buf, size_t len) +{ + static PTLS_THREADLOCAL cf_hash_drbg_sha256 ctx; + + if (cf_hash_drbg_sha256_needs_reseed(&ctx)) { + uint8_t entropy[256]; + read_entropy(entropy, sizeof(entropy)); + cf_hash_drbg_sha256_init(&ctx, entropy, sizeof(entropy) / 2, entropy + sizeof(entropy) / 2, sizeof(entropy) / 2, "ptls", 4); + } + cf_hash_drbg_sha256_gen(&ctx, buf, len); +} diff --git a/deps/picoquic/picotls/lib/cifra/x25519.c b/deps/picoquic/picotls/lib/cifra/x25519.c new file mode 100644 index 0000000..3d28487 --- /dev/null +++ b/deps/picoquic/picotls/lib/cifra/x25519.c @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2016 DeNA Co., Ltd., Kazuho Oku + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +#include +#include "curve25519.h" +#include "picotls.h" +#include "picotls/minicrypto.h" + +#define X25519_KEY_SIZE 32 + +struct st_x25519_key_exchange_t { + ptls_key_exchange_context_t super; + uint8_t priv[X25519_KEY_SIZE]; + uint8_t pub[X25519_KEY_SIZE]; +}; + +static void x25519_create_keypair(uint8_t *priv, uint8_t *pub) +{ + ptls_minicrypto_random_bytes(priv, X25519_KEY_SIZE); + cf_curve25519_mul_base(pub, priv); +} + +static int x25519_derive_secret(ptls_iovec_t *secret, const uint8_t *clientpriv, const uint8_t *clientpub, + const uint8_t *serverpriv, const uint8_t *serverpub) +{ + if ((secret->base = malloc(X25519_KEY_SIZE)) == NULL) + return PTLS_ERROR_NO_MEMORY; + + cf_curve25519_mul(secret->base, clientpriv != NULL ? clientpriv : serverpriv, clientpriv != NULL ? serverpub : clientpub); + secret->len = X25519_KEY_SIZE; + return 0; +} + +static int x25519_on_exchange(ptls_key_exchange_context_t **_ctx, int release, ptls_iovec_t *secret, ptls_iovec_t peerkey) +{ + struct st_x25519_key_exchange_t *ctx = (struct st_x25519_key_exchange_t *)*_ctx; + int ret; + + if (secret == NULL) { + ret = 0; + goto Exit; + } + + if (peerkey.len != X25519_KEY_SIZE) { + ret = PTLS_ALERT_DECRYPT_ERROR; + goto Exit; + } + ret = x25519_derive_secret(secret, ctx->priv, ctx->pub, NULL, peerkey.base); + +Exit: + if (release) { + ptls_clear_memory(ctx->priv, sizeof(ctx->priv)); + free(ctx); + *_ctx = NULL; + } + return ret; +} + +static int x25519_create_key_exchange(ptls_key_exchange_algorithm_t *algo, ptls_key_exchange_context_t **_ctx) +{ + struct st_x25519_key_exchange_t *ctx; + + if ((ctx = (struct st_x25519_key_exchange_t *)malloc(sizeof(*ctx))) == NULL) + return PTLS_ERROR_NO_MEMORY; + ctx->super = (ptls_key_exchange_context_t){algo, ptls_iovec_init(ctx->pub, sizeof(ctx->pub)), x25519_on_exchange}; + x25519_create_keypair(ctx->priv, ctx->pub); + + *_ctx = &ctx->super; + return 0; +} + +static int x25519_key_exchange(ptls_key_exchange_algorithm_t *algo, ptls_iovec_t *pubkey, ptls_iovec_t *secret, + ptls_iovec_t peerkey) +{ + uint8_t priv[X25519_KEY_SIZE], *pub = NULL; + int ret; + + if (peerkey.len != X25519_KEY_SIZE) { + ret = PTLS_ALERT_DECRYPT_ERROR; + goto Exit; + } + if ((pub = malloc(X25519_KEY_SIZE)) == NULL) { + ret = PTLS_ERROR_NO_MEMORY; + goto Exit; + } + + x25519_create_keypair(priv, pub); + if ((ret = x25519_derive_secret(secret, NULL, peerkey.base, priv, pub)) != 0) + goto Exit; + + *pubkey = ptls_iovec_init(pub, X25519_KEY_SIZE); + ret = 0; + +Exit: + ptls_clear_memory(priv, sizeof(priv)); + if (pub != NULL && ret != 0) + ptls_clear_memory(pub, X25519_KEY_SIZE); + return ret; +} + +ptls_key_exchange_algorithm_t ptls_minicrypto_x25519 = {PTLS_GROUP_X25519, x25519_create_key_exchange, x25519_key_exchange}; diff --git a/deps/picoquic/picotls/lib/ffx.c b/deps/picoquic/picotls/lib/ffx.c new file mode 100644 index 0000000..26b08c2 --- /dev/null +++ b/deps/picoquic/picotls/lib/ffx.c @@ -0,0 +1,207 @@ +/* + * Copyright (c) 2016 Christian Huitema + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#ifdef _WINDOWS +#include "wincompat.h" +#else +#include +#endif +#include +#include +#include +#include "picotls.h" +#include "picotls/minicrypto.h" +#include "picotls/ffx.h" + +static void ffx_dispose(ptls_cipher_context_t *_ctx); +static void ffx_encrypt(ptls_cipher_context_t *_ctx, void *output, const void *input, size_t len); +static void ffx_init(struct st_ptls_cipher_context_t *ctx, const void *iv); + +int ptls_ffx_setup_crypto(ptls_cipher_context_t *_ctx, ptls_cipher_algorithm_t *algo, int is_enc, int nb_rounds, size_t bit_length, + const void *key) +{ + int ret = 0; + ptls_ffx_context_t *ctx = (ptls_ffx_context_t *)_ctx; + ptls_cipher_context_t *enc_ctx = NULL; + size_t len = (bit_length + 7) / 8; + uint8_t last_byte_mask[8] = {0xFF, 0xFE, 0xFC, 0xF8, 0xF0, 0xE0, 0xC0, 0x80}; + + assert(len <= 32 && len >= 2); + assert(ctx->super.do_dispose == NULL); + assert(ctx->super.do_init == NULL); + assert(ctx->super.do_transform == NULL); + assert(ctx->super.algo == NULL || algo->key_size == ctx->super.algo->key_size); + assert(ctx->super.algo == NULL || algo->iv_size == ctx->super.algo->iv_size); + assert(ctx->super.algo == NULL || ctx->super.algo->block_size == len); + assert(algo->iv_size == 16); + + if (len <= 32 && len >= 2) { + /* len must be lower than 32 */ + enc_ctx = ptls_cipher_new(algo, 1, key); + + if (enc_ctx == NULL) { + ret = PTLS_ERROR_LIBRARY; + } + } else { + ret = PTLS_ERROR_LIBRARY; + } + + if (ret == 0) { + ctx->enc_ctx = enc_ctx; + ctx->nb_rounds = nb_rounds; + ctx->is_enc = is_enc; + ctx->byte_length = len; + ctx->nb_left = (int)len / 2; + ctx->nb_right = (int)len - ctx->nb_left; + ctx->mask_last_byte = last_byte_mask[bit_length % 8]; + ptls_clear_memory(ctx->tweaks, sizeof(ctx->tweaks)); + + ctx->super.do_dispose = ffx_dispose; + ctx->super.do_init = ffx_init; + ctx->super.do_transform = ffx_encrypt; + } else { + ffx_dispose(_ctx); + } + + return ret; +} + +static void ffx_dispose(ptls_cipher_context_t *_ctx) +{ + ptls_ffx_context_t *ctx = (ptls_ffx_context_t *)_ctx; + + assert(ctx->super.do_dispose == ffx_dispose); + + if (ctx->enc_ctx != NULL) { + ptls_cipher_free(ctx->enc_ctx); + } + + ctx->enc_ctx = NULL; + ctx->nb_rounds = 0; + ctx->byte_length = 0; + ctx->nb_left = 0; + ctx->nb_right = 0; + ctx->mask_last_byte = 0; + ctx->is_enc = 0; + + ctx->super.do_dispose = NULL; + ctx->super.do_init = NULL; + ctx->super.do_transform = NULL; +} + +ptls_cipher_context_t *ptls_ffx_new(ptls_cipher_algorithm_t *algo, int is_enc, int nb_rounds, size_t bit_length, const void *key) +{ + ptls_cipher_context_t *ctx = (ptls_cipher_context_t *)malloc(sizeof(ptls_ffx_context_t)); + + if (ctx != NULL) { + memset(ctx, 0, sizeof(ptls_ffx_context_t)); + if (ptls_ffx_setup_crypto(ctx, algo, is_enc, nb_rounds, bit_length, key) != 0) { + free(ctx); + ctx = NULL; + } + } + + return ctx; +} + +static void ptls_ffx_one_pass(ptls_cipher_context_t *enc_ctx, uint8_t *source, size_t source_size, uint8_t *target, + size_t target_size, uint8_t mask_last_byte, uint8_t *confusion, uint8_t *iv, uint8_t *tweaks, + uint8_t round, uint8_t nb_rounds) +{ + static const uint8_t zeros[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + + memcpy(iv, tweaks, 16); + iv[round & 15] ^= nb_rounds; + for (size_t i = 0; i < source_size; i++) { + iv[i] ^= source[i]; + } + ptls_cipher_init(enc_ctx, iv); + ptls_cipher_encrypt(enc_ctx, confusion, zeros, 16); + for (size_t j = 0; j < target_size - 1; j++) { + target[j] ^= confusion[j]; + } + target[target_size - 1] ^= (confusion[target_size - 1] & mask_last_byte); +} + +static void ffx_encrypt(ptls_cipher_context_t *_ctx, void *output, const void *input, size_t len) +{ + ptls_ffx_context_t *ctx = (ptls_ffx_context_t *)_ctx; + uint8_t left[16], right[16], confusion[32], iv[16]; + uint8_t last_byte; + + assert(ctx->super.do_transform == ffx_encrypt); + + /* len must match context definition */ + assert(len == ctx->byte_length); + if (len != ctx->byte_length) { + memset(output, 0, len); /* so that we do not leak anything in production mode */ + return; + } + + /* Split the input in two halves */ + memcpy(left, input, ctx->nb_left); + memcpy(right, ((uint8_t *)input) + ctx->nb_left, ctx->nb_right); + memset(left + ctx->nb_left, 0, 16 - ctx->nb_left); + memset(right + ctx->nb_right, 0, 16 - ctx->nb_right); + last_byte = right[ctx->nb_right - 1]; + right[ctx->nb_right - 1] &= ctx->mask_last_byte; + + if (ctx->is_enc) { + /* Feistel construct, using the specified algorithm as S-Box */ + for (int i = 0; i < ctx->nb_rounds; i += 2) { + /* Each pass encrypts a zero field with a cipher using one + * half of the message as IV. This construct lets us use + * either AES or chacha 20 */ + ptls_ffx_one_pass(ctx->enc_ctx, right, ctx->nb_right, left, ctx->nb_left, 0xFF, confusion, iv, ctx->tweaks, i, + ctx->nb_rounds); + ptls_ffx_one_pass(ctx->enc_ctx, left, ctx->nb_left, right, ctx->nb_right, ctx->mask_last_byte, confusion, iv, + ctx->tweaks, i + 1, ctx->nb_rounds); + } + } else { + /* Feistel construct, using the specified algorithm as S-Box, + * in the opposite order of the encryption */ + + for (int i = 0; i < ctx->nb_rounds; i += 2) { + /* Each pass encrypts a zero field with a cipher using one + * half of the message as IV. This construct lets us use + * either AES or chacha 20 */ + + ptls_ffx_one_pass(ctx->enc_ctx, left, ctx->nb_left, right, ctx->nb_right, ctx->mask_last_byte, confusion, iv, + ctx->tweaks, ctx->nb_rounds - 1 - i, ctx->nb_rounds); + ptls_ffx_one_pass(ctx->enc_ctx, right, ctx->nb_right, left, ctx->nb_left, 0xFF, confusion, iv, ctx->tweaks, + ctx->nb_rounds - 2 - i, ctx->nb_rounds); + } + } + /* After enough passes, we have a very strong length preserving + * encryption, only that many times slower than the underlying + * algorithm. We copy the result to the output */ + memcpy(output, left, ctx->nb_left); + + right[ctx->nb_right - 1] &= ctx->mask_last_byte; + right[ctx->nb_right - 1] |= (last_byte & ~ctx->mask_last_byte); + + memcpy(((uint8_t *)output) + ctx->nb_left, right, ctx->nb_right); + + ptls_clear_memory(left, sizeof(left)); + ptls_clear_memory(right, sizeof(right)); + ptls_clear_memory(confusion, sizeof(confusion)); +} + +static void ffx_init(struct st_ptls_cipher_context_t *_ctx, const void *iv) +{ + ptls_ffx_context_t *ctx = (ptls_ffx_context_t *)_ctx; + assert(ctx->super.do_init == ffx_init); + memcpy(ctx->tweaks, iv, 16); +} diff --git a/deps/picoquic/picotls/lib/fusion.c b/deps/picoquic/picotls/lib/fusion.c new file mode 100644 index 0000000..1610447 --- /dev/null +++ b/deps/picoquic/picotls/lib/fusion.c @@ -0,0 +1,1050 @@ +/* + * This source file is licensed under the Apache License 2.0 *and* the MIT + * License. Please agree to *both* of the licensing terms! + * + * + * `transformH` function is a derivative work of OpenSSL. The original work + * is covered by the following license: + * + * Copyright 2013-2020 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + * + * + * All other work, including modifications to the `transformH` function is + * covered by the following MIT license: + * + * Copyright (c) 2020 Fastly, Kazuho Oku + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +#include + +#include +#include +#include +#include +#include +#include +#include "picotls.h" +#include "picotls/fusion.h" + +struct ptls_fusion_aesgcm_context { + ptls_fusion_aesecb_context_t ecb; + size_t capacity; + size_t ghash_cnt; + struct ptls_fusion_aesgcm_ghash_precompute { + __m128i H; + __m128i r; + } ghash[0]; +}; + +struct ctr_context { + ptls_cipher_context_t super; + ptls_fusion_aesecb_context_t fusion; + __m128i bits; + uint8_t is_ready; +}; + +struct aesgcm_context { + ptls_aead_context_t super; + ptls_fusion_aesgcm_context_t *aesgcm; + /** + * retains the static IV in the upper 96 bits (in little endian) + */ + __m128i static_iv; +}; + +static const uint64_t poly_[2] __attribute__((aligned(16))) = {1, 0xc200000000000000}; +#define poly (*(__m128i *)poly_) +static const uint8_t bswap8_[16] __attribute__((aligned(16))) = {15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0}; +#define bswap8 (*(__m128i *)bswap8_) +static const uint8_t one8_[16] __attribute__((aligned(16))) = {1}; +#define one8 (*(__m128i *)one8_) + +/* This function is covered by the Apache License and the MIT License. The origin is crypto/modes/asm/ghash-x86_64.pl of openssl + * at commit 33388b4. */ +static __m128i transformH(__m128i H) +{ + // # <<1 twist + // pshufd \$0b11111111,$Hkey,$T2 # broadcast uppermost dword + __m128i t2 = _mm_shuffle_epi32(H, 0xff); + // movdqa $Hkey,$T1 + __m128i t1 = H; + // psllq \$1,$Hkey + H = _mm_slli_epi64(H, 1); + // pxor $T3,$T3 # + __m128i t3 = _mm_setzero_si128(); + // psrlq \$63,$T1 + t1 = _mm_srli_epi64(t1, 63); + // pcmpgtd $T2,$T3 # broadcast carry bit + t3 = _mm_cmplt_epi32(t2, t3); + // pslldq \$8,$T1 + t1 = _mm_slli_si128(t1, 8); + // por $T1,$Hkey # H<<=1 + H = _mm_or_si128(t1, H); + + // # magic reduction + // pand .L0x1c2_polynomial(%rip),$T3 + t3 = _mm_and_si128(t3, poly); + // pxor $T3,$Hkey # if(carry) H^=0x1c2_polynomial + H = _mm_xor_si128(t3, H); + + return H; +} +// end of Apache License code + +static __m128i gfmul(__m128i x, __m128i y) +{ + __m128i lo = _mm_clmulepi64_si128(x, y, 0x00); + __m128i hi = _mm_clmulepi64_si128(x, y, 0x11); + + __m128i a = _mm_shuffle_epi32(x, 78); + __m128i b = _mm_shuffle_epi32(y, 78); + a = _mm_xor_si128(a, x); + b = _mm_xor_si128(b, y); + + a = _mm_clmulepi64_si128(a, b, 0x00); + a = _mm_xor_si128(a, lo); + a = _mm_xor_si128(a, hi); + + b = _mm_slli_si128(a, 8); + a = _mm_srli_si128(a, 8); + + lo = _mm_xor_si128(lo, b); + hi = _mm_xor_si128(hi, a); + + // from https://crypto.stanford.edu/RealWorldCrypto/slides/gueron.pdf + __m128i t = _mm_clmulepi64_si128(lo, poly, 0x10); + lo = _mm_shuffle_epi32(lo, 78); + lo = _mm_xor_si128(lo, t); + t = _mm_clmulepi64_si128(lo, poly, 0x10); + lo = _mm_shuffle_epi32(lo, 78); + lo = _mm_xor_si128(lo, t); + + return _mm_xor_si128(hi, lo); +} + +struct ptls_fusion_gfmul_state { + __m128i hi, lo, mid; +}; + +static inline void gfmul_onestep(struct ptls_fusion_gfmul_state *gstate, __m128i X, + struct ptls_fusion_aesgcm_ghash_precompute *precompute) +{ + X = _mm_shuffle_epi8(X, bswap8); + __m128i t = _mm_clmulepi64_si128(precompute->H, X, 0x00); + gstate->lo = _mm_xor_si128(gstate->lo, t); + t = _mm_clmulepi64_si128(precompute->H, X, 0x11); + gstate->hi = _mm_xor_si128(gstate->hi, t); + t = _mm_shuffle_epi32(X, 78); + t = _mm_xor_si128(t, X); + t = _mm_clmulepi64_si128(precompute->r, t, 0x00); + gstate->mid = _mm_xor_si128(gstate->mid, t); +} + +static inline __m128i gfmul_final(struct ptls_fusion_gfmul_state *gstate, __m128i ek0) +{ + /* finish multiplication */ + gstate->mid = _mm_xor_si128(gstate->mid, gstate->hi); + gstate->mid = _mm_xor_si128(gstate->mid, gstate->lo); + gstate->lo = _mm_xor_si128(gstate->lo, _mm_slli_si128(gstate->mid, 8)); + gstate->hi = _mm_xor_si128(gstate->hi, _mm_srli_si128(gstate->mid, 8)); + + /* fast reduction, using https://crypto.stanford.edu/RealWorldCrypto/slides/gueron.pdf */ + __m128i r = _mm_clmulepi64_si128(gstate->lo, poly, 0x10); + gstate->lo = _mm_shuffle_epi32(gstate->lo, 78); + gstate->lo = _mm_xor_si128(gstate->lo, r); + r = _mm_clmulepi64_si128(gstate->lo, poly, 0x10); + gstate->lo = _mm_shuffle_epi32(gstate->lo, 78); + gstate->lo = _mm_xor_si128(gstate->lo, r); + __m128i tag = _mm_xor_si128(gstate->hi, gstate->lo); + tag = _mm_shuffle_epi8(tag, bswap8); + tag = _mm_xor_si128(tag, ek0); + + return tag; +} + +static inline __m128i aesecb_encrypt(ptls_fusion_aesecb_context_t *ctx, __m128i v) +{ + size_t i; + + v = _mm_xor_si128(v, ctx->keys[0]); + for (i = 1; i < ctx->rounds; ++i) + v = _mm_aesenc_si128(v, ctx->keys[i]); + v = _mm_aesenclast_si128(v, ctx->keys[i]); + + return v; +} + +static const uint8_t loadn_mask[31] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; +static const uint8_t loadn_shuffle[31] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, // first 16 bytes map to byte offsets + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80}; // latter 15 bytes map to zero + +static inline __m128i loadn(const void *p, size_t l) +{ + __m128i v, mask = _mm_loadu_si128((__m128i *)(loadn_mask + 16 - l)); + uintptr_t mod4k = (uintptr_t)p % 4096; + + if (PTLS_LIKELY(mod4k <= 4080) || mod4k + l > 4096) { + v = _mm_loadu_si128(p); + } else { + uintptr_t shift = (uintptr_t)p & 15; + __m128i pattern = _mm_loadu_si128((const __m128i *)(loadn_shuffle + shift)); + v = _mm_shuffle_epi8(_mm_load_si128((const __m128i *)((uintptr_t)p - shift)), pattern); + } + v = _mm_and_si128(v, mask); + return v; +} + +static inline void storen(void *_p, size_t l, __m128i v) +{ + uint8_t buf[16], *p = _p; + + *(__m128i *)buf = v; + + for (size_t i = 0; i != l; ++i) + p[i] = buf[i]; +} + +void ptls_fusion_aesgcm_encrypt(ptls_fusion_aesgcm_context_t *ctx, void *output, const void *input, size_t inlen, __m128i ctr, + const void *_aad, size_t aadlen, ptls_aead_supplementary_encryption_t *supp) +{ +/* init the bits (we can always run in full), but use the last slot for calculating ek0, if possible */ +#define AESECB6_INIT() \ + do { \ + ctr = _mm_add_epi64(ctr, one8); \ + bits0 = _mm_shuffle_epi8(ctr, bswap8); \ + ctr = _mm_add_epi64(ctr, one8); \ + bits1 = _mm_shuffle_epi8(ctr, bswap8); \ + ctr = _mm_add_epi64(ctr, one8); \ + bits2 = _mm_shuffle_epi8(ctr, bswap8); \ + ctr = _mm_add_epi64(ctr, one8); \ + bits3 = _mm_shuffle_epi8(ctr, bswap8); \ + ctr = _mm_add_epi64(ctr, one8); \ + bits4 = _mm_shuffle_epi8(ctr, bswap8); \ + if (PTLS_LIKELY(srclen > 16 * 5)) { \ + ctr = _mm_add_epi64(ctr, one8); \ + bits5 = _mm_shuffle_epi8(ctr, bswap8); \ + } else { \ + if ((state & STATE_EK0_BEEN_FED) == 0) { \ + bits5 = ek0; \ + state |= STATE_EK0_BEEN_FED; \ + } \ + if ((state & STATE_SUPP_USED) != 0 && srclen <= 16 * 4 && (const __m128i *)supp->input + 1 <= dst_ghash) { \ + bits4 = _mm_loadu_si128(supp->input); \ + bits4keys = ((struct ctr_context *)supp->ctx)->fusion.keys; \ + state |= STATE_SUPP_IN_PROCESS; \ + } \ + } \ + __m128i k = ctx->ecb.keys[0]; \ + bits0 = _mm_xor_si128(bits0, k); \ + bits1 = _mm_xor_si128(bits1, k); \ + bits2 = _mm_xor_si128(bits2, k); \ + bits3 = _mm_xor_si128(bits3, k); \ + bits4 = _mm_xor_si128(bits4, bits4keys[0]); \ + bits5 = _mm_xor_si128(bits5, k); \ + } while (0) + +/* aes block update */ +#define AESECB6_UPDATE(i) \ + do { \ + __m128i k = ctx->ecb.keys[i]; \ + bits0 = _mm_aesenc_si128(bits0, k); \ + bits1 = _mm_aesenc_si128(bits1, k); \ + bits2 = _mm_aesenc_si128(bits2, k); \ + bits3 = _mm_aesenc_si128(bits3, k); \ + bits4 = _mm_aesenc_si128(bits4, bits4keys[i]); \ + bits5 = _mm_aesenc_si128(bits5, k); \ + } while (0) + +/* aesenclast */ +#define AESECB6_FINAL(i) \ + do { \ + __m128i k = ctx->ecb.keys[i]; \ + bits0 = _mm_aesenclast_si128(bits0, k); \ + bits1 = _mm_aesenclast_si128(bits1, k); \ + bits2 = _mm_aesenclast_si128(bits2, k); \ + bits3 = _mm_aesenclast_si128(bits3, k); \ + bits4 = _mm_aesenclast_si128(bits4, bits4keys[i]); \ + bits5 = _mm_aesenclast_si128(bits5, k); \ + } while (0) + + __m128i ek0, bits0, bits1, bits2, bits3, bits4, bits5 = _mm_setzero_si128(); + const __m128i *bits4keys = ctx->ecb.keys; /* is changed to supp->ctx.keys when calcurating suppout */ + struct ptls_fusion_gfmul_state gstate = {0}; + __m128i gdatabuf[6]; + __m128i ac = _mm_shuffle_epi8(_mm_set_epi32(0, (int)aadlen * 8, 0, (int)inlen * 8), bswap8); + + // src and dst are updated after the chunk is processed + const __m128i *src = input; + __m128i *dst = output; + size_t srclen = inlen; + // aad and src_ghash are updated before the chunk is processed (i.e., when the pointers are fed indo the processor) + const __m128i *aad = _aad, *dst_ghash = dst; + size_t dst_ghashlen = srclen; + + struct ptls_fusion_aesgcm_ghash_precompute *ghash_precompute = ctx->ghash + (aadlen + 15) / 16 + (srclen + 15) / 16 + 1; + +#define STATE_EK0_BEEN_FED 0x3 +#define STATE_EK0_INCOMPLETE 0x2 +#define STATE_EK0_READY() ((state & STATE_EK0_BEEN_FED) == 0x1) +#define STATE_SUPP_USED 0x4 +#define STATE_SUPP_IN_PROCESS 0x8 + int32_t state = supp != NULL ? STATE_SUPP_USED : 0; + + /* build counter */ + ctr = _mm_insert_epi32(ctr, 1, 0); + ek0 = _mm_shuffle_epi8(ctr, bswap8); + + /* start preparing AES */ + AESECB6_INIT(); + AESECB6_UPDATE(1); + + /* build first ghash data (only AAD can be fed at this point, as this would be calculated alongside the first AES block) */ + const __m128i *gdata = gdatabuf; // points to the elements fed into GHASH + size_t gdata_cnt = 0; + if (PTLS_LIKELY(aadlen != 0)) { + while (gdata_cnt < 6) { + if (PTLS_LIKELY(aadlen < 16)) { + if (aadlen != 0) { + gdatabuf[gdata_cnt++] = loadn(aad, aadlen); + aadlen = 0; + } + goto MainLoop; + } + gdatabuf[gdata_cnt++] = _mm_loadu_si128(aad++); + aadlen -= 16; + } + } + + /* the main loop */ +MainLoop: + while (1) { + /* run AES and multiplication in parallel */ + size_t i; + for (i = 2; i < gdata_cnt + 2; ++i) { + AESECB6_UPDATE(i); + gfmul_onestep(&gstate, _mm_loadu_si128(gdata++), --ghash_precompute); + } + for (; i < ctx->ecb.rounds; ++i) + AESECB6_UPDATE(i); + AESECB6_FINAL(i); + + /* apply the bit stream to src and write to dest */ + if (PTLS_LIKELY(srclen >= 6 * 16)) { +#define APPLY(i) _mm_storeu_si128(dst + i, _mm_xor_si128(_mm_loadu_si128(src + i), bits##i)) + APPLY(0); + APPLY(1); + APPLY(2); + APPLY(3); + APPLY(4); + APPLY(5); +#undef APPLY + dst += 6; + src += 6; + srclen -= 6 * 16; + } else { + if ((state & STATE_EK0_BEEN_FED) == STATE_EK0_BEEN_FED) { + ek0 = bits5; + state &= ~STATE_EK0_INCOMPLETE; + } + if ((state & STATE_SUPP_IN_PROCESS) != 0) { + _mm_storeu_si128((__m128i *)supp->output, bits4); + state &= ~(STATE_SUPP_USED | STATE_SUPP_IN_PROCESS); + } + if (srclen != 0) { +#define APPLY(i) \ + do { \ + if (PTLS_LIKELY(srclen >= 16)) { \ + _mm_storeu_si128(dst++, _mm_xor_si128(_mm_loadu_si128(src++), bits##i)); \ + srclen -= 16; \ + } else if (PTLS_LIKELY(srclen != 0)) { \ + bits0 = bits##i; \ + goto ApplyRemainder; \ + } else { \ + goto ApplyEnd; \ + } \ + } while (0) + APPLY(0); + APPLY(1); + APPLY(2); + APPLY(3); + APPLY(4); + APPLY(5); +#undef APPLY + goto ApplyEnd; + ApplyRemainder: + storen(dst, srclen, _mm_xor_si128(loadn(src, srclen), bits0)); + dst = (__m128i *)((uint8_t *)dst + srclen); + srclen = 0; + ApplyEnd:; + } + } + + /* next block AES starts here */ + AESECB6_INIT(); + + AESECB6_UPDATE(1); + + /* setup gdata */ + if (PTLS_UNLIKELY(aadlen != 0)) { + gdata_cnt = 0; + while (gdata_cnt < 6) { + if (aadlen < 16) { + if (aadlen != 0) { + gdatabuf[gdata_cnt++] = loadn(aad, aadlen); + aadlen = 0; + } + goto GdataFillDST; + } + gdatabuf[gdata_cnt++] = _mm_loadu_si128(aad++); + aadlen -= 16; + } + gdata = gdatabuf; + } else if (PTLS_LIKELY(dst_ghashlen >= 6 * 16)) { + gdata = dst_ghash; + gdata_cnt = 6; + dst_ghash += 6; + dst_ghashlen -= 96; + } else { + gdata_cnt = 0; + GdataFillDST: + while (gdata_cnt < 6) { + if (dst_ghashlen < 16) { + if (dst_ghashlen != 0) { + gdatabuf[gdata_cnt++] = loadn(dst_ghash, dst_ghashlen); + dst_ghashlen = 0; + } + if (gdata_cnt < 6) + goto Finish; + break; + } + gdatabuf[gdata_cnt++] = _mm_loadu_si128(dst_ghash++); + dst_ghashlen -= 16; + } + gdata = gdatabuf; + } + } + +Finish: + gdatabuf[gdata_cnt++] = ac; + + /* We have complete set of data to be fed into GHASH. Let's finish the remaining calculation. + * Note that by now, all AES operations for payload encryption and ek0 are complete. This is is because it is necessary for GCM + * to process at least the same amount of data (i.e. payload-blocks + AC), and because AES is at least one 96-byte block ahead. + */ + assert(STATE_EK0_READY()); + for (size_t i = 0; i < gdata_cnt; ++i) + gfmul_onestep(&gstate, gdatabuf[i], --ghash_precompute); + + _mm_storeu_si128(dst, gfmul_final(&gstate, ek0)); + + /* Finish the calculation of supplemental vector. Done at the very last, because the sample might cover the GCM tag. */ + if ((state & STATE_SUPP_USED) != 0) { + size_t i; + if ((state & STATE_SUPP_IN_PROCESS) == 0) { + bits4keys = ((struct ctr_context *)supp->ctx)->fusion.keys; + bits4 = _mm_xor_si128(_mm_loadu_si128(supp->input), bits4keys[0]); + i = 1; + } else { + i = 2; + } + do { + bits4 = _mm_aesenc_si128(bits4, bits4keys[i++]); + } while (i != ctx->ecb.rounds); + bits4 = _mm_aesenclast_si128(bits4, bits4keys[i]); + _mm_storeu_si128((__m128i *)supp->output, bits4); + } + +#undef AESECB6_INIT +#undef AESECB6_UPDATE +#undef AESECB6_FINAL +#undef STATE_EK0_BEEN_FOUND +#undef STATE_EK0_READY +#undef STATE_SUPP_IN_PROCESS +} + +int ptls_fusion_aesgcm_decrypt(ptls_fusion_aesgcm_context_t *ctx, void *output, const void *input, size_t inlen, __m128i ctr, + const void *_aad, size_t aadlen, const void *tag) +{ + __m128i ek0 = _mm_setzero_si128(), bits0, bits1 = _mm_setzero_si128(), bits2 = _mm_setzero_si128(), bits3 = _mm_setzero_si128(), + bits4 = _mm_setzero_si128(), bits5 = _mm_setzero_si128(); + struct ptls_fusion_gfmul_state gstate = {0}; + __m128i gdatabuf[6]; + __m128i ac = _mm_shuffle_epi8(_mm_set_epi32(0, (int)aadlen * 8, 0, (int)inlen * 8), bswap8); + struct ptls_fusion_aesgcm_ghash_precompute *ghash_precompute = ctx->ghash + (aadlen + 15) / 16 + (inlen + 15) / 16 + 1; + + const __m128i *gdata; // points to the elements fed into GHASH + size_t gdata_cnt; + + const __m128i *src_ghash = input, *src_aes = input, *aad = _aad; + __m128i *dst = output; + size_t nondata_aes_cnt = 0, src_ghashlen = inlen, src_aeslen = inlen; + + /* schedule ek0 and suppkey */ + ctr = _mm_add_epi64(ctr, one8); + bits0 = _mm_xor_si128(_mm_shuffle_epi8(ctr, bswap8), ctx->ecb.keys[0]); + ++nondata_aes_cnt; + +#define STATE_IS_FIRST_RUN 0x1 +#define STATE_GHASH_HAS_MORE 0x2 + int state = STATE_IS_FIRST_RUN | STATE_GHASH_HAS_MORE; + + /* the main loop */ + while (1) { + + /* setup gdata */ + if (PTLS_UNLIKELY(aadlen != 0)) { + gdata = gdatabuf; + gdata_cnt = 0; + while (gdata_cnt < 6) { + if (aadlen < 16) { + if (aadlen != 0) { + gdatabuf[gdata_cnt++] = loadn(aad, aadlen); + aadlen = 0; + ++nondata_aes_cnt; + } + goto GdataFillSrc; + } + gdatabuf[gdata_cnt++] = _mm_loadu_si128(aad++); + aadlen -= 16; + ++nondata_aes_cnt; + } + } else if (PTLS_LIKELY(src_ghashlen >= 6 * 16)) { + gdata = src_ghash; + gdata_cnt = 6; + src_ghash += 6; + src_ghashlen -= 6 * 16; + } else { + gdata = gdatabuf; + gdata_cnt = 0; + GdataFillSrc: + while (gdata_cnt < 6) { + if (src_ghashlen < 16) { + if (src_ghashlen != 0) { + gdatabuf[gdata_cnt++] = loadn(src_ghash, src_ghashlen); + src_ghash = (__m128i *)((uint8_t *)src_ghash + src_ghashlen); + src_ghashlen = 0; + } + if (gdata_cnt < 6 && (state & STATE_GHASH_HAS_MORE) != 0) { + gdatabuf[gdata_cnt++] = ac; + state &= ~STATE_GHASH_HAS_MORE; + } + break; + } + gdatabuf[gdata_cnt++] = _mm_loadu_si128(src_ghash++); + src_ghashlen -= 16; + } + } + + /* setup aes bits */ + if (PTLS_LIKELY(nondata_aes_cnt == 0)) + goto InitAllBits; + switch (nondata_aes_cnt) { +#define INIT_BITS(n, keys) \ + case n: \ + ctr = _mm_add_epi64(ctr, one8); \ + bits##n = _mm_xor_si128(_mm_shuffle_epi8(ctr, bswap8), keys[0]); + InitAllBits: + INIT_BITS(0, ctx->ecb.keys); + INIT_BITS(1, ctx->ecb.keys); + INIT_BITS(2, ctx->ecb.keys); + INIT_BITS(3, ctx->ecb.keys); + INIT_BITS(4, ctx->ecb.keys); + INIT_BITS(5, ctx->ecb.keys); +#undef INIT_BITS + } + + { /* run aes and ghash */ +#define AESECB6_UPDATE(i) \ + do { \ + __m128i k = ctx->ecb.keys[i]; \ + bits0 = _mm_aesenc_si128(bits0, k); \ + bits1 = _mm_aesenc_si128(bits1, k); \ + bits2 = _mm_aesenc_si128(bits2, k); \ + bits3 = _mm_aesenc_si128(bits3, k); \ + bits4 = _mm_aesenc_si128(bits4, k); \ + bits5 = _mm_aesenc_si128(bits5, k); \ + } while (0) + + size_t aesi; + for (aesi = 1; aesi <= gdata_cnt; ++aesi) { + AESECB6_UPDATE(aesi); + gfmul_onestep(&gstate, _mm_loadu_si128(gdata++), --ghash_precompute); + } + for (; aesi < ctx->ecb.rounds; ++aesi) + AESECB6_UPDATE(aesi); + __m128i k = ctx->ecb.keys[aesi]; + bits0 = _mm_aesenclast_si128(bits0, k); + bits1 = _mm_aesenclast_si128(bits1, k); + bits2 = _mm_aesenclast_si128(bits2, k); + bits3 = _mm_aesenclast_si128(bits3, k); + bits4 = _mm_aesenclast_si128(bits4, k); + bits5 = _mm_aesenclast_si128(bits5, k); + +#undef AESECB6_UPDATE + } + + /* apply aes bits */ + if (PTLS_LIKELY(nondata_aes_cnt == 0 && src_aeslen >= 6 * 16)) { +#define APPLY(i) _mm_storeu_si128(dst + i, _mm_xor_si128(_mm_loadu_si128(src_aes + i), bits##i)) + APPLY(0); + APPLY(1); + APPLY(2); + APPLY(3); + APPLY(4); + APPLY(5); +#undef APPLY + dst += 6; + src_aes += 6; + src_aeslen -= 6 * 16; + } else { + if ((state & STATE_IS_FIRST_RUN) != 0) { + ek0 = bits0; + state &= ~STATE_IS_FIRST_RUN; + } + switch (nondata_aes_cnt) { +#define APPLY(i) \ + case i: \ + if (PTLS_LIKELY(src_aeslen > 16)) { \ + _mm_storeu_si128(dst++, _mm_xor_si128(_mm_loadu_si128(src_aes++), bits##i)); \ + src_aeslen -= 16; \ + } else { \ + bits0 = bits##i; \ + goto Finish; \ + } + APPLY(0); + APPLY(1); + APPLY(2); + APPLY(3); + APPLY(4); + APPLY(5); +#undef APPLY + } + nondata_aes_cnt = 0; + } + } + +Finish: + if (src_aeslen == 16) { + _mm_storeu_si128(dst, _mm_xor_si128(_mm_loadu_si128(src_aes), bits0)); + } else if (src_aeslen != 0) { + storen(dst, src_aeslen, _mm_xor_si128(loadn(src_aes, src_aeslen), bits0)); + } + + assert((state & STATE_IS_FIRST_RUN) == 0); + + /* the only case where AES operation is complete and GHASH is not is when the application of AC is remaining */ + if ((state & STATE_GHASH_HAS_MORE) != 0) { + assert(ghash_precompute - 1 == ctx->ghash); + gfmul_onestep(&gstate, ac, --ghash_precompute); + } + + __m128i calctag = gfmul_final(&gstate, ek0); + + return _mm_movemask_epi8(_mm_cmpeq_epi8(calctag, _mm_loadu_si128(tag))) == 0xffff; + +#undef STATE_IS_FIRST_RUN +#undef STATE_GHASH_HAS_MORE +} + +static __m128i expand_key(__m128i key, __m128i temp) +{ + key = _mm_xor_si128(key, _mm_slli_si128(key, 4)); + key = _mm_xor_si128(key, _mm_slli_si128(key, 4)); + key = _mm_xor_si128(key, _mm_slli_si128(key, 4)); + + key = _mm_xor_si128(key, temp); + + return key; +} + +void ptls_fusion_aesecb_init(ptls_fusion_aesecb_context_t *ctx, int is_enc, const void *key, size_t key_size) +{ + assert(is_enc && "decryption is not supported (yet)"); + + size_t i = 0; + + switch (key_size) { + case 16: /* AES128 */ + ctx->rounds = 10; + break; + case 32: /* AES256 */ + ctx->rounds = 14; + break; + default: + assert(!"invalid key size; AES128 / AES256 are supported"); + break; + } + + ctx->keys[i++] = _mm_loadu_si128((__m128i *)key); + if (key_size == 32) + ctx->keys[i++] = _mm_loadu_si128((__m128i *)key + 1); + +#define EXPAND(R) \ + do { \ + ctx->keys[i] = expand_key(ctx->keys[i - key_size / 16], \ + _mm_shuffle_epi32(_mm_aeskeygenassist_si128(ctx->keys[i - 1], R), _MM_SHUFFLE(3, 3, 3, 3))); \ + if (i == ctx->rounds) \ + goto Done; \ + ++i; \ + if (key_size > 24) { \ + ctx->keys[i] = expand_key(ctx->keys[i - key_size / 16], \ + _mm_shuffle_epi32(_mm_aeskeygenassist_si128(ctx->keys[i - 1], R), _MM_SHUFFLE(2, 2, 2, 2))); \ + ++i; \ + } \ + } while (0) + EXPAND(0x1); + EXPAND(0x2); + EXPAND(0x4); + EXPAND(0x8); + EXPAND(0x10); + EXPAND(0x20); + EXPAND(0x40); + EXPAND(0x80); + EXPAND(0x1b); + EXPAND(0x36); +#undef EXPAND +Done: + assert(i == ctx->rounds); +} + +void ptls_fusion_aesecb_dispose(ptls_fusion_aesecb_context_t *ctx) +{ + ptls_clear_memory(ctx, sizeof(*ctx)); +} + +void ptls_fusion_aesecb_encrypt(ptls_fusion_aesecb_context_t *ctx, void *dst, const void *src) +{ + __m128i v = _mm_loadu_si128(src); + v = aesecb_encrypt(ctx, v); + _mm_storeu_si128(dst, v); +} + +/** + * returns the number of ghash entries that is required to handle an AEAD block of given size + */ +static size_t aesgcm_calc_ghash_cnt(size_t capacity) +{ + // round-up by block size, add to handle worst split of the size between AAD and payload, plus context to hash AC + return (capacity + 15) / 16 + 2; +} + +static void setup_one_ghash_entry(ptls_fusion_aesgcm_context_t *ctx) +{ + if (ctx->ghash_cnt != 0) + ctx->ghash[ctx->ghash_cnt].H = gfmul(ctx->ghash[ctx->ghash_cnt - 1].H, ctx->ghash[0].H); + + __m128i r = _mm_shuffle_epi32(ctx->ghash[ctx->ghash_cnt].H, 78); + r = _mm_xor_si128(r, ctx->ghash[ctx->ghash_cnt].H); + ctx->ghash[ctx->ghash_cnt].r = r; + + ++ctx->ghash_cnt; +} + +ptls_fusion_aesgcm_context_t *ptls_fusion_aesgcm_new(const void *key, size_t key_size, size_t capacity) +{ + ptls_fusion_aesgcm_context_t *ctx; + size_t ghash_cnt = aesgcm_calc_ghash_cnt(capacity); + + if ((ctx = malloc(sizeof(*ctx) + sizeof(ctx->ghash[0]) * ghash_cnt)) == NULL) + return NULL; + + ptls_fusion_aesecb_init(&ctx->ecb, 1, key, key_size); + + ctx->capacity = capacity; + + ctx->ghash[0].H = aesecb_encrypt(&ctx->ecb, _mm_setzero_si128()); + ctx->ghash[0].H = _mm_shuffle_epi8(ctx->ghash[0].H, bswap8); + ctx->ghash[0].H = transformH(ctx->ghash[0].H); + ctx->ghash_cnt = 0; + while (ctx->ghash_cnt < ghash_cnt) + setup_one_ghash_entry(ctx); + + return ctx; +} + +ptls_fusion_aesgcm_context_t *ptls_fusion_aesgcm_set_capacity(ptls_fusion_aesgcm_context_t *ctx, size_t capacity) +{ + size_t ghash_cnt = aesgcm_calc_ghash_cnt(capacity); + + if (ghash_cnt <= ctx->ghash_cnt) + return ctx; + + if ((ctx = realloc(ctx, sizeof(*ctx) + sizeof(ctx->ghash[0]) * ghash_cnt)) == NULL) + return NULL; + + ctx->capacity = capacity; + while (ghash_cnt < ctx->ghash_cnt) + setup_one_ghash_entry(ctx); + + return ctx; +} + +void ptls_fusion_aesgcm_free(ptls_fusion_aesgcm_context_t *ctx) +{ + ptls_clear_memory(ctx->ghash, sizeof(ctx->ghash[0]) * ctx->ghash_cnt); + ctx->ghash_cnt = 0; + ptls_fusion_aesecb_dispose(&ctx->ecb); + free(ctx); +} + +static void ctr_dispose(ptls_cipher_context_t *_ctx) +{ + struct ctr_context *ctx = (struct ctr_context *)_ctx; + ptls_fusion_aesecb_dispose(&ctx->fusion); + _mm_storeu_si128(&ctx->bits, _mm_setzero_si128()); +} + +static void ctr_init(ptls_cipher_context_t *_ctx, const void *iv) +{ + struct ctr_context *ctx = (struct ctr_context *)_ctx; + _mm_storeu_si128(&ctx->bits, aesecb_encrypt(&ctx->fusion, _mm_loadu_si128(iv))); + ctx->is_ready = 1; +} + +static void ctr_transform(ptls_cipher_context_t *_ctx, void *output, const void *input, size_t len) +{ + struct ctr_context *ctx = (struct ctr_context *)_ctx; + + assert((ctx->is_ready && len <= 16) || + !"CTR transfomation is supported only once per call to `init` and the maximum size is limited to 16 bytes"); + ctx->is_ready = 0; + + if (len < 16) { + storen(output, len, _mm_xor_si128(_mm_loadu_si128(&ctx->bits), loadn(input, len))); + } else { + _mm_storeu_si128(output, _mm_xor_si128(_mm_loadu_si128(&ctx->bits), _mm_loadu_si128(input))); + } +} + +static int aesctr_setup(ptls_cipher_context_t *_ctx, int is_enc, const void *key, size_t key_size) +{ + struct ctr_context *ctx = (struct ctr_context *)_ctx; + + ctx->super.do_dispose = ctr_dispose; + ctx->super.do_init = ctr_init; + ctx->super.do_transform = ctr_transform; + ptls_fusion_aesecb_init(&ctx->fusion, 1, key, key_size); + ctx->is_ready = 0; + + return 0; +} + +static int aes128ctr_setup(ptls_cipher_context_t *ctx, int is_enc, const void *key) +{ + return aesctr_setup(ctx, is_enc, key, PTLS_AES128_KEY_SIZE); +} + +static int aes256ctr_setup(ptls_cipher_context_t *ctx, int is_enc, const void *key) +{ + return aesctr_setup(ctx, is_enc, key, PTLS_AES256_KEY_SIZE); +} + +static void aesgcm_dispose_crypto(ptls_aead_context_t *_ctx) +{ + struct aesgcm_context *ctx = (struct aesgcm_context *)_ctx; + + ptls_fusion_aesgcm_free(ctx->aesgcm); +} + +static void aead_do_encrypt_init(ptls_aead_context_t *_ctx, uint64_t seq, const void *aad, size_t aadlen) +{ + assert(!"FIXME"); +} + +static size_t aead_do_encrypt_update(ptls_aead_context_t *_ctx, void *output, const void *input, size_t inlen) +{ + assert(!"FIXME"); + return SIZE_MAX; +} + +static size_t aead_do_encrypt_final(ptls_aead_context_t *_ctx, void *_output) +{ + assert(!"FIXME"); + return SIZE_MAX; +} + +static inline __m128i calc_counter(struct aesgcm_context *ctx, uint64_t seq) +{ + __m128i ctr = _mm_setzero_si128(); + ctr = _mm_insert_epi64(ctr, seq, 0); + ctr = _mm_slli_si128(ctr, 4); + ctr = _mm_xor_si128(ctx->static_iv, ctr); + return ctr; +} + +void aead_do_encrypt(struct st_ptls_aead_context_t *_ctx, void *output, const void *input, size_t inlen, uint64_t seq, + const void *aad, size_t aadlen, ptls_aead_supplementary_encryption_t *supp) +{ + struct aesgcm_context *ctx = (void *)_ctx; + + if (inlen + aadlen > ctx->aesgcm->capacity) + ctx->aesgcm = ptls_fusion_aesgcm_set_capacity(ctx->aesgcm, inlen + aadlen); + ptls_fusion_aesgcm_encrypt(ctx->aesgcm, output, input, inlen, calc_counter(ctx, seq), aad, aadlen, supp); +} + +static size_t aead_do_decrypt(ptls_aead_context_t *_ctx, void *output, const void *input, size_t inlen, uint64_t seq, + const void *aad, size_t aadlen) +{ + struct aesgcm_context *ctx = (void *)_ctx; + + if (inlen < 16) + return SIZE_MAX; + + size_t enclen = inlen - 16; + if (enclen + aadlen > ctx->aesgcm->capacity) + ctx->aesgcm = ptls_fusion_aesgcm_set_capacity(ctx->aesgcm, enclen + aadlen); + if (!ptls_fusion_aesgcm_decrypt(ctx->aesgcm, output, input, enclen, calc_counter(ctx, seq), aad, aadlen, + (const uint8_t *)input + enclen)) + return SIZE_MAX; + return enclen; +} + +static int aesgcm_setup(ptls_aead_context_t *_ctx, int is_enc, const void *key, const void *iv, size_t key_size) +{ + struct aesgcm_context *ctx = (struct aesgcm_context *)_ctx; + + ctx->static_iv = loadn(iv, PTLS_AESGCM_IV_SIZE); + ctx->static_iv = _mm_shuffle_epi8(ctx->static_iv, bswap8); + if (key == NULL) + return 0; + + ctx->super.dispose_crypto = aesgcm_dispose_crypto; + ctx->super.do_encrypt_init = aead_do_encrypt_init; + ctx->super.do_encrypt_update = aead_do_encrypt_update; + ctx->super.do_encrypt_final = aead_do_encrypt_final; + ctx->super.do_encrypt = aead_do_encrypt; + ctx->super.do_decrypt = aead_do_decrypt; + + ctx->aesgcm = ptls_fusion_aesgcm_new(key, key_size, 1500 /* assume ordinary packet size */); + + return 0; +} + +static int aes128gcm_setup(ptls_aead_context_t *ctx, int is_enc, const void *key, const void *iv) +{ + return aesgcm_setup(ctx, is_enc, key, iv, PTLS_AES128_KEY_SIZE); +} + +static int aes256gcm_setup(ptls_aead_context_t *ctx, int is_enc, const void *key, const void *iv) +{ + return aesgcm_setup(ctx, is_enc, key, iv, PTLS_AES256_KEY_SIZE); +} + +ptls_cipher_algorithm_t ptls_fusion_aes128ctr = {"AES128-CTR", + PTLS_AES128_KEY_SIZE, + 1, // block size + PTLS_AES_IV_SIZE, + sizeof(struct ctr_context), + aes128ctr_setup}; +ptls_cipher_algorithm_t ptls_fusion_aes256ctr = {"AES256-CTR", + PTLS_AES256_KEY_SIZE, + 1, // block size + PTLS_AES_IV_SIZE, + sizeof(struct ctr_context), + aes256ctr_setup}; +ptls_aead_algorithm_t ptls_fusion_aes128gcm = {"AES128-GCM", + PTLS_AESGCM_CONFIDENTIALITY_LIMIT, + PTLS_AESGCM_INTEGRITY_LIMIT, + &ptls_fusion_aes128ctr, + NULL, // &ptls_fusion_aes128ecb, + PTLS_AES128_KEY_SIZE, + PTLS_AESGCM_IV_SIZE, + PTLS_AESGCM_TAG_SIZE, + sizeof(struct aesgcm_context), + aes128gcm_setup}; +ptls_aead_algorithm_t ptls_fusion_aes256gcm = {"AES256-GCM", + PTLS_AESGCM_CONFIDENTIALITY_LIMIT, + PTLS_AESGCM_INTEGRITY_LIMIT, + &ptls_fusion_aes256ctr, + NULL, // &ptls_fusion_aes256ecb, + PTLS_AES256_KEY_SIZE, + PTLS_AESGCM_IV_SIZE, + PTLS_AESGCM_TAG_SIZE, + sizeof(struct aesgcm_context), + aes256gcm_setup}; + +#ifdef _WINDOWS +/** + * ptls_fusion_is_supported_by_cpu: + * Check that the CPU has extended instructions for PCMUL, AES and AVX2. + * This test assumes that the CPU is following the x86/x64 architecture. + * A slightly more refined test could check that the cpu_info spells out + * "genuineIntel" or "authenticAMD", but would fail in presence of + * little known CPU brands or some VM */ +int ptls_fusion_is_supported_by_cpu(void) +{ + uint32_t cpu_info[4]; + uint32_t nb_ids; + int is_supported = 0; + + __cpuid(cpu_info, 0); + nb_ids = cpu_info[0]; + + if (nb_ids >= 7) { + uint32_t leaf1_ecx; + __cpuid(cpu_info, 1); + leaf1_ecx = cpu_info[2]; + + if (/* PCLMUL */ (leaf1_ecx & (1 << 5)) != 0 && /* AES */ (leaf1_ecx & (1 << 25)) != 0) { + uint32_t leaf7_ebx; + __cpuid(cpu_info, 7); + leaf7_ebx = cpu_info[1]; + + is_supported = /* AVX2 */ (leaf7_ebx & (1 << 5)) != 0; + } + } + + return is_supported; +} +#else +int ptls_fusion_is_supported_by_cpu(void) +{ + unsigned leaf1_ecx, leaf7_ebx; + + { /* GCC-specific code to obtain CPU features */ + unsigned leaf_cnt; + __asm__("cpuid" : "=a"(leaf_cnt) : "a"(0) : "ebx", "ecx", "edx"); + if (leaf_cnt < 7) + return 0; + __asm__("cpuid" : "=c"(leaf1_ecx) : "a"(1) : "ebx", "edx"); + __asm__("cpuid" : "=b"(leaf7_ebx) : "a"(7), "c"(0) : "edx"); + } + + /* AVX2 */ + if ((leaf7_ebx & (1 << 5)) == 0) + return 0; + /* AES */ + if ((leaf1_ecx & (1 << 25)) == 0) + return 0; + /* PCLMUL */ + if ((leaf1_ecx & (1 << 1)) == 0) + return 0; + + return 1; +} +#endif diff --git a/deps/picoquic/picotls/lib/minicrypto-pem.c b/deps/picoquic/picotls/lib/minicrypto-pem.c new file mode 100644 index 0000000..9d6c499 --- /dev/null +++ b/deps/picoquic/picotls/lib/minicrypto-pem.c @@ -0,0 +1,350 @@ +/* + * Copyright (c) 2017,2018 Christian Huitema + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +#include +#include +#include +#include +#ifdef _WINDOWS +#include "wincompat.h" +#else +#include +#endif +#include "picotls.h" +#include "picotls/minicrypto.h" +#include "picotls/asn1.h" +#include "picotls/pembase64.h" + +/* + * This function could be declared as static, but we want to access it + * in the unit tests. + */ +size_t ptls_minicrypto_asn1_decode_private_key(ptls_asn1_pkcs8_private_key_t *pkey, int *decode_error, + ptls_minicrypto_log_ctx_t *log_ctx) +{ + uint8_t *bytes = pkey->vec.base; + size_t bytes_max = pkey->vec.len; + + /* read the ASN1 messages */ + size_t byte_index = 0; + uint32_t seq0_length = 0; + size_t last_byte0; + uint32_t seq1_length = 0; + size_t last_byte1 = 0; + uint32_t oid_length; + size_t last_oid_byte; + uint32_t key_data_length; + size_t key_data_last; + + /* start with sequence */ + byte_index = ptls_asn1_get_expected_type_and_length(bytes, bytes_max, byte_index, 0x30, &seq0_length, NULL, &last_byte0, + decode_error, log_ctx); + + if (*decode_error == 0 && bytes_max != last_byte0) { + byte_index = ptls_asn1_error_message("Length larger than message", bytes_max, byte_index, 0, log_ctx); + *decode_error = PTLS_ERROR_BER_EXCESSIVE_LENGTH; + } + + if (*decode_error == 0) { + /* get first component: version, INTEGER, expect value 0 */ + if (byte_index + 3 > bytes_max) { + byte_index = ptls_asn1_error_message("Cannot find key version", bytes_max, byte_index, 0, log_ctx); + *decode_error = PTLS_ERROR_INCORRECT_PEM_KEY_VERSION; + } else if (bytes[byte_index] != 0x02 || bytes[byte_index + 1] != 0x01 || bytes[byte_index + 2] != 0x00) { + *decode_error = PTLS_ERROR_INCORRECT_PEM_KEY_VERSION; + byte_index = ptls_asn1_error_message("Incorrect PEM Version", bytes_max, byte_index, 0, log_ctx); + } else { + byte_index += 3; + if (log_ctx != NULL) { + log_ctx->fn(log_ctx->ctx, " Version = 1,\n"); + } + } + } + + if (*decode_error == 0) { + /* open embedded sequence */ + byte_index = ptls_asn1_get_expected_type_and_length(bytes, bytes_max, byte_index, 0x30, &seq1_length, NULL, &last_byte1, + decode_error, log_ctx); + } + + if (*decode_error == 0) { + if (log_ctx != NULL) { + log_ctx->fn(log_ctx->ctx, " Algorithm Identifier:\n"); + } + /* get length of OID */ + byte_index = ptls_asn1_get_expected_type_and_length(bytes, last_byte1, byte_index, 0x06, &oid_length, NULL, &last_oid_byte, + decode_error, log_ctx); + + if (*decode_error == 0) { + if (log_ctx != NULL) { + /* print the OID value */ + log_ctx->fn(log_ctx->ctx, " Algorithm:"); + ptls_asn1_dump_content(bytes + byte_index, oid_length, 0, log_ctx); + log_ctx->fn(log_ctx->ctx, ",\n"); + } + pkey->algorithm_index = byte_index; + pkey->algorithm_length = oid_length; + byte_index += oid_length; + } + } + + if (*decode_error == 0) { + /* get parameters, ANY */ + if (log_ctx != NULL) { + log_ctx->fn(log_ctx->ctx, " Parameters:\n"); + } + + if (last_byte1 <= byte_index) { + pkey->parameters_index = 0; + pkey->parameters_length = 0; + } else { + pkey->parameters_index = byte_index; + + pkey->parameters_length = + (uint32_t)ptls_asn1_validation_recursive(bytes + byte_index, last_byte1 - byte_index, decode_error, 2, log_ctx); + if (*decode_error == 0) { + byte_index += pkey->parameters_length; + } + } + if (log_ctx != NULL) { + log_ctx->fn(log_ctx->ctx, "\n"); + } + /* close sequence */ + if (*decode_error == 0 && byte_index != last_byte1) { + byte_index = ptls_asn1_error_message("Length larger than element", bytes_max, byte_index, 2, log_ctx); + *decode_error = PTLS_ERROR_BER_ELEMENT_TOO_SHORT; + } + } + + /* get octet string, key */ + if (*decode_error == 0) { + byte_index = ptls_asn1_get_expected_type_and_length(bytes, last_byte0, byte_index, 0x04, &key_data_length, NULL, + &key_data_last, decode_error, log_ctx); + + if (*decode_error == 0) { + pkey->key_data_index = byte_index; + pkey->key_data_length = key_data_length; + byte_index += key_data_length; + + if (log_ctx != NULL) { + log_ctx->fn(log_ctx->ctx, " Key data (%d bytes):\n", key_data_length); + + (void)ptls_asn1_validation_recursive(bytes + pkey->key_data_index, key_data_length, decode_error, 1, log_ctx); + log_ctx->fn(log_ctx->ctx, "\n"); + } + } + } + + if (*decode_error == 0 && byte_index != last_byte0) { + byte_index = ptls_asn1_error_message("Length larger than element", bytes_max, byte_index, 0, log_ctx); + *decode_error = PTLS_ERROR_BER_ELEMENT_TOO_SHORT; + } + + if (log_ctx != NULL) { + log_ctx->fn(log_ctx->ctx, "\n"); + } + + return byte_index; +} + +static int ptls_pem_parse_private_key(char const *pem_fname, ptls_asn1_pkcs8_private_key_t *pkey, + ptls_minicrypto_log_ctx_t *log_ctx) +{ + size_t nb_keys = 0; + int ret = ptls_load_pem_objects(pem_fname, "PRIVATE KEY", &pkey->vec, 1, &nb_keys); + + if (ret == 0) { + if (nb_keys != 1) { + ret = PTLS_ERROR_PEM_LABEL_NOT_FOUND; + } + } + + if (ret == 0 && nb_keys == 1) { + int decode_error = 0; + + if (log_ctx != NULL) { + log_ctx->fn(log_ctx->ctx, "\nFound PRIVATE KEY, length = %d bytes\n", (int)pkey->vec.len); + } + + (void)ptls_minicrypto_asn1_decode_private_key(pkey, &decode_error, log_ctx); + + if (decode_error != 0) { + ret = decode_error; + } + } + + return ret; +} + +static const uint8_t ptls_asn1_algorithm_ecdsa[] = {0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01}; + +static const uint8_t ptls_asn1_curve_secp256r1[] = {0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07}; + +static int ptls_set_ecdsa_private_key(ptls_context_t *ctx, ptls_asn1_pkcs8_private_key_t *pkey, ptls_minicrypto_log_ctx_t *log_ctx) +{ + uint8_t *bytes = pkey->vec.base + pkey->parameters_index; + size_t bytes_max = pkey->parameters_length; + size_t byte_index = 0; + uint8_t *curve_id = NULL; + uint32_t curve_id_length = 0; + int decode_error = 0; + uint32_t seq_length; + size_t last_byte = 0; + uint8_t *ecdsa_key_data = NULL; + uint32_t ecdsa_key_data_length = 0; + size_t ecdsa_key_data_last = 0; + + /* We expect the parameters to include just the curve ID */ + + byte_index = ptls_asn1_get_expected_type_and_length(bytes, bytes_max, byte_index, 0x06, &curve_id_length, NULL, &last_byte, + &decode_error, log_ctx); + + if (decode_error == 0 && bytes_max != last_byte) { + byte_index = ptls_asn1_error_message("Length larger than parameters", bytes_max, byte_index, 0, log_ctx); + decode_error = PTLS_ERROR_BER_EXCESSIVE_LENGTH; + } + + if (decode_error == 0) { + curve_id = bytes + byte_index; + + if (log_ctx != NULL) { + /* print the OID value */ + log_ctx->fn(log_ctx->ctx, "Curve: "); + ptls_asn1_dump_content(curve_id, curve_id_length, 0, log_ctx); + log_ctx->fn(log_ctx->ctx, "\n"); + } + } + + /* We expect the key data to follow the ECDSA structure per RFC 5915 */ + bytes = pkey->vec.base + pkey->key_data_index; + bytes_max = pkey->key_data_length; + byte_index = 0; + + /* decode the wrapping sequence */ + if (decode_error == 0) { + byte_index = ptls_asn1_get_expected_type_and_length(bytes, bytes_max, byte_index, 0x30, &seq_length, NULL, &last_byte, + &decode_error, log_ctx); + } + + if (decode_error == 0 && bytes_max != last_byte) { + byte_index = ptls_asn1_error_message("Length larger than key data", bytes_max, byte_index, 0, log_ctx); + decode_error = PTLS_ERROR_BER_ELEMENT_TOO_SHORT; + } + + /* verify and skip the version number 1 */ + if (decode_error == 0) { + /* get first component: version, INTEGER, expect value 0 */ + if (byte_index + 3 > bytes_max) { + byte_index = ptls_asn1_error_message("Cannot find ECDSA Key Data Version", bytes_max, byte_index, 0, log_ctx); + decode_error = PTLS_ERROR_INCORRECT_ASN1_ECDSA_KEY_SYNTAX; + } else if (bytes[byte_index] != 0x02 || bytes[byte_index + 1] != 0x01 || bytes[byte_index + 2] != 0x01) { + decode_error = PTLS_ERROR_INCORRECT_PEM_ECDSA_KEY_VERSION; + byte_index = ptls_asn1_error_message("Incorrect ECDSA Key Data Version", bytes_max, byte_index, 0, log_ctx); + } else { + byte_index += 3; + if (log_ctx != NULL) { + log_ctx->fn(log_ctx->ctx, "ECDSA Version = 1,\n"); + } + } + } + + /* obtain the octet string that contains the ECDSA private key */ + if (decode_error == 0) { + byte_index = ptls_asn1_get_expected_type_and_length(bytes, last_byte, byte_index, 0x04, &ecdsa_key_data_length, NULL, + &ecdsa_key_data_last, &decode_error, log_ctx); + + if (decode_error == 0) { + ecdsa_key_data = bytes + byte_index; + } + } + + /* If everything is fine, associate the ECDSA key with the context */ + if (curve_id_length == sizeof(ptls_asn1_curve_secp256r1) && curve_id != NULL && + memcmp(curve_id, ptls_asn1_curve_secp256r1, sizeof(ptls_asn1_curve_secp256r1)) == 0) { + if (SECP256R1_PRIVATE_KEY_SIZE != ecdsa_key_data_length) { + decode_error = PTLS_ERROR_INCORRECT_PEM_ECDSA_KEYSIZE; + if (log_ctx != NULL) { + /* print the OID value */ + log_ctx->fn(log_ctx->ctx, "Wrong SECP256R1 key length, %d instead of %d.\n", ecdsa_key_data_length, + SECP256R1_PRIVATE_KEY_SIZE); + } + } else { + ptls_minicrypto_secp256r1sha256_sign_certificate_t *minicrypto_sign_certificate; + + minicrypto_sign_certificate = (ptls_minicrypto_secp256r1sha256_sign_certificate_t *)malloc( + sizeof(ptls_minicrypto_secp256r1sha256_sign_certificate_t)); + + if (minicrypto_sign_certificate == NULL) { + decode_error = PTLS_ERROR_NO_MEMORY; + } else { + memset(minicrypto_sign_certificate, 0, sizeof(ptls_minicrypto_secp256r1sha256_sign_certificate_t)); + decode_error = ptls_minicrypto_init_secp256r1sha256_sign_certificate( + minicrypto_sign_certificate, ptls_iovec_init(ecdsa_key_data, ecdsa_key_data_length)); + } + if (decode_error == 0) { + ctx->sign_certificate = &minicrypto_sign_certificate->super; + + if (log_ctx != NULL) { + /* print the OID value */ + log_ctx->fn(log_ctx->ctx, "Initialized SECP512R1 signing key with %d bytes.\n", ecdsa_key_data_length); + } + } else if (log_ctx != NULL) { + log_ctx->fn(log_ctx->ctx, "SECP512R1 init with %d bytes returns %d.\n", ecdsa_key_data_length, decode_error); + } + } + } else { + decode_error = PTLS_ERROR_INCORRECT_PEM_ECDSA_CURVE; + if (log_ctx != NULL) { + /* print the OID value */ + log_ctx->fn(log_ctx->ctx, "Curve is not supported for signatures.\n"); + } + } + + return decode_error; +} + +int ptls_minicrypto_load_private_key(ptls_context_t *ctx, char const *pem_fname) +{ + ptls_asn1_pkcs8_private_key_t pkey = {{0}}; + int ret = ptls_pem_parse_private_key(pem_fname, &pkey, NULL); + + if (ret != 0) + goto err; + + /* Check that this is the expected key type. + * At this point, the minicrypto library only supports ECDSA keys. + * In theory, we could add support for RSA keys at some point. + */ + if (pkey.algorithm_length != sizeof(ptls_asn1_algorithm_ecdsa) || + memcmp(pkey.vec.base + pkey.algorithm_index, ptls_asn1_algorithm_ecdsa, sizeof(ptls_asn1_algorithm_ecdsa)) != 0) { + ret = -1; + goto err; + } + + ret = ptls_set_ecdsa_private_key(ctx, &pkey, NULL); + +err: + if (pkey.vec.base) { + ptls_clear_memory(pkey.vec.base, pkey.vec.len); + free(pkey.vec.base); + } + return ret; +} diff --git a/deps/picoquic/picotls/lib/openssl.c b/deps/picoquic/picotls/lib/openssl.c new file mode 100644 index 0000000..c7ba115 --- /dev/null +++ b/deps/picoquic/picotls/lib/openssl.c @@ -0,0 +1,1491 @@ +/* + * Copyright (c) 2016 DeNA Co., Ltd., Kazuho Oku + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#ifdef _WINDOWS +#include "wincompat.h" +#else +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "picotls.h" +#include "picotls/openssl.h" + +#ifdef _WINDOWS +#ifndef _CRT_SECURE_NO_WARNINGS +#define _CRT_SECURE_NO_WARNINGS +#endif +#pragma warning(disable : 4996) +#include +#endif + +#if !defined(LIBRESSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER >= 0x10100000L +#define OPENSSL_1_1_API 1 +#elif defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER >= 0x2070000fL +#define OPENSSL_1_1_API 1 +#else +#define OPENSSL_1_1_API 0 +#endif + +#if !OPENSSL_1_1_API + +#define EVP_PKEY_up_ref(p) CRYPTO_add(&(p)->references, 1, CRYPTO_LOCK_EVP_PKEY) +#define X509_STORE_up_ref(p) CRYPTO_add(&(p)->references, 1, CRYPTO_LOCK_X509_STORE) + +static HMAC_CTX *HMAC_CTX_new(void) +{ + HMAC_CTX *ctx; + + if ((ctx = OPENSSL_malloc(sizeof(*ctx))) == NULL) + return NULL; + HMAC_CTX_init(ctx); + return ctx; +} + +static void HMAC_CTX_free(HMAC_CTX *ctx) +{ + HMAC_CTX_cleanup(ctx); + OPENSSL_free(ctx); +} + +static int EVP_CIPHER_CTX_reset(EVP_CIPHER_CTX *ctx) +{ + return EVP_CIPHER_CTX_cleanup(ctx); +} + +#endif + +void ptls_openssl_random_bytes(void *buf, size_t len) +{ + int ret = RAND_bytes(buf, (int)len); + if (ret != 1) { + fprintf(stderr, "RAND_bytes() failed with code: %d\n", ret); + abort(); + } +} + +static EC_KEY *ecdh_gerenate_key(EC_GROUP *group) +{ + EC_KEY *key; + + if ((key = EC_KEY_new()) == NULL) + return NULL; + if (!EC_KEY_set_group(key, group) || !EC_KEY_generate_key(key)) { + EC_KEY_free(key); + return NULL; + } + + return key; +} + +static int ecdh_calc_secret(ptls_iovec_t *out, const EC_GROUP *group, EC_KEY *privkey, EC_POINT *peer_point) +{ + ptls_iovec_t secret; + int ret; + + secret.len = (EC_GROUP_get_degree(group) + 7) / 8; + if ((secret.base = malloc(secret.len)) == NULL) { + ret = PTLS_ERROR_NO_MEMORY; + goto Exit; + } + if (ECDH_compute_key(secret.base, secret.len, peer_point, privkey, NULL) <= 0) { + ret = PTLS_ALERT_HANDSHAKE_FAILURE; /* ??? */ + goto Exit; + } + ret = 0; + +Exit: + if (ret == 0) { + *out = secret; + } else { + free(secret.base); + *out = (ptls_iovec_t){NULL}; + } + return ret; +} + +static EC_POINT *x9_62_decode_point(const EC_GROUP *group, ptls_iovec_t vec, BN_CTX *bn_ctx) +{ + EC_POINT *point = NULL; + + if ((point = EC_POINT_new(group)) == NULL) + return NULL; + if (!EC_POINT_oct2point(group, point, vec.base, vec.len, bn_ctx)) { + EC_POINT_free(point); + return NULL; + } + + return point; +} + +static ptls_iovec_t x9_62_encode_point(const EC_GROUP *group, const EC_POINT *point, BN_CTX *bn_ctx) +{ + ptls_iovec_t vec; + + if ((vec.len = EC_POINT_point2oct(group, point, POINT_CONVERSION_UNCOMPRESSED, NULL, 0, bn_ctx)) == 0) + return (ptls_iovec_t){NULL}; + if ((vec.base = malloc(vec.len)) == NULL) + return (ptls_iovec_t){NULL}; + if (EC_POINT_point2oct(group, point, POINT_CONVERSION_UNCOMPRESSED, vec.base, vec.len, bn_ctx) != vec.len) { + free(vec.base); + return (ptls_iovec_t){NULL}; + } + + return vec; +} + +struct st_x9_62_keyex_context_t { + ptls_key_exchange_context_t super; + BN_CTX *bn_ctx; + EC_KEY *privkey; +}; + +static void x9_62_free_context(struct st_x9_62_keyex_context_t *ctx) +{ + free(ctx->super.pubkey.base); + if (ctx->privkey != NULL) + EC_KEY_free(ctx->privkey); + if (ctx->bn_ctx != NULL) + BN_CTX_free(ctx->bn_ctx); + free(ctx); +} + +static int x9_62_on_exchange(ptls_key_exchange_context_t **_ctx, int release, ptls_iovec_t *secret, ptls_iovec_t peerkey) +{ + struct st_x9_62_keyex_context_t *ctx = (struct st_x9_62_keyex_context_t *)*_ctx; + const EC_GROUP *group = EC_KEY_get0_group(ctx->privkey); + EC_POINT *peer_point = NULL; + int ret; + + if (secret == NULL) { + ret = 0; + goto Exit; + } + + if ((peer_point = x9_62_decode_point(group, peerkey, ctx->bn_ctx)) == NULL) { + ret = PTLS_ALERT_DECODE_ERROR; + goto Exit; + } + if ((ret = ecdh_calc_secret(secret, group, ctx->privkey, peer_point)) != 0) + goto Exit; + +Exit: + if (peer_point != NULL) + EC_POINT_free(peer_point); + if (release) { + x9_62_free_context(ctx); + *_ctx = NULL; + } + return ret; +} + +static int x9_62_create_context(ptls_key_exchange_algorithm_t *algo, struct st_x9_62_keyex_context_t **ctx) +{ + int ret; + + if ((*ctx = (struct st_x9_62_keyex_context_t *)malloc(sizeof(**ctx))) == NULL) { + ret = PTLS_ERROR_NO_MEMORY; + goto Exit; + } + **ctx = (struct st_x9_62_keyex_context_t){{algo, {NULL}, x9_62_on_exchange}}; + + if (((*ctx)->bn_ctx = BN_CTX_new()) == NULL) { + ret = PTLS_ERROR_NO_MEMORY; + goto Exit; + } + + ret = 0; +Exit: + if (ret != 0 && *ctx != NULL) { + x9_62_free_context(*ctx); + *ctx = NULL; + } + return ret; +} + +static int x9_62_setup_pubkey(struct st_x9_62_keyex_context_t *ctx) +{ + const EC_GROUP *group = EC_KEY_get0_group(ctx->privkey); + const EC_POINT *pubkey = EC_KEY_get0_public_key(ctx->privkey); + if ((ctx->super.pubkey = x9_62_encode_point(group, pubkey, ctx->bn_ctx)).base == NULL) + return PTLS_ERROR_NO_MEMORY; + return 0; +} + +static int x9_62_create_key_exchange(ptls_key_exchange_algorithm_t *algo, ptls_key_exchange_context_t **_ctx) +{ + EC_GROUP *group = NULL; + struct st_x9_62_keyex_context_t *ctx = NULL; + int ret; + + /* FIXME use a global? */ + if ((group = EC_GROUP_new_by_curve_name((int)algo->data)) == NULL) { + ret = PTLS_ERROR_LIBRARY; + goto Exit; + } + if ((ret = x9_62_create_context(algo, &ctx)) != 0) + goto Exit; + if ((ctx->privkey = ecdh_gerenate_key(group)) == NULL) { + ret = PTLS_ERROR_LIBRARY; + goto Exit; + } + if ((ret = x9_62_setup_pubkey(ctx)) != 0) + goto Exit; + ret = 0; + +Exit: + if (group != NULL) + EC_GROUP_free(group); + if (ret == 0) { + *_ctx = &ctx->super; + } else { + if (ctx != NULL) + x9_62_free_context(ctx); + *_ctx = NULL; + } + + return ret; +} + +static int x9_62_init_key(ptls_key_exchange_algorithm_t *algo, ptls_key_exchange_context_t **_ctx, EC_KEY *eckey) +{ + struct st_x9_62_keyex_context_t *ctx = NULL; + int ret; + + if ((ret = x9_62_create_context(algo, &ctx)) != 0) + goto Exit; + ctx->privkey = eckey; + if ((ret = x9_62_setup_pubkey(ctx)) != 0) + goto Exit; + ret = 0; + +Exit: + if (ret == 0) { + *_ctx = &ctx->super; + } else { + if (ctx != NULL) + x9_62_free_context(ctx); + *_ctx = NULL; + } + return ret; +} + +static int x9_62_key_exchange(EC_GROUP *group, ptls_iovec_t *pubkey, ptls_iovec_t *secret, ptls_iovec_t peerkey, BN_CTX *bn_ctx) +{ + EC_POINT *peer_point = NULL; + EC_KEY *privkey = NULL; + int ret; + + *pubkey = (ptls_iovec_t){NULL}; + *secret = (ptls_iovec_t){NULL}; + + /* decode peer key */ + if ((peer_point = x9_62_decode_point(group, peerkey, bn_ctx)) == NULL) { + ret = PTLS_ALERT_DECODE_ERROR; + goto Exit; + } + + /* create private key */ + if ((privkey = ecdh_gerenate_key(group)) == NULL) { + ret = PTLS_ERROR_NO_MEMORY; + goto Exit; + } + + /* encode public key */ + if ((*pubkey = x9_62_encode_point(group, EC_KEY_get0_public_key(privkey), bn_ctx)).base == NULL) { + ret = PTLS_ERROR_NO_MEMORY; + goto Exit; + } + + /* calc secret */ + secret->len = (EC_GROUP_get_degree(group) + 7) / 8; + if ((secret->base = malloc(secret->len)) == NULL) { + ret = PTLS_ERROR_NO_MEMORY; + goto Exit; + } + + /* ecdh! */ + if (ECDH_compute_key(secret->base, secret->len, peer_point, privkey, NULL) <= 0) { + ret = PTLS_ALERT_HANDSHAKE_FAILURE; /* ??? */ + goto Exit; + } + + ret = 0; + +Exit: + if (peer_point != NULL) + EC_POINT_free(peer_point); + if (privkey != NULL) + EC_KEY_free(privkey); + if (ret != 0) { + free(pubkey->base); + *pubkey = (ptls_iovec_t){NULL}; + free(secret->base); + *secret = (ptls_iovec_t){NULL}; + } + return ret; +} + +static int secp_key_exchange(ptls_key_exchange_algorithm_t *algo, ptls_iovec_t *pubkey, ptls_iovec_t *secret, ptls_iovec_t peerkey) +{ + EC_GROUP *group = NULL; + BN_CTX *bn_ctx = NULL; + int ret; + + if ((group = EC_GROUP_new_by_curve_name((int)algo->data)) == NULL) { + ret = PTLS_ERROR_LIBRARY; + goto Exit; + } + if ((bn_ctx = BN_CTX_new()) == NULL) { + ret = PTLS_ERROR_NO_MEMORY; + goto Exit; + } + + ret = x9_62_key_exchange(group, pubkey, secret, peerkey, bn_ctx); + +Exit: + if (bn_ctx != NULL) + BN_CTX_free(bn_ctx); + if (group != NULL) + EC_GROUP_free(group); + return ret; +} + +#if PTLS_OPENSSL_HAVE_X25519 + +struct st_evp_keyex_context_t { + ptls_key_exchange_context_t super; + EVP_PKEY *privkey; +}; + +static void evp_keyex_free(struct st_evp_keyex_context_t *ctx) +{ + if (ctx->privkey != NULL) + EVP_PKEY_free(ctx->privkey); + if (ctx->super.pubkey.base != NULL) + OPENSSL_free(ctx->super.pubkey.base); + free(ctx); +} + +static int evp_keyex_on_exchange(ptls_key_exchange_context_t **_ctx, int release, ptls_iovec_t *secret, ptls_iovec_t peerkey) +{ + struct st_evp_keyex_context_t *ctx = (void *)*_ctx; + EVP_PKEY *evppeer = NULL; + EVP_PKEY_CTX *evpctx = NULL; + int ret; + + if (secret == NULL) { + ret = 0; + goto Exit; + } + + secret->base = NULL; + + if (peerkey.len != ctx->super.pubkey.len) { + ret = PTLS_ALERT_DECRYPT_ERROR; + goto Exit; + } + + if ((evppeer = EVP_PKEY_new()) == NULL) { + ret = PTLS_ERROR_NO_MEMORY; + goto Exit; + } + if (EVP_PKEY_copy_parameters(evppeer, ctx->privkey) <= 0) { + ret = PTLS_ERROR_LIBRARY; + goto Exit; + } + if (EVP_PKEY_set1_tls_encodedpoint(evppeer, peerkey.base, peerkey.len) <= 0) { + ret = PTLS_ERROR_LIBRARY; + goto Exit; + } + if ((evpctx = EVP_PKEY_CTX_new(ctx->privkey, NULL)) == NULL) { + ret = PTLS_ERROR_LIBRARY; + goto Exit; + } + if (EVP_PKEY_derive_init(evpctx) <= 0) { + ret = PTLS_ERROR_LIBRARY; + goto Exit; + } + if (EVP_PKEY_derive_set_peer(evpctx, evppeer) <= 0) { + ret = PTLS_ERROR_LIBRARY; + goto Exit; + } + if (EVP_PKEY_derive(evpctx, NULL, &secret->len) <= 0) { + ret = PTLS_ERROR_LIBRARY; + goto Exit; + } + if ((secret->base = malloc(secret->len)) == NULL) { + ret = PTLS_ERROR_NO_MEMORY; + goto Exit; + } + if (EVP_PKEY_derive(evpctx, secret->base, &secret->len) <= 0) { + ret = PTLS_ERROR_LIBRARY; + goto Exit; + } + + ret = 0; +Exit: + if (evpctx != NULL) + EVP_PKEY_CTX_free(evpctx); + if (evppeer != NULL) + EVP_PKEY_free(evppeer); + if (ret != 0) + free(secret->base); + if (release) { + evp_keyex_free(ctx); + *_ctx = NULL; + } + return ret; +} + +static int evp_keyex_init(ptls_key_exchange_algorithm_t *algo, ptls_key_exchange_context_t **_ctx, EVP_PKEY *pkey) +{ + struct st_evp_keyex_context_t *ctx = NULL; + int ret; + + /* instantiate */ + if ((ctx = malloc(sizeof(*ctx))) == NULL) { + ret = PTLS_ERROR_NO_MEMORY; + goto Exit; + } + *ctx = (struct st_evp_keyex_context_t){{algo, {NULL}, evp_keyex_on_exchange}, pkey}; + + /* set public key */ + if ((ctx->super.pubkey.len = EVP_PKEY_get1_tls_encodedpoint(ctx->privkey, &ctx->super.pubkey.base)) == 0) { + ctx->super.pubkey.base = NULL; + return PTLS_ERROR_NO_MEMORY; + } + + *_ctx = &ctx->super; + ret = 0; +Exit: + if (ret != 0 && ctx != NULL) + evp_keyex_free(ctx); + return ret; +} + +static int evp_keyex_create(ptls_key_exchange_algorithm_t *algo, ptls_key_exchange_context_t **ctx) +{ + EVP_PKEY_CTX *evpctx = NULL; + EVP_PKEY *pkey = NULL; + int ret; + + /* generate private key */ + if ((evpctx = EVP_PKEY_CTX_new_id((int)algo->data, NULL)) == NULL) { + ret = PTLS_ERROR_LIBRARY; + goto Exit; + } + if (EVP_PKEY_keygen_init(evpctx) <= 0) { + ret = PTLS_ERROR_LIBRARY; + goto Exit; + } + if (EVP_PKEY_keygen(evpctx, &pkey) <= 0) { + ret = PTLS_ERROR_LIBRARY; + goto Exit; + } + + /* setup */ + if ((ret = evp_keyex_init(algo, ctx, pkey)) != 0) + goto Exit; + pkey = NULL; + ret = 0; + +Exit: + if (pkey != NULL) + EVP_PKEY_free(pkey); + if (evpctx != NULL) + EVP_PKEY_CTX_free(evpctx); + return ret; +} + +static int evp_keyex_exchange(ptls_key_exchange_algorithm_t *algo, ptls_iovec_t *outpubkey, ptls_iovec_t *secret, + ptls_iovec_t peerkey) +{ + ptls_key_exchange_context_t *ctx = NULL; + int ret; + + outpubkey->base = NULL; + + if ((ret = evp_keyex_create(algo, &ctx)) != 0) + goto Exit; + if ((outpubkey->base = malloc(ctx->pubkey.len)) == NULL) { + ret = PTLS_ERROR_NO_MEMORY; + goto Exit; + } + memcpy(outpubkey->base, ctx->pubkey.base, ctx->pubkey.len); + outpubkey->len = ctx->pubkey.len; + ret = evp_keyex_on_exchange(&ctx, 1, secret, peerkey); + assert(ctx == NULL); + +Exit: + if (ctx != NULL) + evp_keyex_on_exchange(&ctx, 1, NULL, ptls_iovec_init(NULL, 0)); + if (ret != 0) + free(outpubkey->base); + return ret; +} + +#endif + +int ptls_openssl_create_key_exchange(ptls_key_exchange_context_t **ctx, EVP_PKEY *pkey) +{ + int ret, id; + + switch (id = EVP_PKEY_id(pkey)) { + + case EVP_PKEY_EC: { + /* obtain eckey */ + EC_KEY *eckey = EVP_PKEY_get1_EC_KEY(pkey); + + /* determine algo */ + ptls_key_exchange_algorithm_t *algo; + switch (EC_GROUP_get_curve_name(EC_KEY_get0_group(eckey))) { + case NID_X9_62_prime256v1: + algo = &ptls_openssl_secp256r1; + break; +#if PTLS_OPENSSL_HAVE_SECP384R1 + case NID_secp384r1: + algo = &ptls_openssl_secp384r1; + break; +#endif +#if PTLS_OPENSSL_HAVE_SECP521R1 + case NID_secp521r1: + algo = &ptls_openssl_secp521r1; + break; +#endif + default: + EC_KEY_free(eckey); + return PTLS_ERROR_INCOMPATIBLE_KEY; + } + + /* load key */ + if ((ret = x9_62_init_key(algo, ctx, eckey)) != 0) { + EC_KEY_free(eckey); + return ret; + } + + return 0; + } break; + +#if PTLS_OPENSSL_HAVE_X25519 + case NID_X25519: + if ((ret = evp_keyex_init(&ptls_openssl_x25519, ctx, pkey)) != 0) + return ret; + EVP_PKEY_up_ref(pkey); + return 0; +#endif + + default: + return PTLS_ERROR_INCOMPATIBLE_KEY; + } +} + +static int do_sign(EVP_PKEY *key, ptls_buffer_t *outbuf, ptls_iovec_t input, const EVP_MD *md) +{ + EVP_MD_CTX *ctx = NULL; + EVP_PKEY_CTX *pkey_ctx; + size_t siglen; + int ret; + + if ((ctx = EVP_MD_CTX_create()) == NULL) { + ret = PTLS_ERROR_NO_MEMORY; + goto Exit; + } + if (EVP_DigestSignInit(ctx, &pkey_ctx, md, NULL, key) != 1) { + ret = PTLS_ERROR_LIBRARY; + goto Exit; + } + if (EVP_PKEY_id(key) == EVP_PKEY_RSA) { + if (EVP_PKEY_CTX_set_rsa_padding(pkey_ctx, RSA_PKCS1_PSS_PADDING) != 1) { + ret = PTLS_ERROR_LIBRARY; + goto Exit; + } + if (EVP_PKEY_CTX_set_rsa_pss_saltlen(pkey_ctx, -1) != 1) { + ret = PTLS_ERROR_LIBRARY; + goto Exit; + } + if (EVP_PKEY_CTX_set_rsa_mgf1_md(pkey_ctx, EVP_sha256()) != 1) { + ret = PTLS_ERROR_LIBRARY; + goto Exit; + } + } + if (EVP_DigestSignUpdate(ctx, input.base, input.len) != 1) { + ret = PTLS_ERROR_LIBRARY; + goto Exit; + } + if (EVP_DigestSignFinal(ctx, NULL, &siglen) != 1) { + ret = PTLS_ERROR_LIBRARY; + goto Exit; + } + if ((ret = ptls_buffer_reserve(outbuf, siglen)) != 0) + goto Exit; + if (EVP_DigestSignFinal(ctx, outbuf->base + outbuf->off, &siglen) != 1) { + ret = PTLS_ERROR_LIBRARY; + goto Exit; + } + outbuf->off += siglen; + + ret = 0; +Exit: + if (ctx != NULL) + EVP_MD_CTX_destroy(ctx); + return ret; +} + +struct cipher_context_t { + ptls_cipher_context_t super; + EVP_CIPHER_CTX *evp; +}; + +static void cipher_dispose(ptls_cipher_context_t *_ctx) +{ + struct cipher_context_t *ctx = (struct cipher_context_t *)_ctx; + EVP_CIPHER_CTX_free(ctx->evp); +} + +static void cipher_do_init(ptls_cipher_context_t *_ctx, const void *iv) +{ + struct cipher_context_t *ctx = (struct cipher_context_t *)_ctx; + int ret; + ret = EVP_EncryptInit_ex(ctx->evp, NULL, NULL, NULL, iv); + assert(ret); +} + +static int cipher_setup_crypto(ptls_cipher_context_t *_ctx, int is_enc, const void *key, const EVP_CIPHER *cipher, + void (*do_transform)(ptls_cipher_context_t *, void *, const void *, size_t)) +{ + struct cipher_context_t *ctx = (struct cipher_context_t *)_ctx; + + ctx->super.do_dispose = cipher_dispose; + ctx->super.do_init = cipher_do_init; + ctx->super.do_transform = do_transform; + + if ((ctx->evp = EVP_CIPHER_CTX_new()) == NULL) + return PTLS_ERROR_NO_MEMORY; + + if (is_enc) { + if (!EVP_EncryptInit_ex(ctx->evp, cipher, NULL, key, NULL)) + goto Error; + } else { + if (!EVP_DecryptInit_ex(ctx->evp, cipher, NULL, key, NULL)) + goto Error; + EVP_CIPHER_CTX_set_padding(ctx->evp, 0); /* required to disable one block buffering in ECB mode */ + } + + return 0; +Error: + EVP_CIPHER_CTX_free(ctx->evp); + return PTLS_ERROR_LIBRARY; +} + +static void cipher_encrypt(ptls_cipher_context_t *_ctx, void *output, const void *input, size_t _len) +{ + struct cipher_context_t *ctx = (struct cipher_context_t *)_ctx; + int len = (int)_len, ret = EVP_EncryptUpdate(ctx->evp, output, &len, input, len); + assert(ret); + assert(len == (int)_len); +} + +static void cipher_decrypt(ptls_cipher_context_t *_ctx, void *output, const void *input, size_t _len) +{ + struct cipher_context_t *ctx = (struct cipher_context_t *)_ctx; + int len = (int)_len, ret = EVP_DecryptUpdate(ctx->evp, output, &len, input, len); + assert(ret); + assert(len == (int)_len); +} + +static int aes128ecb_setup_crypto(ptls_cipher_context_t *ctx, int is_enc, const void *key) +{ + return cipher_setup_crypto(ctx, is_enc, key, EVP_aes_128_ecb(), is_enc ? cipher_encrypt : cipher_decrypt); +} + +static int aes256ecb_setup_crypto(ptls_cipher_context_t *ctx, int is_enc, const void *key) +{ + return cipher_setup_crypto(ctx, is_enc, key, EVP_aes_256_ecb(), is_enc ? cipher_encrypt : cipher_decrypt); +} + +static int aes128ctr_setup_crypto(ptls_cipher_context_t *ctx, int is_enc, const void *key) +{ + return cipher_setup_crypto(ctx, 1, key, EVP_aes_128_ctr(), cipher_encrypt); +} + +static int aes256ctr_setup_crypto(ptls_cipher_context_t *ctx, int is_enc, const void *key) +{ + return cipher_setup_crypto(ctx, 1, key, EVP_aes_256_ctr(), cipher_encrypt); +} + +#if PTLS_OPENSSL_HAVE_CHACHA20_POLY1305 + +static int chacha20_setup_crypto(ptls_cipher_context_t *ctx, int is_enc, const void *key) +{ + return cipher_setup_crypto(ctx, 1, key, EVP_chacha20(), cipher_encrypt); +} + +#endif + +#if PTLS_OPENSSL_HAVE_BF + +static int bfecb_setup_crypto(ptls_cipher_context_t *ctx, int is_enc, const void *key) +{ + return cipher_setup_crypto(ctx, is_enc, key, EVP_bf_ecb(), is_enc ? cipher_encrypt : cipher_decrypt); +} + +#endif + +struct aead_crypto_context_t { + ptls_aead_context_t super; + EVP_CIPHER_CTX *evp_ctx; + uint8_t static_iv[PTLS_MAX_IV_SIZE]; +}; + +static void aead_dispose_crypto(ptls_aead_context_t *_ctx) +{ + struct aead_crypto_context_t *ctx = (struct aead_crypto_context_t *)_ctx; + + if (ctx->evp_ctx != NULL) + EVP_CIPHER_CTX_free(ctx->evp_ctx); +} + +static void aead_do_encrypt_init(ptls_aead_context_t *_ctx, uint64_t seq, const void *aad, size_t aadlen) +{ + struct aead_crypto_context_t *ctx = (struct aead_crypto_context_t *)_ctx; + uint8_t iv[PTLS_MAX_IV_SIZE]; + int ret; + + ptls_aead__build_iv(ctx->super.algo, iv, ctx->static_iv, seq); + ret = EVP_EncryptInit_ex(ctx->evp_ctx, NULL, NULL, NULL, iv); + assert(ret); + + if (aadlen != 0) { + int blocklen; + ret = EVP_EncryptUpdate(ctx->evp_ctx, NULL, &blocklen, aad, (int)aadlen); + assert(ret); + } +} + +static size_t aead_do_encrypt_update(ptls_aead_context_t *_ctx, void *output, const void *input, size_t inlen) +{ + struct aead_crypto_context_t *ctx = (struct aead_crypto_context_t *)_ctx; + int blocklen, ret; + + ret = EVP_EncryptUpdate(ctx->evp_ctx, output, &blocklen, input, (int)inlen); + assert(ret); + + return blocklen; +} + +static size_t aead_do_encrypt_final(ptls_aead_context_t *_ctx, void *_output) +{ + struct aead_crypto_context_t *ctx = (struct aead_crypto_context_t *)_ctx; + uint8_t *output = _output; + size_t off = 0, tag_size = ctx->super.algo->tag_size; + int blocklen, ret; + + ret = EVP_EncryptFinal_ex(ctx->evp_ctx, output + off, &blocklen); + assert(ret); + off += blocklen; + ret = EVP_CIPHER_CTX_ctrl(ctx->evp_ctx, EVP_CTRL_GCM_GET_TAG, (int)tag_size, output + off); + assert(ret); + off += tag_size; + + return off; +} + +static size_t aead_do_decrypt(ptls_aead_context_t *_ctx, void *_output, const void *input, size_t inlen, uint64_t seq, + const void *aad, size_t aadlen) +{ + struct aead_crypto_context_t *ctx = (struct aead_crypto_context_t *)_ctx; + uint8_t *output = _output, iv[PTLS_MAX_IV_SIZE]; + size_t off = 0, tag_size = ctx->super.algo->tag_size; + int blocklen, ret; + + if (inlen < tag_size) + return SIZE_MAX; + + ptls_aead__build_iv(ctx->super.algo, iv, ctx->static_iv, seq); + ret = EVP_DecryptInit_ex(ctx->evp_ctx, NULL, NULL, NULL, iv); + assert(ret); + if (aadlen != 0) { + ret = EVP_DecryptUpdate(ctx->evp_ctx, NULL, &blocklen, aad, (int)aadlen); + assert(ret); + } + ret = EVP_DecryptUpdate(ctx->evp_ctx, output + off, &blocklen, input, (int)(inlen - tag_size)); + assert(ret); + off += blocklen; + if (!EVP_CIPHER_CTX_ctrl(ctx->evp_ctx, EVP_CTRL_GCM_SET_TAG, (int)tag_size, (void *)((uint8_t *)input + inlen - tag_size))) + return SIZE_MAX; + if (!EVP_DecryptFinal_ex(ctx->evp_ctx, output + off, &blocklen)) + return SIZE_MAX; + off += blocklen; + + return off; +} + +static int aead_setup_crypto(ptls_aead_context_t *_ctx, int is_enc, const void *key, const void *iv, const EVP_CIPHER *cipher) +{ + struct aead_crypto_context_t *ctx = (struct aead_crypto_context_t *)_ctx; + int ret; + + memcpy(ctx->static_iv, iv, ctx->super.algo->iv_size); + if (key == NULL) + return 0; + + ctx->super.dispose_crypto = aead_dispose_crypto; + if (is_enc) { + ctx->super.do_encrypt_init = aead_do_encrypt_init; + ctx->super.do_encrypt_update = aead_do_encrypt_update; + ctx->super.do_encrypt_final = aead_do_encrypt_final; + ctx->super.do_encrypt = ptls_aead__do_encrypt; + ctx->super.do_decrypt = NULL; + } else { + ctx->super.do_encrypt_init = NULL; + ctx->super.do_encrypt_update = NULL; + ctx->super.do_encrypt_final = NULL; + ctx->super.do_decrypt = aead_do_decrypt; + } + ctx->evp_ctx = NULL; + + if ((ctx->evp_ctx = EVP_CIPHER_CTX_new()) == NULL) { + ret = PTLS_ERROR_NO_MEMORY; + goto Error; + } + if (is_enc) { + if (!EVP_EncryptInit_ex(ctx->evp_ctx, cipher, NULL, key, NULL)) { + ret = PTLS_ERROR_LIBRARY; + goto Error; + } + } else { + if (!EVP_DecryptInit_ex(ctx->evp_ctx, cipher, NULL, key, NULL)) { + ret = PTLS_ERROR_LIBRARY; + goto Error; + } + } + if (!EVP_CIPHER_CTX_ctrl(ctx->evp_ctx, EVP_CTRL_GCM_SET_IVLEN, (int)ctx->super.algo->iv_size, NULL)) { + ret = PTLS_ERROR_LIBRARY; + goto Error; + } + + return 0; + +Error: + aead_dispose_crypto(&ctx->super); + return ret; +} + +static int aead_aes128gcm_setup_crypto(ptls_aead_context_t *ctx, int is_enc, const void *key, const void *iv) +{ + return aead_setup_crypto(ctx, is_enc, key, iv, EVP_aes_128_gcm()); +} + +static int aead_aes256gcm_setup_crypto(ptls_aead_context_t *ctx, int is_enc, const void *key, const void *iv) +{ + return aead_setup_crypto(ctx, is_enc, key, iv, EVP_aes_256_gcm()); +} + +#if PTLS_OPENSSL_HAVE_CHACHA20_POLY1305 +static int aead_chacha20poly1305_setup_crypto(ptls_aead_context_t *ctx, int is_enc, const void *key, const void *iv) +{ + return aead_setup_crypto(ctx, is_enc, key, iv, EVP_chacha20_poly1305()); +} +#endif + +#define _sha256_final(ctx, md) SHA256_Final((md), (ctx)) +ptls_define_hash(sha256, SHA256_CTX, SHA256_Init, SHA256_Update, _sha256_final); + +#define _sha384_final(ctx, md) SHA384_Final((md), (ctx)) +ptls_define_hash(sha384, SHA512_CTX, SHA384_Init, SHA384_Update, _sha384_final); + +static int sign_certificate(ptls_sign_certificate_t *_self, ptls_t *tls, uint16_t *selected_algorithm, ptls_buffer_t *outbuf, + ptls_iovec_t input, const uint16_t *algorithms, size_t num_algorithms) +{ + ptls_openssl_sign_certificate_t *self = (ptls_openssl_sign_certificate_t *)_self; + const struct st_ptls_openssl_signature_scheme_t *scheme; + + /* select the algorithm */ + for (scheme = self->schemes; scheme->scheme_id != UINT16_MAX; ++scheme) { + size_t i; + for (i = 0; i != num_algorithms; ++i) + if (algorithms[i] == scheme->scheme_id) + goto Found; + } + return PTLS_ALERT_HANDSHAKE_FAILURE; + +Found: + *selected_algorithm = scheme->scheme_id; + return do_sign(self->key, outbuf, input, scheme->scheme_md); +} + +static X509 *to_x509(ptls_iovec_t vec) +{ + const uint8_t *p = vec.base; + return d2i_X509(NULL, &p, (long)vec.len); +} + +static int verify_sign(void *verify_ctx, ptls_iovec_t data, ptls_iovec_t signature) +{ + EVP_PKEY *key = verify_ctx; + EVP_MD_CTX *ctx = NULL; + EVP_PKEY_CTX *pkey_ctx = NULL; + int ret = 0; + + if (data.base == NULL) + goto Exit; + + if ((ctx = EVP_MD_CTX_create()) == NULL) { + ret = PTLS_ERROR_NO_MEMORY; + goto Exit; + } + if (EVP_DigestVerifyInit(ctx, &pkey_ctx, EVP_sha256(), NULL, key) != 1) { + ret = PTLS_ERROR_LIBRARY; + goto Exit; + } + if (EVP_PKEY_id(key) == EVP_PKEY_RSA) { + if (EVP_PKEY_CTX_set_rsa_padding(pkey_ctx, RSA_PKCS1_PSS_PADDING) != 1) { + ret = PTLS_ERROR_LIBRARY; + goto Exit; + } + if (EVP_PKEY_CTX_set_rsa_pss_saltlen(pkey_ctx, -1) != 1) { + ret = PTLS_ERROR_LIBRARY; + goto Exit; + } + if (EVP_PKEY_CTX_set_rsa_mgf1_md(pkey_ctx, EVP_sha256()) != 1) { + ret = PTLS_ERROR_LIBRARY; + goto Exit; + } + } + if (EVP_DigestVerifyUpdate(ctx, data.base, data.len) != 1) { + ret = PTLS_ERROR_LIBRARY; + goto Exit; + } + if (EVP_DigestVerifyFinal(ctx, signature.base, signature.len) != 1) { + ret = PTLS_ALERT_DECRYPT_ERROR; + goto Exit; + } + ret = 0; + +Exit: + if (ctx != NULL) + EVP_MD_CTX_destroy(ctx); + EVP_PKEY_free(key); + return ret; +} + +int ptls_openssl_init_sign_certificate(ptls_openssl_sign_certificate_t *self, EVP_PKEY *key) +{ + *self = (ptls_openssl_sign_certificate_t){{sign_certificate}}; + size_t scheme_index = 0; + +#define PUSH_SCHEME(id, md) \ + self->schemes[scheme_index++] = (struct st_ptls_openssl_signature_scheme_t) \ + { \ + id, md \ + } + + switch (EVP_PKEY_id(key)) { + case EVP_PKEY_RSA: + PUSH_SCHEME(PTLS_SIGNATURE_RSA_PSS_RSAE_SHA256, EVP_sha256()); + PUSH_SCHEME(PTLS_SIGNATURE_RSA_PSS_RSAE_SHA384, EVP_sha384()); + PUSH_SCHEME(PTLS_SIGNATURE_RSA_PSS_RSAE_SHA512, EVP_sha512()); + break; + case EVP_PKEY_EC: { + EC_KEY *eckey = EVP_PKEY_get1_EC_KEY(key); + switch (EC_GROUP_get_curve_name(EC_KEY_get0_group(eckey))) { + case NID_X9_62_prime256v1: + PUSH_SCHEME(PTLS_SIGNATURE_ECDSA_SECP256R1_SHA256, EVP_sha256()); + break; +#if defined(NID_secp384r1) && !OPENSSL_NO_SHA384 + case NID_secp384r1: + PUSH_SCHEME(PTLS_SIGNATURE_ECDSA_SECP384R1_SHA384, EVP_sha384()); + break; +#endif +#if defined(NID_secp384r1) && !OPENSSL_NO_SHA512 + case NID_secp521r1: + PUSH_SCHEME(PTLS_SIGNATURE_ECDSA_SECP521R1_SHA512, EVP_sha512()); + break; +#endif + default: + EC_KEY_free(eckey); + return PTLS_ERROR_INCOMPATIBLE_KEY; + } + EC_KEY_free(eckey); + } break; + default: + return PTLS_ERROR_INCOMPATIBLE_KEY; + } + PUSH_SCHEME(UINT16_MAX, NULL); + assert(scheme_index <= PTLS_ELEMENTSOF(self->schemes)); + +#undef PUSH_SCHEME + + EVP_PKEY_up_ref(key); + self->key = key; + + return 0; +} + +void ptls_openssl_dispose_sign_certificate(ptls_openssl_sign_certificate_t *self) +{ + EVP_PKEY_free(self->key); +} + +static int serialize_cert(X509 *cert, ptls_iovec_t *dst) +{ + int len = i2d_X509(cert, NULL); + assert(len > 0); + + if ((dst->base = malloc(len)) == NULL) + return PTLS_ERROR_NO_MEMORY; + unsigned char *p = dst->base; + dst->len = i2d_X509(cert, &p); + assert(len == dst->len); + + return 0; +} + +int ptls_openssl_load_certificates(ptls_context_t *ctx, X509 *cert, STACK_OF(X509) * chain) +{ + ptls_iovec_t *list = NULL; + size_t slot = 0, count = (cert != NULL) + (chain != NULL ? sk_X509_num(chain) : 0); + int ret; + + assert(ctx->certificates.list == NULL); + + if ((list = malloc(sizeof(*list) * count)) == NULL) { + ret = PTLS_ERROR_NO_MEMORY; + goto Exit; + } + if (cert != NULL) { + if ((ret = serialize_cert(cert, list + slot++)) != 0) + goto Exit; + } + if (chain != NULL) { + int i; + for (i = 0; i != sk_X509_num(chain); ++i) { + if ((ret = serialize_cert(sk_X509_value(chain, i), list + slot++)) != 0) + goto Exit; + } + } + + assert(slot == count); + + ctx->certificates.list = list; + ctx->certificates.count = count; + ret = 0; + +Exit: + if (ret != 0 && list != NULL) { + size_t i; + for (i = 0; i != slot; ++i) + free(list[i].base); + free(list); + } + return ret; +} + +static int verify_cert_chain(X509_STORE *store, X509 *cert, STACK_OF(X509) * chain, int is_server, const char *server_name) +{ + X509_STORE_CTX *verify_ctx; + int ret; + + assert(server_name != NULL && "ptls_set_server_name MUST be called"); + + /* verify certificate chain */ + if ((verify_ctx = X509_STORE_CTX_new()) == NULL) { + ret = PTLS_ERROR_NO_MEMORY; + goto Exit; + } + if (X509_STORE_CTX_init(verify_ctx, store, cert, chain) != 1) { + ret = PTLS_ERROR_LIBRARY; + goto Exit; + } + X509_STORE_CTX_set_purpose(verify_ctx, is_server ? X509_PURPOSE_SSL_SERVER : X509_PURPOSE_SSL_CLIENT); + if (X509_verify_cert(verify_ctx) != 1) { + int x509_err = X509_STORE_CTX_get_error(verify_ctx); + switch (x509_err) { + case X509_V_ERR_OUT_OF_MEM: + ret = PTLS_ERROR_NO_MEMORY; + break; + case X509_V_ERR_CERT_REVOKED: + ret = PTLS_ALERT_CERTIFICATE_REVOKED; + break; + case X509_V_ERR_CERT_NOT_YET_VALID: + case X509_V_ERR_CERT_HAS_EXPIRED: + ret = PTLS_ALERT_CERTIFICATE_EXPIRED; + break; + case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT: + case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY: + case X509_V_ERR_CERT_UNTRUSTED: + case X509_V_ERR_CERT_REJECTED: + ret = PTLS_ALERT_UNKNOWN_CA; + break; + case X509_V_ERR_INVALID_CA: + ret = PTLS_ALERT_BAD_CERTIFICATE; + break; + default: + ret = PTLS_ALERT_CERTIFICATE_UNKNOWN; + break; + } + goto Exit; + } + +#ifdef X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS + /* verify CN */ + if (server_name != NULL) { + if (ptls_server_name_is_ipaddr(server_name)) { + ret = X509_check_ip_asc(cert, server_name, 0); + } else { + ret = X509_check_host(cert, server_name, strlen(server_name), X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS, NULL); + } + if (ret != 1) { + if (ret == 0) { /* failed match */ + ret = PTLS_ALERT_BAD_CERTIFICATE; + } else { + ret = PTLS_ERROR_LIBRARY; + } + goto Exit; + } + } +#else +#warning "hostname validation is disabled; OpenSSL >= 1.0.2 or LibreSSL >= 2.5.0 is required" +#endif + + ret = 0; + +Exit: + if (verify_ctx != NULL) + X509_STORE_CTX_free(verify_ctx); + return ret; +} + +static int verify_cert(ptls_verify_certificate_t *_self, ptls_t *tls, int (**verifier)(void *, ptls_iovec_t, ptls_iovec_t), + void **verify_data, ptls_iovec_t *certs, size_t num_certs) +{ + ptls_openssl_verify_certificate_t *self = (ptls_openssl_verify_certificate_t *)_self; + X509 *cert = NULL; + STACK_OF(X509) *chain = sk_X509_new_null(); + size_t i; + int ret = 0; + + assert(num_certs != 0); + + /* convert certificates to OpenSSL representation */ + if ((cert = to_x509(certs[0])) == NULL) { + ret = PTLS_ALERT_BAD_CERTIFICATE; + goto Exit; + } + for (i = 1; i != num_certs; ++i) { + X509 *interm = to_x509(certs[i]); + if (interm == NULL) { + ret = PTLS_ALERT_BAD_CERTIFICATE; + goto Exit; + } + sk_X509_push(chain, interm); + } + + /* verify the chain */ + if ((ret = verify_cert_chain(self->cert_store, cert, chain, ptls_is_server(tls), ptls_get_server_name(tls))) != 0) + goto Exit; + + /* extract public key for verifying the TLS handshake signature */ + if ((*verify_data = X509_get_pubkey(cert)) == NULL) { + ret = PTLS_ALERT_BAD_CERTIFICATE; + goto Exit; + } + *verifier = verify_sign; + +Exit: + if (chain != NULL) + sk_X509_pop_free(chain, X509_free); + if (cert != NULL) + X509_free(cert); + return ret; +} + +int ptls_openssl_init_verify_certificate(ptls_openssl_verify_certificate_t *self, X509_STORE *store) +{ + *self = (ptls_openssl_verify_certificate_t){{verify_cert}}; + + if (store != NULL) { + X509_STORE_up_ref(store); + self->cert_store = store; + } else { + /* use default store */ + if ((self->cert_store = ptls_openssl_create_default_certificate_store()) == NULL) + return -1; + } + + return 0; +} + +void ptls_openssl_dispose_verify_certificate(ptls_openssl_verify_certificate_t *self) +{ + X509_STORE_free(self->cert_store); +} + +X509_STORE *ptls_openssl_create_default_certificate_store(void) +{ + X509_STORE *store; + X509_LOOKUP *lookup; + + if ((store = X509_STORE_new()) == NULL) + goto Error; + if ((lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file())) == NULL) + goto Error; + X509_LOOKUP_load_file(lookup, NULL, X509_FILETYPE_DEFAULT); + if ((lookup = X509_STORE_add_lookup(store, X509_LOOKUP_hash_dir())) == NULL) + goto Error; + X509_LOOKUP_add_dir(lookup, NULL, X509_FILETYPE_DEFAULT); + + return store; +Error: + if (store != NULL) + X509_STORE_free(store); + return NULL; +} + +#define TICKET_LABEL_SIZE 16 +#define TICKET_IV_SIZE EVP_MAX_IV_LENGTH + +int ptls_openssl_encrypt_ticket(ptls_buffer_t *buf, ptls_iovec_t src, + int (*cb)(unsigned char *key_name, unsigned char *iv, EVP_CIPHER_CTX *ctx, HMAC_CTX *hctx, int enc)) +{ + EVP_CIPHER_CTX *cctx = NULL; + HMAC_CTX *hctx = NULL; + uint8_t *dst; + int clen, ret; + + if ((cctx = EVP_CIPHER_CTX_new()) == NULL) { + ret = PTLS_ERROR_NO_MEMORY; + goto Exit; + } + if ((hctx = HMAC_CTX_new()) == NULL) { + ret = PTLS_ERROR_NO_MEMORY; + goto Exit; + } + + if ((ret = ptls_buffer_reserve(buf, TICKET_LABEL_SIZE + TICKET_IV_SIZE + src.len + EVP_MAX_BLOCK_LENGTH + EVP_MAX_MD_SIZE)) != + 0) + goto Exit; + dst = buf->base + buf->off; + + /* fill label and iv, as well as obtaining the keys */ + if (!(*cb)(dst, dst + TICKET_LABEL_SIZE, cctx, hctx, 1)) { + ret = PTLS_ERROR_LIBRARY; + goto Exit; + } + dst += TICKET_LABEL_SIZE + TICKET_IV_SIZE; + + /* encrypt */ + if (!EVP_EncryptUpdate(cctx, dst, &clen, src.base, (int)src.len)) { + ret = PTLS_ERROR_LIBRARY; + goto Exit; + } + dst += clen; + if (!EVP_EncryptFinal_ex(cctx, dst, &clen)) { + ret = PTLS_ERROR_LIBRARY; + goto Exit; + } + dst += clen; + + /* append hmac */ + if (!HMAC_Update(hctx, buf->base + buf->off, dst - (buf->base + buf->off)) || !HMAC_Final(hctx, dst, NULL)) { + ret = PTLS_ERROR_LIBRARY; + goto Exit; + } + dst += HMAC_size(hctx); + + assert(dst <= buf->base + buf->capacity); + buf->off += dst - (buf->base + buf->off); + ret = 0; + +Exit: + if (cctx != NULL) + EVP_CIPHER_CTX_free(cctx); + if (hctx != NULL) + HMAC_CTX_free(hctx); + return ret; +} + +int ptls_openssl_decrypt_ticket(ptls_buffer_t *buf, ptls_iovec_t src, + int (*cb)(unsigned char *key_name, unsigned char *iv, EVP_CIPHER_CTX *ctx, HMAC_CTX *hctx, int enc)) +{ + EVP_CIPHER_CTX *cctx = NULL; + HMAC_CTX *hctx = NULL; + int clen, ret; + + if ((cctx = EVP_CIPHER_CTX_new()) == NULL) { + ret = PTLS_ERROR_NO_MEMORY; + goto Exit; + } + if ((hctx = HMAC_CTX_new()) == NULL) { + ret = PTLS_ERROR_NO_MEMORY; + goto Exit; + } + + /* obtain cipher and hash context. + * Note: no need to handle renew, since in picotls we always send a new ticket to minimize the chance of ticket reuse */ + if (src.len < TICKET_LABEL_SIZE + TICKET_IV_SIZE) { + ret = PTLS_ALERT_DECODE_ERROR; + goto Exit; + } + if (!(*cb)(src.base, src.base + TICKET_LABEL_SIZE, cctx, hctx, 0)) { + ret = PTLS_ERROR_LIBRARY; + goto Exit; + } + + /* check hmac, and exclude label, iv, hmac */ + size_t hmac_size = HMAC_size(hctx); + if (src.len < TICKET_LABEL_SIZE + TICKET_IV_SIZE + hmac_size) { + ret = PTLS_ALERT_DECODE_ERROR; + goto Exit; + } + src.len -= hmac_size; + uint8_t hmac[EVP_MAX_MD_SIZE]; + if (!HMAC_Update(hctx, src.base, src.len) || !HMAC_Final(hctx, hmac, NULL)) { + ret = PTLS_ERROR_LIBRARY; + goto Exit; + } + if (!ptls_mem_equal(src.base + src.len, hmac, hmac_size)) { + ret = PTLS_ALERT_HANDSHAKE_FAILURE; + goto Exit; + } + src.base += TICKET_LABEL_SIZE + TICKET_IV_SIZE; + src.len -= TICKET_LABEL_SIZE + TICKET_IV_SIZE; + + /* decrypt */ + if ((ret = ptls_buffer_reserve(buf, src.len)) != 0) + goto Exit; + if (!EVP_DecryptUpdate(cctx, buf->base + buf->off, &clen, src.base, (int)src.len)) { + ret = PTLS_ERROR_LIBRARY; + goto Exit; + } + buf->off += clen; + if (!EVP_DecryptFinal_ex(cctx, buf->base + buf->off, &clen)) { + ret = PTLS_ERROR_LIBRARY; + goto Exit; + } + buf->off += clen; + + ret = 0; + +Exit: + if (cctx != NULL) + EVP_CIPHER_CTX_free(cctx); + if (hctx != NULL) + HMAC_CTX_free(hctx); + return ret; +} + +ptls_key_exchange_algorithm_t ptls_openssl_secp256r1 = {PTLS_GROUP_SECP256R1, x9_62_create_key_exchange, secp_key_exchange, + NID_X9_62_prime256v1}; +#if PTLS_OPENSSL_HAVE_SECP384R1 +ptls_key_exchange_algorithm_t ptls_openssl_secp384r1 = {PTLS_GROUP_SECP384R1, x9_62_create_key_exchange, secp_key_exchange, + NID_secp384r1}; +#endif +#if PTLS_OPENSSL_HAVE_SECP521R1 +ptls_key_exchange_algorithm_t ptls_openssl_secp521r1 = {PTLS_GROUP_SECP521R1, x9_62_create_key_exchange, secp_key_exchange, + NID_secp521r1}; +#endif +#if PTLS_OPENSSL_HAVE_X25519 +ptls_key_exchange_algorithm_t ptls_openssl_x25519 = {PTLS_GROUP_X25519, evp_keyex_create, evp_keyex_exchange, NID_X25519}; +#endif +ptls_key_exchange_algorithm_t *ptls_openssl_key_exchanges[] = {&ptls_openssl_secp256r1, NULL}; +ptls_cipher_algorithm_t ptls_openssl_aes128ecb = { + "AES128-ECB", PTLS_AES128_KEY_SIZE, PTLS_AES_BLOCK_SIZE, 0 /* iv size */, sizeof(struct cipher_context_t), + aes128ecb_setup_crypto}; +ptls_cipher_algorithm_t ptls_openssl_aes128ctr = { + "AES128-CTR", PTLS_AES128_KEY_SIZE, 1, PTLS_AES_IV_SIZE, sizeof(struct cipher_context_t), aes128ctr_setup_crypto}; +ptls_aead_algorithm_t ptls_openssl_aes128gcm = {"AES128-GCM", + PTLS_AESGCM_CONFIDENTIALITY_LIMIT, + PTLS_AESGCM_INTEGRITY_LIMIT, + &ptls_openssl_aes128ctr, + &ptls_openssl_aes128ecb, + PTLS_AES128_KEY_SIZE, + PTLS_AESGCM_IV_SIZE, + PTLS_AESGCM_TAG_SIZE, + sizeof(struct aead_crypto_context_t), + aead_aes128gcm_setup_crypto}; +ptls_cipher_algorithm_t ptls_openssl_aes256ecb = { + "AES256-ECB", PTLS_AES256_KEY_SIZE, PTLS_AES_BLOCK_SIZE, 0 /* iv size */, sizeof(struct cipher_context_t), + aes256ecb_setup_crypto}; +ptls_cipher_algorithm_t ptls_openssl_aes256ctr = { + "AES256-CTR", PTLS_AES256_KEY_SIZE, 1 /* block size */, PTLS_AES_IV_SIZE, sizeof(struct cipher_context_t), + aes256ctr_setup_crypto}; +ptls_aead_algorithm_t ptls_openssl_aes256gcm = {"AES256-GCM", + PTLS_AESGCM_CONFIDENTIALITY_LIMIT, + PTLS_AESGCM_INTEGRITY_LIMIT, + &ptls_openssl_aes256ctr, + &ptls_openssl_aes256ecb, + PTLS_AES256_KEY_SIZE, + PTLS_AESGCM_IV_SIZE, + PTLS_AESGCM_TAG_SIZE, + sizeof(struct aead_crypto_context_t), + aead_aes256gcm_setup_crypto}; +ptls_hash_algorithm_t ptls_openssl_sha256 = {PTLS_SHA256_BLOCK_SIZE, PTLS_SHA256_DIGEST_SIZE, sha256_create, + PTLS_ZERO_DIGEST_SHA256}; +ptls_hash_algorithm_t ptls_openssl_sha384 = {PTLS_SHA384_BLOCK_SIZE, PTLS_SHA384_DIGEST_SIZE, sha384_create, + PTLS_ZERO_DIGEST_SHA384}; +ptls_cipher_suite_t ptls_openssl_aes128gcmsha256 = {PTLS_CIPHER_SUITE_AES_128_GCM_SHA256, &ptls_openssl_aes128gcm, + &ptls_openssl_sha256}; +ptls_cipher_suite_t ptls_openssl_aes256gcmsha384 = {PTLS_CIPHER_SUITE_AES_256_GCM_SHA384, &ptls_openssl_aes256gcm, + &ptls_openssl_sha384}; +#if PTLS_OPENSSL_HAVE_CHACHA20_POLY1305 +ptls_cipher_algorithm_t ptls_openssl_chacha20 = { + "CHACHA20", PTLS_CHACHA20_KEY_SIZE, 1 /* block size */, PTLS_CHACHA20_IV_SIZE, sizeof(struct cipher_context_t), + chacha20_setup_crypto}; +ptls_aead_algorithm_t ptls_openssl_chacha20poly1305 = {"CHACHA20-POLY1305", + PTLS_CHACHA20POLY1305_CONFIDENTIALITY_LIMIT, + PTLS_CHACHA20POLY1305_INTEGRITY_LIMIT, + &ptls_openssl_chacha20, + NULL, + PTLS_CHACHA20_KEY_SIZE, + PTLS_CHACHA20POLY1305_IV_SIZE, + PTLS_CHACHA20POLY1305_TAG_SIZE, + sizeof(struct aead_crypto_context_t), + aead_chacha20poly1305_setup_crypto}; +ptls_cipher_suite_t ptls_openssl_chacha20poly1305sha256 = {PTLS_CIPHER_SUITE_CHACHA20_POLY1305_SHA256, + &ptls_openssl_chacha20poly1305, &ptls_openssl_sha256}; +#endif +ptls_cipher_suite_t *ptls_openssl_cipher_suites[] = {&ptls_openssl_aes256gcmsha384, &ptls_openssl_aes128gcmsha256, +#if PTLS_OPENSSL_HAVE_CHACHA20_POLY1305 + &ptls_openssl_chacha20poly1305sha256, +#endif + NULL}; + +#if PTLS_OPENSSL_HAVE_BF +ptls_cipher_algorithm_t ptls_openssl_bfecb = {"BF-ECB", PTLS_BLOWFISH_KEY_SIZE, PTLS_BLOWFISH_BLOCK_SIZE, + 0 /* iv size */, sizeof(struct cipher_context_t), bfecb_setup_crypto}; +#endif diff --git a/deps/picoquic/picotls/lib/pembase64.c b/deps/picoquic/picotls/lib/pembase64.c new file mode 100644 index 0000000..70197ef --- /dev/null +++ b/deps/picoquic/picotls/lib/pembase64.c @@ -0,0 +1,370 @@ +/* +* Copyright (c) 2016 Christian Huitema +* +* Permission to use, copy, modify, and distribute this software for any +* purpose with or without fee is hereby granted, provided that the above +* copyright notice and this permission notice appear in all copies. +* +* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +/* + * Manage Base64 encoding. + */ +#ifdef _WINDOWS +#include "wincompat.h" +#else +#include +#endif +#include +#include +#include +#include +#include "picotls.h" +#include "picotls/pembase64.h" + +static char ptls_base64_alphabet[] = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', + 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', + 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', + 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'}; + +static signed char ptls_base64_values[] = { + /* 0x00 to 0x0F */ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + /* 0x10 to 0x1F */ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + /* 0x20 to 0x2F. '+' at 2B, '/' at 2F */ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, + /* 0x30 to 0x3F -- digits 0 to 9 at 0x30 to 0x39*/ + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, + /* 0x40 to 0x4F -- chars 'A' to 'O' at 0x41 to 0x4F */ + -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + /* 0x50 to 0x5F -- chars 'P' to 'Z' at 0x50 to 0x5A */ + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, + /* 0x60 to 0x6F -- chars 'a' to 'o' at 0x61 to 0x6F */ + -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + /* 0x70 to 0x7F -- chars 'p' to 'z' at 0x70 to 0x7A */ + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1}; + +static void ptls_base64_cell(const uint8_t *data, char *text) +{ + int n[4]; + + n[0] = data[0] >> 2; + n[1] = ((data[0] & 3) << 4) | (data[1] >> 4); + n[2] = ((data[1] & 15) << 2) | (data[2] >> 6); + n[3] = data[2] & 63; + + for (int i = 0; i < 4; i++) { + text[i] = ptls_base64_alphabet[n[i]]; + } +} + +size_t ptls_base64_howlong(size_t data_length) +{ + return (((data_length + 2) / 3) * 4); +} + +int ptls_base64_encode(const uint8_t *data, size_t data_len, char *ptls_base64_text) +{ + int l = 0; + int lt = 0; + + while ((data_len - l) >= 3) { + ptls_base64_cell(data + l, ptls_base64_text + lt); + l += 3; + lt += 4; + } + + switch (data_len - l) { + case 0: + break; + case 1: + ptls_base64_text[lt++] = ptls_base64_alphabet[data[l] >> 2]; + ptls_base64_text[lt++] = ptls_base64_alphabet[(data[l] & 3) << 4]; + ptls_base64_text[lt++] = '='; + ptls_base64_text[lt++] = '='; + break; + case 2: + ptls_base64_text[lt++] = ptls_base64_alphabet[data[l] >> 2]; + ptls_base64_text[lt++] = ptls_base64_alphabet[((data[l] & 3) << 4) | (data[l + 1] >> 4)]; + ptls_base64_text[lt++] = ptls_base64_alphabet[((data[l + 1] & 15) << 2)]; + ptls_base64_text[lt++] = '='; + break; + default: + break; + } + ptls_base64_text[lt++] = 0; + + return lt; +} + +/* + * Take into input a line of text, so as to work by increments. + * The intermediate text of the decoding is kept in a state variable. + * The decoded data is accumulated in a PTLS buffer. + * The parsing is consistent with the lax definition in RFC 7468 + */ + +void ptls_base64_decode_init(ptls_base64_decode_state_t *state) +{ + state->nbc = 0; + state->nbo = 3; + state->v = 0; + state->status = PTLS_BASE64_DECODE_IN_PROGRESS; +} + +int ptls_base64_decode(const char *text, ptls_base64_decode_state_t *state, ptls_buffer_t *buf) +{ + int ret = 0; + uint8_t decoded[3]; + size_t text_index = 0; + int c; + signed char vc; + + /* skip initial blanks */ + while (text[text_index] != 0) { + c = text[text_index]; + + if (c == ' ' || c == '\t' || c == '\r' || c == '\n') { + text_index++; + } else { + break; + } + } + + while (text[text_index] != 0 && ret == 0 && state->status == PTLS_BASE64_DECODE_IN_PROGRESS) { + c = text[text_index++]; + + vc = 0 < c && c < 0x7f ? ptls_base64_values[c] : -1; + if (vc == -1) { + if (state->nbc == 2 && c == '=' && text[text_index] == '=') { + state->nbc = 4; + text_index++; + state->nbo = 1; + state->v <<= 12; + } else if (state->nbc == 3 && c == '=') { + state->nbc = 4; + state->nbo = 2; + state->v <<= 6; + } else { + /* Skip final blanks */ + for (--text_index; text[text_index] != 0; ++text_index) { + c = text[text_index]; + if (!(c == ' ' || c == '\t' || c == '\r' || c == '\n' || c == 0x0B || c == 0x0C)) + break; + } + + /* Should now be at end of buffer */ + if (text[text_index] == 0) { + break; + } else { + /* Not at end of buffer, signal a decoding error */ + state->nbo = 0; + state->status = PTLS_BASE64_DECODE_FAILED; + ret = PTLS_ERROR_INCORRECT_BASE64; + } + } + } else { + state->nbc++; + state->v <<= 6; + state->v |= vc; + } + + if (ret == 0 && state->nbc == 4) { + /* Convert to up to 3 octets */ + for (int j = 0; j < state->nbo; j++) { + decoded[j] = (uint8_t)(state->v >> (8 * (2 - j))); + } + + ret = ptls_buffer__do_pushv(buf, decoded, state->nbo); + + if (ret == 0) { + /* test for fin or continuation */ + if (state->nbo < 3) { + /* Check that there are only trainling blanks on this line */ + while (text[text_index] != 0) { + c = text[text_index++]; + + if (c == ' ' || c == '\t' || c == '\r' || c == '\n' || c == 0x0B || c == 0x0C) { + continue; + } + } + if (text[text_index] == 0) { + state->status = PTLS_BASE64_DECODE_DONE; + } else { + state->status = PTLS_BASE64_DECODE_FAILED; + ret = PTLS_ERROR_INCORRECT_BASE64; + } + break; + } else { + state->v = 0; + state->nbo = 3; + state->nbc = 0; + } + } + } + } + return ret; +} + +/* + * Reading a PEM file, to get an object: + * + * - Find first object, get the object name. + * - If object label is what the application expects, parse, else skip to end. + * + * The following labels are defined in RFC 7468: + * + * Sec. Label ASN.1 Type Reference Module + * ----+----------------------+-----------------------+---------+---------- + * 5 CERTIFICATE Certificate [RFC5280] id-pkix1-e + * 6 X509 CRL CertificateList [RFC5280] id-pkix1-e + * 7 CERTIFICATE REQUEST CertificationRequest [RFC2986] id-pkcs10 + * 8 PKCS7 ContentInfo [RFC2315] id-pkcs7* + * 9 CMS ContentInfo [RFC5652] id-cms2004 + * 10 PRIVATE KEY PrivateKeyInfo ::= [RFC5208] id-pkcs8 + * OneAsymmetricKey [RFC5958] id-aKPV1 + * 11 ENCRYPTED PRIVATE KEY EncryptedPrivateKeyInfo [RFC5958] id-aKPV1 + * 12 ATTRIBUTE CERTIFICATE AttributeCertificate [RFC5755] id-acv2 + * 13 PUBLIC KEY SubjectPublicKeyInfo [RFC5280] id-pkix1-e + */ + +static int ptls_compare_separator_line(const char *line, const char *begin_or_end, const char *label) +{ + int ret = strncmp(line, "-----", 5); + size_t text_index = 5; + + if (ret == 0) { + size_t begin_or_end_length = strlen(begin_or_end); + ret = strncmp(line + text_index, begin_or_end, begin_or_end_length); + text_index += begin_or_end_length; + } + + if (ret == 0) { + ret = line[text_index] - ' '; + text_index++; + } + + if (ret == 0) { + size_t label_length = strlen(label); + ret = strncmp(line + text_index, label, label_length); + text_index += label_length; + } + + if (ret == 0) { + ret = strncmp(line + text_index, "-----", 5); + } + + return ret; +} + +static int ptls_get_pem_object(FILE *F, const char *label, ptls_buffer_t *buf) +{ + int ret = PTLS_ERROR_PEM_LABEL_NOT_FOUND; + char line[256]; + ptls_base64_decode_state_t state; + + /* Get the label on a line by itself */ + while (fgets(line, 256, F)) { + if (ptls_compare_separator_line(line, "BEGIN", label) == 0) { + ret = 0; + ptls_base64_decode_init(&state); + break; + } + } + /* Get the data in the buffer */ + while (ret == 0 && fgets(line, 256, F)) { + if (ptls_compare_separator_line(line, "END", label) == 0) { + if (state.status == PTLS_BASE64_DECODE_DONE || (state.status == PTLS_BASE64_DECODE_IN_PROGRESS && state.nbc == 0)) { + ret = 0; + } else { + ret = PTLS_ERROR_INCORRECT_BASE64; + } + break; + } else { + ret = ptls_base64_decode(line, &state, buf); + } + } + + return ret; +} + +int ptls_load_pem_objects(char const *pem_fname, const char *label, ptls_iovec_t *list, size_t list_max, size_t *nb_objects) +{ + FILE *F; + int ret = 0; + size_t count = 0; +#ifdef _WINDOWS + errno_t err = fopen_s(&F, pem_fname, "r"); + if (err != 0) { + ret = -1; + } +#else + F = fopen(pem_fname, "r"); + if (F == NULL) { + ret = -1; + } +#endif + + *nb_objects = 0; + + if (ret == 0) { + while (count < list_max) { + ptls_buffer_t buf; + + ptls_buffer_init(&buf, "", 0); + + ret = ptls_get_pem_object(F, label, &buf); + + if (ret == 0) { + if (buf.off > 0 && buf.is_allocated) { + list[count].base = buf.base; + list[count].len = buf.off; + count++; + } else { + ptls_buffer_dispose(&buf); + } + } else { + ptls_buffer_dispose(&buf); + break; + } + } + } + + if (ret == PTLS_ERROR_PEM_LABEL_NOT_FOUND && count > 0) { + ret = 0; + } + + *nb_objects = count; + + if (F != NULL) { + fclose(F); + } + + return ret; +} + +#define PTLS_MAX_CERTS_IN_CONTEXT 16 + +int ptls_load_certificates(ptls_context_t *ctx, char const *cert_pem_file) +{ + int ret = 0; + + ctx->certificates.list = (ptls_iovec_t *)malloc(PTLS_MAX_CERTS_IN_CONTEXT * sizeof(ptls_iovec_t)); + + if (ctx->certificates.list == NULL) { + ret = PTLS_ERROR_NO_MEMORY; + } else { + ret = ptls_load_pem_objects(cert_pem_file, "CERTIFICATE", ctx->certificates.list, PTLS_MAX_CERTS_IN_CONTEXT, + &ctx->certificates.count); + } + + return ret; +} diff --git a/deps/picoquic/picotls/lib/picotls.c b/deps/picoquic/picotls/lib/picotls.c new file mode 100644 index 0000000..a595b68 --- /dev/null +++ b/deps/picoquic/picotls/lib/picotls.c @@ -0,0 +1,5517 @@ +/* + * Copyright (c) 2016 DeNA Co., Ltd., Kazuho Oku + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +#include +#include +#include +#include +#include +#ifdef _WINDOWS +#include "wincompat.h" +#else +#include +#include +#endif +#include "picotls.h" +#if PICOTLS_USE_DTRACE +#include "picotls-probes.h" +#endif + +#if !defined(alloca) +#if defined(__GLIBC__) || defined(__sun) || defined(__APPLE__) || defined(__NEWLIB__) +#include // alloca (glibc uses . Note that Cygwin may have _WIN32 defined, so the order matters here) +#elif defined(_WIN32) +#include // alloca +#if !defined(alloca) +#define alloca _alloca // for clang with MS Codegen +#endif +#else +#include // alloca +#endif +#endif + +#define PTLS_MAX_PLAINTEXT_RECORD_SIZE 16384 +#define PTLS_MAX_ENCRYPTED_RECORD_SIZE (16384 + 256) + +#define PTLS_RECORD_VERSION_MAJOR 3 +#define PTLS_RECORD_VERSION_MINOR 3 + +#define PTLS_CONTENT_TYPE_CHANGE_CIPHER_SPEC 20 +#define PTLS_CONTENT_TYPE_ALERT 21 +#define PTLS_CONTENT_TYPE_HANDSHAKE 22 +#define PTLS_CONTENT_TYPE_APPDATA 23 + +#define PTLS_PSK_KE_MODE_PSK 0 +#define PTLS_PSK_KE_MODE_PSK_DHE 1 + +#define PTLS_HANDSHAKE_HEADER_SIZE 4 + +#define PTLS_EXTENSION_TYPE_SERVER_NAME 0 +#define PTLS_EXTENSION_TYPE_STATUS_REQUEST 5 +#define PTLS_EXTENSION_TYPE_SUPPORTED_GROUPS 10 +#define PTLS_EXTENSION_TYPE_SIGNATURE_ALGORITHMS 13 +#define PTLS_EXTENSION_TYPE_ALPN 16 +#define PTLS_EXTENSION_TYPE_COMPRESS_CERTIFICATE 27 +#define PTLS_EXTENSION_TYPE_PRE_SHARED_KEY 41 +#define PTLS_EXTENSION_TYPE_EARLY_DATA 42 +#define PTLS_EXTENSION_TYPE_SUPPORTED_VERSIONS 43 +#define PTLS_EXTENSION_TYPE_COOKIE 44 +#define PTLS_EXTENSION_TYPE_PSK_KEY_EXCHANGE_MODES 45 +#define PTLS_EXTENSION_TYPE_KEY_SHARE 51 +#define PTLS_EXTENSION_TYPE_ENCRYPTED_SERVER_NAME 0xffce + +#define PTLS_PROTOCOL_VERSION_TLS13_FINAL 0x0304 +#define PTLS_PROTOCOL_VERSION_TLS13_DRAFT26 0x7f1a +#define PTLS_PROTOCOL_VERSION_TLS13_DRAFT27 0x7f1b +#define PTLS_PROTOCOL_VERSION_TLS13_DRAFT28 0x7f1c + +#define PTLS_SERVER_NAME_TYPE_HOSTNAME 0 + +#define PTLS_SERVER_CERTIFICATE_VERIFY_CONTEXT_STRING "TLS 1.3, server CertificateVerify" +#define PTLS_CLIENT_CERTIFICATE_VERIFY_CONTEXT_STRING "TLS 1.3, client CertificateVerify" +#define PTLS_MAX_CERTIFICATE_VERIFY_SIGNDATA_SIZE \ + (64 + sizeof(PTLS_SERVER_CERTIFICATE_VERIFY_CONTEXT_STRING) + PTLS_MAX_DIGEST_SIZE * 2) + +#define PTLS_EARLY_DATA_MAX_DELAY 10000 /* max. RTT (in msec) to permit early data */ + +#ifndef PTLS_MAX_EARLY_DATA_SKIP_SIZE +#define PTLS_MAX_EARLY_DATA_SKIP_SIZE 65536 +#endif +#if defined(PTLS_DEBUG) && PTLS_DEBUG +#define PTLS_DEBUGF(...) fprintf(stderr, __VA_ARGS__) +#else +#define PTLS_DEBUGF(...) +#endif + +#ifndef PTLS_MEMORY_DEBUG +#define PTLS_MEMORY_DEBUG 0 +#endif + +#if PICOTLS_USE_DTRACE +#define PTLS_SHOULD_PROBE(LABEL, tls) (PTLS_UNLIKELY(PICOTLS_##LABEL##_ENABLED()) && !(tls)->skip_tracing) +#define PTLS_PROBE0(LABEL, tls) \ + do { \ + ptls_t *_tls = (tls); \ + if (PTLS_SHOULD_PROBE(LABEL, _tls)) \ + PICOTLS_##LABEL(_tls); \ + } while (0) +#define PTLS_PROBE(LABEL, tls, ...) \ + do { \ + ptls_t *_tls = (tls); \ + if (PTLS_SHOULD_PROBE(LABEL, _tls)) \ + PICOTLS_##LABEL(_tls, __VA_ARGS__); \ + } while (0) +#else +#define PTLS_PROBE0(LABEL, tls) +#define PTLS_PROBE(LABEL, tls, ...) +#endif + +/** + * list of supported versions in the preferred order + */ +static const uint16_t supported_versions[] = {PTLS_PROTOCOL_VERSION_TLS13_FINAL, PTLS_PROTOCOL_VERSION_TLS13_DRAFT28, + PTLS_PROTOCOL_VERSION_TLS13_DRAFT27, PTLS_PROTOCOL_VERSION_TLS13_DRAFT26}; + +static const uint8_t hello_retry_random[PTLS_HELLO_RANDOM_SIZE] = {0xCF, 0x21, 0xAD, 0x74, 0xE5, 0x9A, 0x61, 0x11, 0xBE, 0x1D, 0x8C, + 0x02, 0x1E, 0x65, 0xB8, 0x91, 0xC2, 0xA2, 0x11, 0x16, 0x7A, 0xBB, + 0x8C, 0x5E, 0x07, 0x9E, 0x09, 0xE2, 0xC8, 0xA8, 0x33, 0x9C}; + +struct st_ptls_traffic_protection_t { + uint8_t secret[PTLS_MAX_DIGEST_SIZE]; + size_t epoch; + /* the following fields are not used if the key_change callback is set */ + ptls_aead_context_t *aead; + uint64_t seq; +}; + +struct st_ptls_record_message_emitter_t { + ptls_message_emitter_t super; + size_t rec_start; +}; + +struct st_ptls_signature_algorithms_t { + uint16_t list[16]; /* expand? */ + size_t count; +}; + +struct st_ptls_certificate_request_t { + /** + * context.base becomes non-NULL when a CertificateRequest is pending for processing + */ + ptls_iovec_t context; + struct st_ptls_signature_algorithms_t signature_algorithms; +}; + +struct st_ptls_t { + /** + * the context + */ + ptls_context_t *ctx; + /** + * the state + */ + enum en_ptls_state_t { + PTLS_STATE_CLIENT_HANDSHAKE_START, + PTLS_STATE_CLIENT_EXPECT_SERVER_HELLO, + PTLS_STATE_CLIENT_EXPECT_SECOND_SERVER_HELLO, + PTLS_STATE_CLIENT_EXPECT_ENCRYPTED_EXTENSIONS, + PTLS_STATE_CLIENT_EXPECT_CERTIFICATE_REQUEST_OR_CERTIFICATE, + PTLS_STATE_CLIENT_EXPECT_CERTIFICATE, + PTLS_STATE_CLIENT_EXPECT_CERTIFICATE_VERIFY, + PTLS_STATE_CLIENT_EXPECT_FINISHED, + PTLS_STATE_SERVER_EXPECT_CLIENT_HELLO, + PTLS_STATE_SERVER_EXPECT_SECOND_CLIENT_HELLO, + PTLS_STATE_SERVER_EXPECT_CERTIFICATE, + PTLS_STATE_SERVER_EXPECT_CERTIFICATE_VERIFY, + /* ptls_send can be called if the state is below here */ + PTLS_STATE_SERVER_EXPECT_END_OF_EARLY_DATA, + PTLS_STATE_SERVER_EXPECT_FINISHED, + PTLS_STATE_POST_HANDSHAKE_MIN, + PTLS_STATE_CLIENT_POST_HANDSHAKE = PTLS_STATE_POST_HANDSHAKE_MIN, + PTLS_STATE_SERVER_POST_HANDSHAKE + } state; + /** + * receive buffers + */ + struct { + ptls_buffer_t rec; + ptls_buffer_t mess; + } recvbuf; + /** + * key schedule + */ + ptls_key_schedule_t *key_schedule; + /** + * values used for record protection + */ + struct { + struct st_ptls_traffic_protection_t dec; + struct st_ptls_traffic_protection_t enc; + } traffic_protection; + /** + * server-name passed using SNI + */ + char *server_name; + /** + * result of ALPN + */ + char *negotiated_protocol; + /** + * selected key-exchange + */ + ptls_key_exchange_algorithm_t *key_share; + /** + * selected cipher-suite + */ + ptls_cipher_suite_t *cipher_suite; + /** + * clienthello.random + */ + uint8_t client_random[PTLS_HELLO_RANDOM_SIZE]; + /** + * esni + */ + ptls_esni_secret_t *esni; + /** + * exporter master secret (either 0rtt or 1rtt) + */ + struct { + uint8_t *early; + uint8_t *one_rtt; + } exporter_master_secret; + /* flags */ + unsigned is_server : 1; + unsigned is_psk_handshake : 1; + unsigned send_change_cipher_spec : 1; + unsigned needs_key_update : 1; + unsigned key_update_send_request : 1; + unsigned skip_tracing : 1; + /** + * misc. + */ + union { + struct { + ptls_iovec_t legacy_session_id; + uint8_t legacy_session_id_buf[32]; + ptls_key_exchange_context_t *key_share_ctx; + unsigned offered_psk : 1; + /** + * if 1-RTT write key is active + */ + unsigned using_early_data : 1; + struct st_ptls_certificate_request_t certificate_request; + } client; + struct { + uint8_t pending_traffic_secret[PTLS_MAX_DIGEST_SIZE]; + uint32_t early_data_skipped_bytes; /* if not UINT32_MAX, the server is skipping early data */ + } server; + }; + /** + * certificate verify + * will be used by the client and the server (if require_client_authentication is set). + */ + struct { + int (*cb)(void *verify_ctx, ptls_iovec_t data, ptls_iovec_t signature); + void *verify_ctx; + } certificate_verify; + /** + * handshake traffic secret to be commisioned (an array of `uint8_t [PTLS_MAX_DIGEST_SIZE]` or NULL) + */ + uint8_t *pending_handshake_secret; + /** + * user data + */ + void *data_ptr; +}; + +struct st_ptls_record_t { + uint8_t type; + uint16_t version; + size_t length; + const uint8_t *fragment; +}; + +struct st_ptls_client_hello_psk_t { + ptls_iovec_t identity; + uint32_t obfuscated_ticket_age; + ptls_iovec_t binder; +}; + +#define MAX_UNKNOWN_EXTENSIONS 16 +#define MAX_CLIENT_CIPHERS 32 + +struct st_ptls_client_hello_t { + uint16_t legacy_version; + const uint8_t *random_bytes; + ptls_iovec_t legacy_session_id; + struct { + const uint8_t *ids; + size_t count; + } compression_methods; + uint16_t selected_version; + ptls_iovec_t cipher_suites; + ptls_iovec_t negotiated_groups; + ptls_iovec_t key_shares; + struct st_ptls_signature_algorithms_t signature_algorithms; + ptls_iovec_t server_name; + struct { + ptls_cipher_suite_t *cipher; /* selected cipher-suite, or NULL if esni extension is not used */ + ptls_key_exchange_algorithm_t *key_share; + ptls_iovec_t peer_key; + const uint8_t *record_digest; + ptls_iovec_t encrypted_sni; + } esni; + struct { + ptls_iovec_t list[16]; + size_t count; + } alpn; + struct { + uint16_t list[16]; + size_t count; + } cert_compression_algos; + struct { + uint16_t list[MAX_CLIENT_CIPHERS]; + size_t count; + } client_ciphers; + struct { + ptls_iovec_t all; + ptls_iovec_t tbs; + ptls_iovec_t ch1_hash; + ptls_iovec_t signature; + unsigned sent_key_share : 1; + } cookie; + struct { + const uint8_t *hash_end; + struct { + struct st_ptls_client_hello_psk_t list[4]; + size_t count; + } identities; + unsigned ke_modes; + unsigned early_data_indication : 1; + unsigned is_last_extension : 1; + } psk; + ptls_raw_extension_t unknown_extensions[MAX_UNKNOWN_EXTENSIONS + 1]; + unsigned status_request : 1; +}; + +struct st_ptls_server_hello_t { + uint8_t random_[PTLS_HELLO_RANDOM_SIZE]; + ptls_iovec_t legacy_session_id; + int is_retry_request; + union { + ptls_iovec_t peerkey; + struct { + uint16_t selected_group; + ptls_iovec_t cookie; + } retry_request; + }; +}; + +struct st_ptls_key_schedule_t { + unsigned generation; /* early secret (1), hanshake secret (2), master secret (3) */ + const char *hkdf_label_prefix; + uint8_t secret[PTLS_MAX_DIGEST_SIZE]; + size_t num_hashes; + struct { + ptls_hash_algorithm_t *algo; + ptls_hash_context_t *ctx; + } hashes[1]; +}; + +struct st_ptls_extension_decoder_t { + uint16_t type; + int (*cb)(ptls_t *tls, void *arg, const uint8_t *src, const uint8_t *const end); +}; + +struct st_ptls_extension_bitmap_t { + uint8_t bits[8]; /* only ids below 64 is tracked */ +}; + +static const uint8_t zeroes_of_max_digest_size[PTLS_MAX_DIGEST_SIZE] = {0}; + +static int hkdf_expand_label(ptls_hash_algorithm_t *algo, void *output, size_t outlen, ptls_iovec_t secret, const char *label, + ptls_iovec_t hash_value, const char *label_prefix); +static ptls_aead_context_t *new_aead(ptls_aead_algorithm_t *aead, ptls_hash_algorithm_t *hash, int is_enc, const void *secret, + ptls_iovec_t hash_value, const char *label_prefix); + +static int is_supported_version(uint16_t v) +{ + size_t i; + for (i = 0; i != PTLS_ELEMENTSOF(supported_versions); ++i) + if (supported_versions[i] == v) + return 1; + return 0; +} + +static inline int extension_bitmap_is_set(struct st_ptls_extension_bitmap_t *bitmap, uint16_t id) +{ + if (id < sizeof(bitmap->bits) * 8) + return (bitmap->bits[id / 8] & (1 << (id % 8))) != 0; + return 0; +} + +static inline void extension_bitmap_set(struct st_ptls_extension_bitmap_t *bitmap, uint16_t id) +{ + if (id < sizeof(bitmap->bits) * 8) + bitmap->bits[id / 8] |= 1 << (id % 8); +} + +static inline void init_extension_bitmap(struct st_ptls_extension_bitmap_t *bitmap, uint8_t hstype) +{ + *bitmap = (struct st_ptls_extension_bitmap_t){{0}}; + +#define EXT(extid, proc) \ + do { \ + int _found = 0; \ + do { \ + proc \ + } while (0); \ + if (!_found) \ + extension_bitmap_set(bitmap, PTLS_EXTENSION_TYPE_##extid); \ + } while (0) +#define ALLOW(allowed_hstype) _found = _found || hstype == PTLS_HANDSHAKE_TYPE_##allowed_hstype + + /* Implements the table found in section 4.2 of draft-19; "If an implementation receives an extension which it recognizes and + * which is not specified for the message in which it appears it MUST abort the handshake with an “illegal_parameter” alert." + */ + EXT(SERVER_NAME, { + ALLOW(CLIENT_HELLO); + ALLOW(ENCRYPTED_EXTENSIONS); + }); + EXT(STATUS_REQUEST, { + ALLOW(CLIENT_HELLO); + ALLOW(CERTIFICATE); + ALLOW(CERTIFICATE_REQUEST); + }); + EXT(SUPPORTED_GROUPS, { + ALLOW(CLIENT_HELLO); + ALLOW(ENCRYPTED_EXTENSIONS); + }); + EXT(SIGNATURE_ALGORITHMS, { + ALLOW(CLIENT_HELLO); + ALLOW(CERTIFICATE_REQUEST); + }); + EXT(ALPN, { + ALLOW(CLIENT_HELLO); + ALLOW(ENCRYPTED_EXTENSIONS); + }); + EXT(KEY_SHARE, { + ALLOW(CLIENT_HELLO); + ALLOW(SERVER_HELLO); + }); + EXT(PRE_SHARED_KEY, { + ALLOW(CLIENT_HELLO); + ALLOW(SERVER_HELLO); + }); + EXT(PSK_KEY_EXCHANGE_MODES, { ALLOW(CLIENT_HELLO); }); + EXT(EARLY_DATA, { + ALLOW(CLIENT_HELLO); + ALLOW(ENCRYPTED_EXTENSIONS); + ALLOW(NEW_SESSION_TICKET); + }); + EXT(COOKIE, { + ALLOW(CLIENT_HELLO); + ALLOW(SERVER_HELLO); + }); + EXT(SUPPORTED_VERSIONS, { + ALLOW(CLIENT_HELLO); + ALLOW(SERVER_HELLO); + }); + +#undef ALLOW +#undef EXT +} + +#ifndef ntoh16 +static uint16_t ntoh16(const uint8_t *src) +{ + return (uint16_t)src[0] << 8 | src[1]; +} +#endif + +#ifndef ntoh24 +static uint32_t ntoh24(const uint8_t *src) +{ + return (uint32_t)src[0] << 16 | (uint32_t)src[1] << 8 | src[2]; +} +#endif + +#ifndef ntoh32 +static uint32_t ntoh32(const uint8_t *src) +{ + return (uint32_t)src[0] << 24 | (uint32_t)src[1] << 16 | (uint32_t)src[2] << 8 | src[3]; +} +#endif + +#ifndef ntoh64 +static uint64_t ntoh64(const uint8_t *src) +{ + return (uint64_t)src[0] << 56 | (uint64_t)src[1] << 48 | (uint64_t)src[2] << 40 | (uint64_t)src[3] << 32 | + (uint64_t)src[4] << 24 | (uint64_t)src[5] << 16 | (uint64_t)src[6] << 8 | src[7]; +} +#endif + +void ptls_buffer__release_memory(ptls_buffer_t *buf) +{ + ptls_clear_memory(buf->base, buf->off); + if (buf->is_allocated) + free(buf->base); +} + +int ptls_buffer_reserve(ptls_buffer_t *buf, size_t delta) +{ + if (buf->base == NULL) + return PTLS_ERROR_NO_MEMORY; + + if (PTLS_MEMORY_DEBUG || buf->capacity < buf->off + delta) { + uint8_t *newp; + size_t new_capacity = buf->capacity; + if (new_capacity < 1024) + new_capacity = 1024; + while (new_capacity < buf->off + delta) { + new_capacity *= 2; + } + if ((newp = malloc(new_capacity)) == NULL) + return PTLS_ERROR_NO_MEMORY; + memcpy(newp, buf->base, buf->off); + ptls_buffer__release_memory(buf); + buf->base = newp; + buf->capacity = new_capacity; + buf->is_allocated = 1; + } + + return 0; +} + +int ptls_buffer__do_pushv(ptls_buffer_t *buf, const void *src, size_t len) +{ + int ret; + + if (len == 0) + return 0; + if ((ret = ptls_buffer_reserve(buf, len)) != 0) + return ret; + memcpy(buf->base + buf->off, src, len); + buf->off += len; + return 0; +} + +int ptls_buffer__adjust_quic_blocksize(ptls_buffer_t *buf, size_t body_size) +{ + uint8_t sizebuf[PTLS_ENCODE_QUICINT_CAPACITY]; + size_t sizelen = ptls_encode_quicint(sizebuf, body_size) - sizebuf; + + /* adjust amount of space before body_size to `sizelen` bytes */ + if (sizelen != 1) { + int ret; + if ((ret = ptls_buffer_reserve(buf, sizelen - 1)) != 0) + return ret; + memmove(buf->base + buf->off - body_size - 1 + sizelen, buf->base + buf->off - body_size, body_size); + buf->off += sizelen - 1; + } + + /* write the size */ + memcpy(buf->base + buf->off - body_size - sizelen, sizebuf, sizelen); + + return 0; +} + +int ptls_buffer__adjust_asn1_blocksize(ptls_buffer_t *buf, size_t body_size) +{ + fprintf(stderr, "unimplemented\n"); + abort(); +} + +int ptls_buffer_push_asn1_ubigint(ptls_buffer_t *buf, const void *bignum, size_t size) +{ + const uint8_t *p = bignum, *const end = p + size; + int ret; + + /* skip zeroes */ + for (; end - p >= 1; ++p) + if (*p != 0) + break; + + /* emit */ + ptls_buffer_push(buf, 2); + ptls_buffer_push_asn1_block(buf, { + if (*p >= 0x80) + ptls_buffer_push(buf, 0); + if (p != end) { + ptls_buffer_pushv(buf, p, end - p); + } else { + ptls_buffer_pushv(buf, "", 1); + } + }); + ret = 0; + +Exit: + return ret; +} + +#if PTLS_FUZZ_HANDSHAKE + +static size_t aead_encrypt(struct st_ptls_traffic_protection_t *ctx, void *output, const void *input, size_t inlen, + uint8_t content_type) +{ + memcpy(output, input, inlen); + memcpy(output + inlen, &content_type, 1); + return inlen + 1 + 16; +} + +static int aead_decrypt(struct st_ptls_traffic_protection_t *ctx, void *output, size_t *outlen, const void *input, size_t inlen) +{ + if (inlen < 16) { + return PTLS_ALERT_BAD_RECORD_MAC; + } + memcpy(output, input, inlen - 16); + *outlen = inlen - 16; /* removing the 16 bytes of tag */ + return 0; +} + +#else + +static void build_aad(uint8_t aad[5], size_t reclen) +{ + aad[0] = PTLS_CONTENT_TYPE_APPDATA; + aad[1] = PTLS_RECORD_VERSION_MAJOR; + aad[2] = PTLS_RECORD_VERSION_MINOR; + aad[3] = (uint8_t)(reclen >> 8); + aad[4] = (uint8_t)reclen; +} + +static size_t aead_encrypt(struct st_ptls_traffic_protection_t *ctx, void *output, const void *input, size_t inlen, + uint8_t content_type) +{ + uint8_t aad[5]; + size_t off = 0; + + build_aad(aad, inlen + 1 + ctx->aead->algo->tag_size); + ptls_aead_encrypt_init(ctx->aead, ctx->seq++, aad, sizeof(aad)); + off += ptls_aead_encrypt_update(ctx->aead, ((uint8_t *)output) + off, input, inlen); + off += ptls_aead_encrypt_update(ctx->aead, ((uint8_t *)output) + off, &content_type, 1); + off += ptls_aead_encrypt_final(ctx->aead, ((uint8_t *)output) + off); + + return off; +} + +static int aead_decrypt(struct st_ptls_traffic_protection_t *ctx, void *output, size_t *outlen, const void *input, size_t inlen) +{ + uint8_t aad[5]; + + build_aad(aad, inlen); + if ((*outlen = ptls_aead_decrypt(ctx->aead, output, input, inlen, ctx->seq, aad, sizeof(aad))) == SIZE_MAX) + return PTLS_ALERT_BAD_RECORD_MAC; + ++ctx->seq; + return 0; +} + +#endif /* #if PTLS_FUZZ_HANDSHAKE */ + +#define buffer_push_record(buf, type, block) \ + do { \ + ptls_buffer_push((buf), (type), PTLS_RECORD_VERSION_MAJOR, PTLS_RECORD_VERSION_MINOR); \ + ptls_buffer_push_block((buf), 2, block); \ + } while (0) + +static int buffer_push_encrypted_records(ptls_buffer_t *buf, uint8_t type, const uint8_t *src, size_t len, + struct st_ptls_traffic_protection_t *enc) +{ + int ret = 0; + + while (len != 0) { + size_t chunk_size = len; + if (chunk_size > PTLS_MAX_PLAINTEXT_RECORD_SIZE) + chunk_size = PTLS_MAX_PLAINTEXT_RECORD_SIZE; + buffer_push_record(buf, PTLS_CONTENT_TYPE_APPDATA, { + if ((ret = ptls_buffer_reserve(buf, chunk_size + enc->aead->algo->tag_size + 1)) != 0) + goto Exit; + buf->off += aead_encrypt(enc, buf->base + buf->off, src, chunk_size, type); + }); + src += chunk_size; + len -= chunk_size; + } + +Exit: + return ret; +} + +static int buffer_encrypt_record(ptls_buffer_t *buf, size_t rec_start, struct st_ptls_traffic_protection_t *enc) +{ + size_t bodylen = buf->off - rec_start - 5; + uint8_t *tmpbuf, type = buf->base[rec_start]; + int ret; + + /* fast path: do in-place encryption if only one record needs to be emitted */ + if (bodylen <= PTLS_MAX_PLAINTEXT_RECORD_SIZE) { + size_t overhead = 1 + enc->aead->algo->tag_size; + if ((ret = ptls_buffer_reserve(buf, overhead)) != 0) + return ret; + size_t encrypted_len = aead_encrypt(enc, buf->base + rec_start + 5, buf->base + rec_start + 5, bodylen, type); + assert(encrypted_len == bodylen + overhead); + buf->off += overhead; + buf->base[rec_start] = PTLS_CONTENT_TYPE_APPDATA; + buf->base[rec_start + 3] = (encrypted_len >> 8) & 0xff; + buf->base[rec_start + 4] = encrypted_len & 0xff; + return 0; + } + + /* move plaintext to temporary buffer */ + if ((tmpbuf = malloc(bodylen)) == NULL) { + ret = PTLS_ERROR_NO_MEMORY; + goto Exit; + } + memcpy(tmpbuf, buf->base + rec_start + 5, bodylen); + ptls_clear_memory(buf->base + rec_start, bodylen + 5); + buf->off = rec_start; + + /* push encrypted records */ + ret = buffer_push_encrypted_records(buf, type, tmpbuf, bodylen, enc); + +Exit: + if (tmpbuf != NULL) { + ptls_clear_memory(tmpbuf, bodylen); + free(tmpbuf); + } + return ret; +} + +static int begin_record_message(ptls_message_emitter_t *_self) +{ + struct st_ptls_record_message_emitter_t *self = (void *)_self; + int ret; + + self->rec_start = self->super.buf->off; + ptls_buffer_push(self->super.buf, PTLS_CONTENT_TYPE_HANDSHAKE, PTLS_RECORD_VERSION_MAJOR, PTLS_RECORD_VERSION_MINOR, 0, 0); + ret = 0; +Exit: + return ret; +} + +static int commit_record_message(ptls_message_emitter_t *_self) +{ + struct st_ptls_record_message_emitter_t *self = (void *)_self; + int ret; + + if (self->super.enc->aead != NULL) { + ret = buffer_encrypt_record(self->super.buf, self->rec_start, self->super.enc); + } else { + /* TODO allow CH,SH,HRR above 16KB */ + size_t sz = self->super.buf->off - self->rec_start - 5; + assert(sz <= PTLS_MAX_PLAINTEXT_RECORD_SIZE); + self->super.buf->base[self->rec_start + 3] = (uint8_t)(sz >> 8); + self->super.buf->base[self->rec_start + 4] = (uint8_t)(sz); + ret = 0; + } + + return ret; +} + +#define buffer_push_extension(buf, type, block) \ + do { \ + ptls_buffer_push16((buf), (type)); \ + ptls_buffer_push_block((buf), 2, block); \ + } while (0); + +#define decode_open_extensions(src, end, hstype, exttype, block) \ + do { \ + struct st_ptls_extension_bitmap_t bitmap; \ + init_extension_bitmap(&bitmap, (hstype)); \ + ptls_decode_open_block((src), end, 2, { \ + while ((src) != end) { \ + if ((ret = ptls_decode16((exttype), &(src), end)) != 0) \ + goto Exit; \ + if (extension_bitmap_is_set(&bitmap, *(exttype)) != 0) { \ + ret = PTLS_ALERT_ILLEGAL_PARAMETER; \ + goto Exit; \ + } \ + extension_bitmap_set(&bitmap, *(exttype)); \ + ptls_decode_open_block((src), end, 2, block); \ + } \ + }); \ + } while (0) + +#define decode_extensions(src, end, hstype, exttype, block) \ + do { \ + decode_open_extensions((src), end, hstype, exttype, block); \ + ptls_decode_assert_block_close((src), end); \ + } while (0) + +int ptls_decode16(uint16_t *value, const uint8_t **src, const uint8_t *end) +{ + if (end - *src < 2) + return PTLS_ALERT_DECODE_ERROR; + *value = ntoh16(*src); + *src += 2; + return 0; +} + +int ptls_decode24(uint32_t *value, const uint8_t **src, const uint8_t *end) +{ + if (end - *src < 3) + return PTLS_ALERT_DECODE_ERROR; + *value = ((uint32_t)(*src)[0] << 16) | ((uint32_t)(*src)[1] << 8) | (*src)[2]; + *src += 3; + return 0; +} + +int ptls_decode32(uint32_t *value, const uint8_t **src, const uint8_t *end) +{ + if (end - *src < 4) + return PTLS_ALERT_DECODE_ERROR; + *value = ntoh32(*src); + *src += 4; + return 0; +} + +int ptls_decode64(uint64_t *value, const uint8_t **src, const uint8_t *end) +{ + if (end - *src < 8) + return PTLS_ALERT_DECODE_ERROR; + *value = ntoh64(*src); + *src += 8; + return 0; +} + +uint64_t ptls_decode_quicint(const uint8_t **src, const uint8_t *end) +{ + if (PTLS_UNLIKELY(*src == end)) + return UINT64_MAX; + + uint8_t b = *(*src)++; + + if (PTLS_LIKELY(b <= 0x3f)) + return b; + + uint64_t v = b & 0x3f; + unsigned bytes_left = (1 << (b >> 6)) - 1; + if (PTLS_UNLIKELY((size_t)(end - *src) < bytes_left)) + return UINT64_MAX; + do { + v = (v << 8) | *(*src)++; + } while (--bytes_left != 0); + return v; +} + +static void log_secret(ptls_t *tls, const char *type, ptls_iovec_t secret) +{ + char hexbuf[PTLS_MAX_DIGEST_SIZE * 2 + 1]; + + PTLS_PROBE(NEW_SECRET, tls, type, ptls_hexdump(hexbuf, secret.base, secret.len)); + + if (tls->ctx->log_event != NULL) + tls->ctx->log_event->cb(tls->ctx->log_event, tls, type, "%s", ptls_hexdump(hexbuf, secret.base, secret.len)); +} + +static void key_schedule_free(ptls_key_schedule_t *sched) +{ + size_t i; + ptls_clear_memory(sched->secret, sizeof(sched->secret)); + for (i = 0; i != sched->num_hashes; ++i) + sched->hashes[i].ctx->final(sched->hashes[i].ctx, NULL, PTLS_HASH_FINAL_MODE_FREE); + free(sched); +} + +static ptls_key_schedule_t *key_schedule_new(ptls_cipher_suite_t *preferred, ptls_cipher_suite_t **offered, + const char *hkdf_label_prefix) +{ +#define FOREACH_HASH(block) \ + do { \ + ptls_cipher_suite_t *cs; \ + if ((cs = preferred) != NULL) { \ + block \ + } \ + if (offered != NULL) { \ + size_t i, j; \ + for (i = 0; (cs = offered[i]) != NULL; ++i) { \ + if (preferred == NULL || cs->hash != preferred->hash) { \ + for (j = 0; j != i; ++j) \ + if (cs->hash == offered[j]->hash) \ + break; \ + if (j == i) { \ + block \ + } \ + } \ + } \ + } \ + } while (0) + + ptls_key_schedule_t *sched; + + if (hkdf_label_prefix == NULL) + hkdf_label_prefix = PTLS_HKDF_EXPAND_LABEL_PREFIX; + + { /* allocate */ + size_t num_hashes = 0; + FOREACH_HASH({ ++num_hashes; }); + if ((sched = malloc(offsetof(ptls_key_schedule_t, hashes) + sizeof(sched->hashes[0]) * num_hashes)) == NULL) + return NULL; + *sched = (ptls_key_schedule_t){0, hkdf_label_prefix}; + } + + /* setup the hash algos and contexts */ + FOREACH_HASH({ + sched->hashes[sched->num_hashes].algo = cs->hash; + if ((sched->hashes[sched->num_hashes].ctx = cs->hash->create()) == NULL) + goto Fail; + ++sched->num_hashes; + }); + + return sched; +Fail: + key_schedule_free(sched); + return NULL; + +#undef FOREACH_HASH +} + +static int key_schedule_extract(ptls_key_schedule_t *sched, ptls_iovec_t ikm) +{ + int ret; + + if (ikm.base == NULL) + ikm = ptls_iovec_init(zeroes_of_max_digest_size, sched->hashes[0].algo->digest_size); + + if (sched->generation != 0 && + (ret = hkdf_expand_label(sched->hashes[0].algo, sched->secret, sched->hashes[0].algo->digest_size, + ptls_iovec_init(sched->secret, sched->hashes[0].algo->digest_size), "derived", + ptls_iovec_init(sched->hashes[0].algo->empty_digest, sched->hashes[0].algo->digest_size), + sched->hkdf_label_prefix)) != 0) + return ret; + + ++sched->generation; + ret = ptls_hkdf_extract(sched->hashes[0].algo, sched->secret, + ptls_iovec_init(sched->secret, sched->hashes[0].algo->digest_size), ikm); + PTLS_DEBUGF("%s: %u, %02x%02x\n", __FUNCTION__, sched->generation, (int)sched->secret[0], (int)sched->secret[1]); + return ret; +} + +static int key_schedule_select_one(ptls_key_schedule_t *sched, ptls_cipher_suite_t *cs, int reset) +{ + size_t found_slot = SIZE_MAX, i; + int ret; + + assert(sched->generation == 1); + + /* find the one, while freeing others */ + for (i = 0; i != sched->num_hashes; ++i) { + if (sched->hashes[i].algo == cs->hash) { + assert(found_slot == SIZE_MAX); + found_slot = i; + } else { + sched->hashes[i].ctx->final(sched->hashes[i].ctx, NULL, PTLS_HASH_FINAL_MODE_FREE); + } + } + if (found_slot != 0) { + sched->hashes[0] = sched->hashes[found_slot]; + reset = 1; + } + sched->num_hashes = 1; + + /* recalculate the hash if a different hash as been selected than the one we used for calculating the early secrets */ + if (reset) { + --sched->generation; + memset(sched->secret, 0, sizeof(sched->secret)); + if ((ret = key_schedule_extract(sched, ptls_iovec_init(NULL, 0))) != 0) + goto Exit; + } + + ret = 0; +Exit: + return ret; +} + +void ptls__key_schedule_update_hash(ptls_key_schedule_t *sched, const uint8_t *msg, size_t msglen) +{ + size_t i; + + PTLS_DEBUGF("%s:%zu\n", __FUNCTION__, msglen); + for (i = 0; i != sched->num_hashes; ++i) + sched->hashes[i].ctx->update(sched->hashes[i].ctx, msg, msglen); +} + +static void key_schedule_update_ch1hash_prefix(ptls_key_schedule_t *sched) +{ + uint8_t prefix[4] = {PTLS_HANDSHAKE_TYPE_MESSAGE_HASH, 0, 0, (uint8_t)sched->hashes[0].algo->digest_size}; + ptls__key_schedule_update_hash(sched, prefix, sizeof(prefix)); +} + +static void key_schedule_extract_ch1hash(ptls_key_schedule_t *sched, uint8_t *hash) +{ + sched->hashes[0].ctx->final(sched->hashes[0].ctx, hash, PTLS_HASH_FINAL_MODE_RESET); +} + +static void key_schedule_transform_post_ch1hash(ptls_key_schedule_t *sched) +{ + uint8_t ch1hash[PTLS_MAX_DIGEST_SIZE]; + + key_schedule_extract_ch1hash(sched, ch1hash); + + key_schedule_update_ch1hash_prefix(sched); + ptls__key_schedule_update_hash(sched, ch1hash, sched->hashes[0].algo->digest_size); +} + +static int derive_secret_with_hash(ptls_key_schedule_t *sched, void *secret, const char *label, const uint8_t *hash) +{ + int ret = hkdf_expand_label(sched->hashes[0].algo, secret, sched->hashes[0].algo->digest_size, + ptls_iovec_init(sched->secret, sched->hashes[0].algo->digest_size), label, + ptls_iovec_init(hash, sched->hashes[0].algo->digest_size), sched->hkdf_label_prefix); + PTLS_DEBUGF("%s: (label=%s, hash=%02x%02x) => %02x%02x\n", __FUNCTION__, label, hash[0], hash[1], ((uint8_t *)secret)[0], + ((uint8_t *)secret)[1]); + return ret; +} + +static int derive_secret(ptls_key_schedule_t *sched, void *secret, const char *label) +{ + uint8_t hash_value[PTLS_MAX_DIGEST_SIZE]; + + sched->hashes[0].ctx->final(sched->hashes[0].ctx, hash_value, PTLS_HASH_FINAL_MODE_SNAPSHOT); + int ret = derive_secret_with_hash(sched, secret, label, hash_value); + ptls_clear_memory(hash_value, sizeof(hash_value)); + return ret; +} + +static int derive_secret_with_empty_digest(ptls_key_schedule_t *sched, void *secret, const char *label) +{ + return derive_secret_with_hash(sched, secret, label, sched->hashes[0].algo->empty_digest); +} + +static int derive_exporter_secret(ptls_t *tls, int is_early) +{ + int ret; + + if (!tls->ctx->use_exporter) + return 0; + + uint8_t **slot = is_early ? &tls->exporter_master_secret.early : &tls->exporter_master_secret.one_rtt; + assert(*slot == NULL); + if ((*slot = malloc(tls->key_schedule->hashes[0].algo->digest_size)) == NULL) + return PTLS_ERROR_NO_MEMORY; + + if ((ret = derive_secret(tls->key_schedule, *slot, is_early ? "e exp master" : "exp master")) != 0) + return ret; + + log_secret(tls, is_early ? "EARLY_EXPORTER_SECRET" : "EXPORTER_SECRET", + ptls_iovec_init(*slot, tls->key_schedule->hashes[0].algo->digest_size)); + + return 0; +} + +static void free_exporter_master_secret(ptls_t *tls, int is_early) +{ + uint8_t *slot = is_early ? tls->exporter_master_secret.early : tls->exporter_master_secret.one_rtt; + if (slot == NULL) + return; + assert(tls->key_schedule != NULL); + ptls_clear_memory(slot, tls->key_schedule->hashes[0].algo->digest_size); + free(slot); +} + +static int derive_resumption_secret(ptls_key_schedule_t *sched, uint8_t *secret, ptls_iovec_t nonce) +{ + int ret; + + if ((ret = derive_secret(sched, secret, "res master")) != 0) + goto Exit; + if ((ret = hkdf_expand_label(sched->hashes[0].algo, secret, sched->hashes[0].algo->digest_size, + ptls_iovec_init(secret, sched->hashes[0].algo->digest_size), "resumption", nonce, + sched->hkdf_label_prefix)) != 0) + goto Exit; + +Exit: + if (ret != 0) + ptls_clear_memory(secret, sched->hashes[0].algo->digest_size); + return ret; +} + +static int decode_new_session_ticket(ptls_t *tls, uint32_t *lifetime, uint32_t *age_add, ptls_iovec_t *nonce, ptls_iovec_t *ticket, + uint32_t *max_early_data_size, const uint8_t *src, const uint8_t *const end) +{ + uint16_t exttype; + int ret; + + if ((ret = ptls_decode32(lifetime, &src, end)) != 0) + goto Exit; + if ((ret = ptls_decode32(age_add, &src, end)) != 0) + goto Exit; + ptls_decode_open_block(src, end, 1, { + *nonce = ptls_iovec_init(src, end - src); + src = end; + }); + ptls_decode_open_block(src, end, 2, { + if (src == end) { + ret = PTLS_ALERT_DECODE_ERROR; + goto Exit; + } + *ticket = ptls_iovec_init(src, end - src); + src = end; + }); + + *max_early_data_size = 0; + decode_extensions(src, end, PTLS_HANDSHAKE_TYPE_NEW_SESSION_TICKET, &exttype, { + if (tls->ctx->on_extension != NULL && + (ret = tls->ctx->on_extension->cb(tls->ctx->on_extension, tls, PTLS_HANDSHAKE_TYPE_NEW_SESSION_TICKET, exttype, + ptls_iovec_init(src, end - src)) != 0)) + goto Exit; + switch (exttype) { + case PTLS_EXTENSION_TYPE_EARLY_DATA: + if ((ret = ptls_decode32(max_early_data_size, &src, end)) != 0) + goto Exit; + break; + default: + src = end; + break; + } + }); + + ret = 0; +Exit: + return ret; +} + +static int decode_stored_session_ticket(ptls_t *tls, ptls_key_exchange_algorithm_t **key_share, ptls_cipher_suite_t **cs, + ptls_iovec_t *secret, uint32_t *obfuscated_ticket_age, ptls_iovec_t *ticket, + uint32_t *max_early_data_size, const uint8_t *src, const uint8_t *const end) +{ + uint16_t kxid, csid; + uint32_t lifetime, age_add; + uint64_t obtained_at, now; + ptls_iovec_t nonce; + int ret; + + /* decode */ + if ((ret = ptls_decode64(&obtained_at, &src, end)) != 0) + goto Exit; + if ((ret = ptls_decode16(&kxid, &src, end)) != 0) + goto Exit; + if ((ret = ptls_decode16(&csid, &src, end)) != 0) + goto Exit; + ptls_decode_open_block(src, end, 3, { + if ((ret = decode_new_session_ticket(tls, &lifetime, &age_add, &nonce, ticket, max_early_data_size, src, end)) != 0) + goto Exit; + src = end; + }); + ptls_decode_block(src, end, 2, { + *secret = ptls_iovec_init(src, end - src); + src = end; + }); + + { /* determine the key-exchange */ + ptls_key_exchange_algorithm_t **cand; + for (cand = tls->ctx->key_exchanges; *cand != NULL; ++cand) + if ((*cand)->id == kxid) + break; + if (*cand == NULL) { + ret = PTLS_ERROR_LIBRARY; + goto Exit; + } + *key_share = *cand; + } + + { /* determine the cipher-suite */ + ptls_cipher_suite_t **cand; + for (cand = tls->ctx->cipher_suites; *cand != NULL; ++cand) + if ((*cand)->id == csid) + break; + if (*cand == NULL) { + ret = PTLS_ERROR_LIBRARY; + goto Exit; + } + *cs = *cand; + } + + /* calculate obfuscated_ticket_age */ + now = tls->ctx->get_time->cb(tls->ctx->get_time); + if (!(obtained_at <= now && now - obtained_at < 7 * 86400 * 1000)) { + ret = PTLS_ERROR_LIBRARY; + goto Exit; + } + *obfuscated_ticket_age = (uint32_t)(now - obtained_at) + age_add; + + ret = 0; +Exit: + return ret; +} + +static int get_traffic_key(ptls_hash_algorithm_t *algo, void *key, size_t key_size, int is_iv, const void *secret, + ptls_iovec_t hash_value, const char *label_prefix) +{ + return ptls_hkdf_expand_label(algo, key, key_size, ptls_iovec_init(secret, algo->digest_size), is_iv ? "iv" : "key", hash_value, + label_prefix); +} + +static int setup_traffic_protection(ptls_t *tls, int is_enc, const char *secret_label, size_t epoch, int skip_notify) +{ + static const char *log_labels[2][4] = { + {NULL, "CLIENT_EARLY_TRAFFIC_SECRET", "CLIENT_HANDSHAKE_TRAFFIC_SECRET", "CLIENT_TRAFFIC_SECRET_0"}, + {NULL, NULL, "SERVER_HANDSHAKE_TRAFFIC_SECRET", "SERVER_TRAFFIC_SECRET_0"}}; + struct st_ptls_traffic_protection_t *ctx = is_enc ? &tls->traffic_protection.enc : &tls->traffic_protection.dec; + + if (secret_label != NULL) { + int ret; + if ((ret = derive_secret(tls->key_schedule, ctx->secret, secret_label)) != 0) + return ret; + } + + ctx->epoch = epoch; + + /* special path for applications having their own record layer */ + if (tls->ctx->update_traffic_key != NULL) { + if (skip_notify) + return 0; + return tls->ctx->update_traffic_key->cb(tls->ctx->update_traffic_key, tls, is_enc, epoch, ctx->secret); + } + + if (ctx->aead != NULL) + ptls_aead_free(ctx->aead); + if ((ctx->aead = ptls_aead_new(tls->cipher_suite->aead, tls->cipher_suite->hash, is_enc, ctx->secret, + tls->ctx->hkdf_label_prefix__obsolete)) == NULL) + return PTLS_ERROR_NO_MEMORY; /* TODO obtain error from ptls_aead_new */ + ctx->seq = 0; + + log_secret(tls, log_labels[ptls_is_server(tls) == is_enc][epoch], + ptls_iovec_init(ctx->secret, tls->key_schedule->hashes[0].algo->digest_size)); + PTLS_DEBUGF("[%s] %02x%02x,%02x%02x\n", log_labels[ptls_is_server(tls)][epoch], (unsigned)ctx->secret[0], + (unsigned)ctx->secret[1], (unsigned)ctx->aead->static_iv[0], (unsigned)ctx->aead->static_iv[1]); + + return 0; +} + +static int commission_handshake_secret(ptls_t *tls) +{ + int is_enc = !ptls_is_server(tls); + + assert(tls->pending_handshake_secret != NULL); + memcpy((is_enc ? &tls->traffic_protection.enc : &tls->traffic_protection.dec)->secret, tls->pending_handshake_secret, + PTLS_MAX_DIGEST_SIZE); + ptls_clear_memory(tls->pending_handshake_secret, PTLS_MAX_DIGEST_SIZE); + free(tls->pending_handshake_secret); + tls->pending_handshake_secret = NULL; + + return setup_traffic_protection(tls, is_enc, NULL, 2, 1); +} + +static void log_client_random(ptls_t *tls) +{ + PTLS_PROBE(CLIENT_RANDOM, tls, + ptls_hexdump(alloca(sizeof(tls->client_random) * 2 + 1), tls->client_random, sizeof(tls->client_random))); +} + +#define SESSION_IDENTIFIER_MAGIC "ptls0001" /* the number should be changed upon incompatible format change */ +#define SESSION_IDENTIFIER_MAGIC_SIZE (sizeof(SESSION_IDENTIFIER_MAGIC) - 1) + +static int encode_session_identifier(ptls_context_t *ctx, ptls_buffer_t *buf, uint32_t ticket_age_add, ptls_iovec_t ticket_nonce, + ptls_key_schedule_t *sched, const char *server_name, uint16_t key_exchange_id, uint16_t csid, + const char *negotiated_protocol) +{ + int ret = 0; + + ptls_buffer_push_block(buf, 2, { + /* format id */ + ptls_buffer_pushv(buf, SESSION_IDENTIFIER_MAGIC, SESSION_IDENTIFIER_MAGIC_SIZE); + /* date */ + ptls_buffer_push64(buf, ctx->get_time->cb(ctx->get_time)); + /* resumption master secret */ + ptls_buffer_push_block(buf, 2, { + if ((ret = ptls_buffer_reserve(buf, sched->hashes[0].algo->digest_size)) != 0) + goto Exit; + if ((ret = derive_resumption_secret(sched, buf->base + buf->off, ticket_nonce)) != 0) + goto Exit; + buf->off += sched->hashes[0].algo->digest_size; + }); + /* key-exchange */ + ptls_buffer_push16(buf, key_exchange_id); + /* cipher-suite */ + ptls_buffer_push16(buf, csid); + /* ticket_age_add */ + ptls_buffer_push32(buf, ticket_age_add); + /* server-name */ + ptls_buffer_push_block(buf, 2, { + if (server_name != NULL) + ptls_buffer_pushv(buf, server_name, strlen(server_name)); + }); + /* alpn */ + ptls_buffer_push_block(buf, 1, { + if (negotiated_protocol != NULL) + ptls_buffer_pushv(buf, negotiated_protocol, strlen(negotiated_protocol)); + }); + }); + +Exit: + return ret; +} + +int decode_session_identifier(uint64_t *issued_at, ptls_iovec_t *psk, uint32_t *ticket_age_add, ptls_iovec_t *server_name, + uint16_t *key_exchange_id, uint16_t *csid, ptls_iovec_t *negotiated_protocol, const uint8_t *src, + const uint8_t *const end) +{ + int ret = 0; + + ptls_decode_block(src, end, 2, { + if (end - src < SESSION_IDENTIFIER_MAGIC_SIZE || + memcmp(src, SESSION_IDENTIFIER_MAGIC, SESSION_IDENTIFIER_MAGIC_SIZE) != 0) { + ret = PTLS_ALERT_DECODE_ERROR; + goto Exit; + } + src += SESSION_IDENTIFIER_MAGIC_SIZE; + if ((ret = ptls_decode64(issued_at, &src, end)) != 0) + goto Exit; + ptls_decode_open_block(src, end, 2, { + *psk = ptls_iovec_init(src, end - src); + src = end; + }); + if ((ret = ptls_decode16(key_exchange_id, &src, end)) != 0) + goto Exit; + if ((ret = ptls_decode16(csid, &src, end)) != 0) + goto Exit; + if ((ret = ptls_decode32(ticket_age_add, &src, end)) != 0) + goto Exit; + ptls_decode_open_block(src, end, 2, { + *server_name = ptls_iovec_init(src, end - src); + src = end; + }); + ptls_decode_open_block(src, end, 1, { + *negotiated_protocol = ptls_iovec_init(src, end - src); + src = end; + }); + }); + +Exit: + return ret; +} + +static size_t build_certificate_verify_signdata(uint8_t *data, ptls_key_schedule_t *sched, const char *context_string) +{ + size_t datalen = 0; + + memset(data + datalen, 32, 64); + datalen += 64; + memcpy(data + datalen, context_string, strlen(context_string) + 1); + datalen += strlen(context_string) + 1; + sched->hashes[0].ctx->final(sched->hashes[0].ctx, data + datalen, PTLS_HASH_FINAL_MODE_SNAPSHOT); + datalen += sched->hashes[0].algo->digest_size; + assert(datalen <= PTLS_MAX_CERTIFICATE_VERIFY_SIGNDATA_SIZE); + + return datalen; +} + +static int calc_verify_data(void *output, ptls_key_schedule_t *sched, const void *secret) +{ + ptls_hash_context_t *hmac; + uint8_t digest[PTLS_MAX_DIGEST_SIZE]; + int ret; + + if ((ret = hkdf_expand_label(sched->hashes[0].algo, digest, sched->hashes[0].algo->digest_size, + ptls_iovec_init(secret, sched->hashes[0].algo->digest_size), "finished", ptls_iovec_init(NULL, 0), + sched->hkdf_label_prefix)) != 0) + return ret; + if ((hmac = ptls_hmac_create(sched->hashes[0].algo, digest, sched->hashes[0].algo->digest_size)) == NULL) { + ptls_clear_memory(digest, sizeof(digest)); + return PTLS_ERROR_NO_MEMORY; + } + + sched->hashes[0].ctx->final(sched->hashes[0].ctx, digest, PTLS_HASH_FINAL_MODE_SNAPSHOT); + PTLS_DEBUGF("%s: %02x%02x,%02x%02x\n", __FUNCTION__, ((uint8_t *)secret)[0], ((uint8_t *)secret)[1], digest[0], digest[1]); + hmac->update(hmac, digest, sched->hashes[0].algo->digest_size); + ptls_clear_memory(digest, sizeof(digest)); + hmac->final(hmac, output, PTLS_HASH_FINAL_MODE_FREE); + + return 0; +} + +static int verify_finished(ptls_t *tls, ptls_iovec_t message) +{ + uint8_t verify_data[PTLS_MAX_DIGEST_SIZE]; + int ret; + + if (PTLS_HANDSHAKE_HEADER_SIZE + tls->key_schedule->hashes[0].algo->digest_size != message.len) { + ret = PTLS_ALERT_DECODE_ERROR; + goto Exit; + } + + if ((ret = calc_verify_data(verify_data, tls->key_schedule, tls->traffic_protection.dec.secret)) != 0) + goto Exit; + if (!ptls_mem_equal(message.base + PTLS_HANDSHAKE_HEADER_SIZE, verify_data, tls->key_schedule->hashes[0].algo->digest_size)) { + ret = PTLS_ALERT_HANDSHAKE_FAILURE; + goto Exit; + } + +Exit: + ptls_clear_memory(verify_data, sizeof(verify_data)); + return ret; +} + +static int send_finished(ptls_t *tls, ptls_message_emitter_t *emitter) +{ + int ret; + + ptls_push_message(emitter, tls->key_schedule, PTLS_HANDSHAKE_TYPE_FINISHED, { + if ((ret = ptls_buffer_reserve(emitter->buf, tls->key_schedule->hashes[0].algo->digest_size)) != 0) + goto Exit; + if ((ret = calc_verify_data(emitter->buf->base + emitter->buf->off, tls->key_schedule, + tls->traffic_protection.enc.secret)) != 0) + goto Exit; + emitter->buf->off += tls->key_schedule->hashes[0].algo->digest_size; + }); + +Exit: + return ret; +} + +static int send_session_ticket(ptls_t *tls, ptls_message_emitter_t *emitter) +{ + ptls_hash_context_t *msghash_backup = tls->key_schedule->hashes[0].ctx->clone_(tls->key_schedule->hashes[0].ctx); + ptls_buffer_t session_id; + char session_id_smallbuf[128]; + uint32_t ticket_age_add; + int ret = 0; + + assert(tls->ctx->ticket_lifetime != 0); + assert(tls->ctx->encrypt_ticket != NULL); + + { /* calculate verify-data that will be sent by the client */ + size_t orig_off = emitter->buf->off; + if (tls->pending_handshake_secret != NULL && !tls->ctx->omit_end_of_early_data) { + assert(tls->state == PTLS_STATE_SERVER_EXPECT_END_OF_EARLY_DATA); + ptls_buffer_push_message_body(emitter->buf, tls->key_schedule, PTLS_HANDSHAKE_TYPE_END_OF_EARLY_DATA, {}); + emitter->buf->off = orig_off; + } + ptls_buffer_push_message_body(emitter->buf, tls->key_schedule, PTLS_HANDSHAKE_TYPE_FINISHED, { + if ((ret = ptls_buffer_reserve(emitter->buf, tls->key_schedule->hashes[0].algo->digest_size)) != 0) + goto Exit; + if ((ret = calc_verify_data(emitter->buf->base + emitter->buf->off, tls->key_schedule, + tls->pending_handshake_secret != NULL ? tls->pending_handshake_secret + : tls->traffic_protection.dec.secret)) != 0) + goto Exit; + emitter->buf->off += tls->key_schedule->hashes[0].algo->digest_size; + }); + emitter->buf->off = orig_off; + } + + tls->ctx->random_bytes(&ticket_age_add, sizeof(ticket_age_add)); + + /* build the raw nsk */ + ptls_buffer_init(&session_id, session_id_smallbuf, sizeof(session_id_smallbuf)); + ret = encode_session_identifier(tls->ctx, &session_id, ticket_age_add, ptls_iovec_init(NULL, 0), tls->key_schedule, + tls->server_name, tls->key_share->id, tls->cipher_suite->id, tls->negotiated_protocol); + if (ret != 0) + goto Exit; + + /* encrypt and send */ + ptls_push_message(emitter, tls->key_schedule, PTLS_HANDSHAKE_TYPE_NEW_SESSION_TICKET, { + ptls_buffer_push32(emitter->buf, tls->ctx->ticket_lifetime); + ptls_buffer_push32(emitter->buf, ticket_age_add); + ptls_buffer_push_block(emitter->buf, 1, {}); + ptls_buffer_push_block(emitter->buf, 2, { + if ((ret = tls->ctx->encrypt_ticket->cb(tls->ctx->encrypt_ticket, tls, 1, emitter->buf, + ptls_iovec_init(session_id.base, session_id.off))) != 0) + goto Exit; + }); + ptls_buffer_push_block(emitter->buf, 2, { + if (tls->ctx->max_early_data_size != 0) + buffer_push_extension(emitter->buf, PTLS_EXTENSION_TYPE_EARLY_DATA, + { ptls_buffer_push32(emitter->buf, tls->ctx->max_early_data_size); }); + }); + }); + +Exit: + ptls_buffer_dispose(&session_id); + + /* restore handshake state */ + tls->key_schedule->hashes[0].ctx->final(tls->key_schedule->hashes[0].ctx, NULL, PTLS_HASH_FINAL_MODE_FREE); + tls->key_schedule->hashes[0].ctx = msghash_backup; + + return ret; +} + +static int push_change_cipher_spec(ptls_t *tls, ptls_message_emitter_t *emitter) +{ + int ret; + + /* check if we are requested to (or still need to) */ + if (!tls->send_change_cipher_spec) { + ret = 0; + goto Exit; + } + + /* CCS is a record, can only be sent when using a record-based protocol. */ + if (emitter->begin_message != begin_record_message) { + ret = PTLS_ALERT_UNEXPECTED_MESSAGE; + goto Exit; + } + + /* emit CCS */ + buffer_push_record(emitter->buf, PTLS_CONTENT_TYPE_CHANGE_CIPHER_SPEC, { ptls_buffer_push(emitter->buf, 1); }); + + tls->send_change_cipher_spec = 0; + ret = 0; +Exit: + return ret; +} + +static int push_additional_extensions(ptls_handshake_properties_t *properties, ptls_buffer_t *sendbuf) +{ + int ret; + + if (properties != NULL && properties->additional_extensions != NULL) { + ptls_raw_extension_t *ext; + for (ext = properties->additional_extensions; ext->type != UINT16_MAX; ++ext) { + buffer_push_extension(sendbuf, ext->type, { ptls_buffer_pushv(sendbuf, ext->data.base, ext->data.len); }); + } + } + ret = 0; +Exit: + return ret; +} + +static int push_signature_algorithms(ptls_buffer_t *sendbuf) +{ + int ret; + + ptls_buffer_push_block(sendbuf, 2, { + ptls_buffer_push16(sendbuf, PTLS_SIGNATURE_RSA_PSS_RSAE_SHA256); + ptls_buffer_push16(sendbuf, PTLS_SIGNATURE_ECDSA_SECP256R1_SHA256); + ptls_buffer_push16(sendbuf, PTLS_SIGNATURE_RSA_PKCS1_SHA256); + ptls_buffer_push16(sendbuf, PTLS_SIGNATURE_RSA_PKCS1_SHA1); + }); + + ret = 0; +Exit: + return ret; +} + +static int decode_signature_algorithms(struct st_ptls_signature_algorithms_t *sa, const uint8_t **src, const uint8_t *end) +{ + int ret; + + ptls_decode_block(*src, end, 2, { + do { + uint16_t id; + if ((ret = ptls_decode16(&id, src, end)) != 0) + goto Exit; + if (sa->count < PTLS_ELEMENTSOF(sa->list)) + sa->list[sa->count++] = id; + } while (*src != end); + }); + + ret = 0; +Exit: + return ret; +} + +static ptls_hash_context_t *create_sha256_context(ptls_context_t *ctx) +{ + ptls_cipher_suite_t **cs; + + for (cs = ctx->cipher_suites; *cs != NULL; ++cs) { + switch ((*cs)->id) { + case PTLS_CIPHER_SUITE_AES_128_GCM_SHA256: + case PTLS_CIPHER_SUITE_CHACHA20_POLY1305_SHA256: + return (*cs)->hash->create(); + } + } + + return NULL; +} + +static int select_cipher(ptls_cipher_suite_t **selected, ptls_cipher_suite_t **candidates, const uint8_t *src, + const uint8_t *const end) +{ + int ret; + + while (src != end) { + uint16_t id; + if ((ret = ptls_decode16(&id, &src, end)) != 0) + goto Exit; + ptls_cipher_suite_t **c = candidates; + for (; *c != NULL; ++c) { + if ((*c)->id == id) { + *selected = *c; + return 0; + } + } + } + + ret = PTLS_ALERT_HANDSHAKE_FAILURE; + +Exit: + return ret; +} + +static int push_key_share_entry(ptls_buffer_t *buf, uint16_t group, ptls_iovec_t pubkey) +{ + int ret; + + ptls_buffer_push16(buf, group); + ptls_buffer_push_block(buf, 2, { ptls_buffer_pushv(buf, pubkey.base, pubkey.len); }); + ret = 0; +Exit: + return ret; +} + +static int decode_key_share_entry(uint16_t *group, ptls_iovec_t *key_exchange, const uint8_t **src, const uint8_t *const end) +{ + int ret; + + if ((ret = ptls_decode16(group, src, end)) != 0) + goto Exit; + ptls_decode_open_block(*src, end, 2, { + *key_exchange = ptls_iovec_init(*src, end - *src); + *src = end; + }); + +Exit: + return ret; +} + +static int select_key_share(ptls_key_exchange_algorithm_t **selected, ptls_iovec_t *peer_key, + ptls_key_exchange_algorithm_t **candidates, const uint8_t **src, const uint8_t *const end, + int expect_one) +{ + int ret; + + *selected = NULL; + + if (expect_one && *src == end) { + ret = PTLS_ALERT_ILLEGAL_PARAMETER; + goto Exit; + } + + while (*src != end) { + uint16_t group; + ptls_iovec_t key; + if ((ret = decode_key_share_entry(&group, &key, src, end)) != 0) + goto Exit; + ptls_key_exchange_algorithm_t **c = candidates; + for (; *c != NULL; ++c) { + if (*selected == NULL && (*c)->id == group) { + *selected = *c; + *peer_key = key; + } + } + if (expect_one) { + ret = *selected != NULL ? 0 : PTLS_ALERT_ILLEGAL_PARAMETER; + goto Exit; + } + } + + ret = 0; + +Exit: + return ret; +} + +static int emit_server_name_extension(ptls_buffer_t *buf, const char *server_name) +{ + int ret; + + ptls_buffer_push_block(buf, 2, { + ptls_buffer_push(buf, PTLS_SERVER_NAME_TYPE_HOSTNAME); + ptls_buffer_push_block(buf, 2, { ptls_buffer_pushv(buf, server_name, strlen(server_name)); }); + }); + + ret = 0; +Exit: + return ret; +} + +static int parse_esni_keys(ptls_context_t *ctx, uint16_t *esni_version, ptls_key_exchange_algorithm_t **selected_key_share, + ptls_cipher_suite_t **selected_cipher, ptls_iovec_t *peer_key, uint16_t *padded_length, + char **published_sni, ptls_iovec_t input) +{ + const uint8_t *src = input.base, *const end = input.base + input.len; + uint16_t version; + uint64_t not_before, not_after, now; + int ret = 0; + + /* version */ + if ((ret = ptls_decode16(&version, &src, end)) != 0) + goto Exit; + if (version != PTLS_ESNI_VERSION_DRAFT03) { + ret = PTLS_ALERT_DECODE_ERROR; + goto Exit; + } + + { /* verify checksum */ + ptls_hash_context_t *hctx; + uint8_t digest[PTLS_SHA256_DIGEST_SIZE]; + if (end - src < 4) { + ret = PTLS_ALERT_DECODE_ERROR; + goto Exit; + } + if ((hctx = create_sha256_context(ctx)) == NULL) { + ret = PTLS_ERROR_LIBRARY; + goto Exit; + } + hctx->update(hctx, input.base, src - input.base); + hctx->update(hctx, "\0\0\0\0", 4); + hctx->update(hctx, src + 4, end - (src + 4)); + hctx->final(hctx, digest, PTLS_HASH_FINAL_MODE_FREE); + if (memcmp(src, digest, 4) != 0) { + ret = PTLS_ALERT_DECODE_ERROR; + goto Exit; + } + src += 4; + } + *esni_version = version; + /* published sni */ + ptls_decode_open_block(src, end, 2, { + size_t len = end - src; + *published_sni = malloc(len + 1); + if (*published_sni == NULL) { + ret = PTLS_ERROR_NO_MEMORY; + goto Exit; + } + if (len > 0) { + memcpy(*published_sni, src, len); + } + (*published_sni)[len] = 0; + src = end; + }); + /* key-shares */ + ptls_decode_open_block(src, end, 2, { + if ((ret = select_key_share(selected_key_share, peer_key, ctx->key_exchanges, &src, end, 0)) != 0) + goto Exit; + }); + /* cipher-suite */ + ptls_decode_open_block(src, end, 2, { + if ((ret = select_cipher(selected_cipher, ctx->cipher_suites, src, end)) != 0) + goto Exit; + src = end; + }); + /* padded-length */ + if ((ret = ptls_decode16(padded_length, &src, end)) != 0) + goto Exit; + if (padded_length == 0) + goto Exit; + /* not-before, not_after */ + if ((ret = ptls_decode64(¬_before, &src, end)) != 0 || (ret = ptls_decode64(¬_after, &src, end)) != 0) + goto Exit; + /* extensions */ + ptls_decode_block(src, end, 2, { + while (src != end) { + uint16_t id; + if ((ret = ptls_decode16(&id, &src, end)) != 0) + goto Exit; + ptls_decode_open_block(src, end, 2, { src = end; }); + } + }); + + /* check validity period */ + now = ctx->get_time->cb(ctx->get_time); + if (!(not_before * 1000 <= now && now <= not_after * 1000)) { + ret = PTLS_ALERT_DECODE_ERROR; + goto Exit; + } + + ret = 0; +Exit: + return ret; +} + +static int create_esni_aead(ptls_aead_context_t **aead_ctx, int is_enc, ptls_cipher_suite_t *cipher, ptls_iovec_t ecdh_secret, + const uint8_t *esni_contents_hash) +{ + uint8_t aead_secret[PTLS_MAX_DIGEST_SIZE]; + int ret; + + if ((ret = ptls_hkdf_extract(cipher->hash, aead_secret, ptls_iovec_init(NULL, 0), ecdh_secret)) != 0) + goto Exit; + if ((*aead_ctx = new_aead(cipher->aead, cipher->hash, is_enc, aead_secret, + ptls_iovec_init(esni_contents_hash, cipher->hash->digest_size), "tls13 esni ")) == NULL) { + ret = PTLS_ERROR_NO_MEMORY; + goto Exit; + } + + ret = 0; +Exit: + ptls_clear_memory(aead_secret, sizeof(aead_secret)); + return ret; +} + +static int build_esni_contents_hash(ptls_hash_algorithm_t *hash, uint8_t *digest, const uint8_t *record_digest, uint16_t group, + ptls_iovec_t pubkey, const uint8_t *client_random) +{ + ptls_buffer_t buf; + uint8_t smallbuf[256]; + int ret; + + /* build ESNIContents */ + ptls_buffer_init(&buf, smallbuf, sizeof(smallbuf)); + ptls_buffer_push_block(&buf, 2, { ptls_buffer_pushv(&buf, record_digest, hash->digest_size); }); + if ((ret = push_key_share_entry(&buf, group, pubkey)) != 0) + goto Exit; + ptls_buffer_pushv(&buf, client_random, PTLS_HELLO_RANDOM_SIZE); + + /* calculate digest */ + if ((ret = ptls_calc_hash(hash, digest, buf.base, buf.off)) != 0) + goto Exit; + + ret = 0; +Exit: + ptls_buffer_dispose(&buf); + return ret; +} + +static void free_esni_secret(ptls_esni_secret_t **esni, int is_server) +{ + assert(*esni != NULL); + if ((*esni)->secret.base != NULL) { + ptls_clear_memory((*esni)->secret.base, (*esni)->secret.len); + free((*esni)->secret.base); + } + if (!is_server) + free((*esni)->client.pubkey.base); + ptls_clear_memory((*esni), sizeof(**esni)); + free(*esni); + *esni = NULL; +} + +static int client_setup_esni(ptls_context_t *ctx, ptls_esni_secret_t **esni, ptls_iovec_t esni_keys, char **published_sni, + const uint8_t *client_random) +{ + ptls_iovec_t peer_key; + int ret; + + if ((*esni = malloc(sizeof(**esni))) == NULL) + return PTLS_ERROR_NO_MEMORY; + memset(*esni, 0, sizeof(**esni)); + + /* parse ESNI_Keys (and return success while keeping *esni NULL) */ + if (parse_esni_keys(ctx, &(*esni)->version, &(*esni)->client.key_share, &(*esni)->client.cipher, &peer_key, + &(*esni)->client.padded_length, published_sni, esni_keys) != 0) { + free(*esni); + *esni = NULL; + return 0; + } + + ctx->random_bytes((*esni)->nonce, sizeof((*esni)->nonce)); + + /* calc record digest */ + if ((ret = ptls_calc_hash((*esni)->client.cipher->hash, (*esni)->client.record_digest, esni_keys.base, esni_keys.len)) != 0) + goto Exit; + /* derive ECDH secret */ + if ((ret = (*esni)->client.key_share->exchange((*esni)->client.key_share, &(*esni)->client.pubkey, &(*esni)->secret, + peer_key)) != 0) + goto Exit; + /* calc H(ESNIContents) */ + if ((ret = build_esni_contents_hash((*esni)->client.cipher->hash, (*esni)->esni_contents_hash, (*esni)->client.record_digest, + (*esni)->client.key_share->id, (*esni)->client.pubkey, client_random)) != 0) + goto Exit; + + ret = 0; +Exit: + if (ret != 0) + free_esni_secret(esni, 0); + return ret; +} + +static int emit_esni_extension(ptls_esni_secret_t *esni, ptls_buffer_t *buf, ptls_iovec_t esni_keys, const char *server_name, + size_t key_share_ch_off, size_t key_share_ch_len) +{ + ptls_aead_context_t *aead = NULL; + int ret; + + if ((ret = create_esni_aead(&aead, 1, esni->client.cipher, esni->secret, esni->esni_contents_hash)) != 0) + goto Exit; + + /* cipher-suite id */ + ptls_buffer_push16(buf, esni->client.cipher->id); + /* key-share */ + if ((ret = push_key_share_entry(buf, esni->client.key_share->id, esni->client.pubkey)) != 0) + goto Exit; + /* record-digest */ + ptls_buffer_push_block(buf, 2, { ptls_buffer_pushv(buf, esni->client.record_digest, esni->client.cipher->hash->digest_size); }); + /* encrypted sni */ + ptls_buffer_push_block(buf, 2, { + size_t start_off = buf->off; + /* nonce */ + ptls_buffer_pushv(buf, esni->nonce, PTLS_ESNI_NONCE_SIZE); + /* emit server-name extension */ + if ((ret = emit_server_name_extension(buf, server_name)) != 0) + goto Exit; + /* pad */ + if (buf->off - start_off < (size_t)(esni->client.padded_length + PTLS_ESNI_NONCE_SIZE)) { + size_t bytes_to_pad = esni->client.padded_length + PTLS_ESNI_NONCE_SIZE - (buf->off - start_off); + if ((ret = ptls_buffer_reserve(buf, bytes_to_pad)) != 0) + goto Exit; + memset(buf->base + buf->off, 0, bytes_to_pad); + buf->off += bytes_to_pad; + } + /* encrypt */ + if ((ret = ptls_buffer_reserve(buf, aead->algo->tag_size)) != 0) + goto Exit; + ptls_aead_encrypt(aead, buf->base + start_off, buf->base + start_off, buf->off - start_off, 0, buf->base + key_share_ch_off, + key_share_ch_len); + buf->off += aead->algo->tag_size; + }); + + ret = 0; +Exit: + if (aead != NULL) + ptls_aead_free(aead); + return ret; +} + +static int send_client_hello(ptls_t *tls, ptls_message_emitter_t *emitter, ptls_handshake_properties_t *properties, + ptls_iovec_t *cookie) +{ + ptls_iovec_t resumption_secret = {NULL}, resumption_ticket; + char *published_sni = NULL; + uint32_t obfuscated_ticket_age = 0; + size_t msghash_off; + uint8_t binder_key[PTLS_MAX_DIGEST_SIZE]; + int ret, is_second_flight = tls->key_schedule != NULL, + send_sni = tls->server_name != NULL && !ptls_server_name_is_ipaddr(tls->server_name); + + if (properties != NULL) { + /* try to use ESNI */ + if (!is_second_flight && send_sni && properties->client.esni_keys.base != NULL) { + if ((ret = client_setup_esni(tls->ctx, &tls->esni, properties->client.esni_keys, &published_sni, tls->client_random)) != + 0) { + goto Exit; + } + if (tls->ctx->update_esni_key != NULL) { + if ((ret = tls->ctx->update_esni_key->cb(tls->ctx->update_esni_key, tls, tls->esni->secret, + tls->esni->client.cipher->hash, tls->esni->esni_contents_hash)) != 0) + goto Exit; + } + } + /* setup resumption-related data. If successful, resumption_secret becomes a non-zero value. */ + if (properties->client.session_ticket.base != NULL) { + ptls_key_exchange_algorithm_t *key_share = NULL; + ptls_cipher_suite_t *cipher_suite = NULL; + uint32_t max_early_data_size; + if (decode_stored_session_ticket(tls, &key_share, &cipher_suite, &resumption_secret, &obfuscated_ticket_age, + &resumption_ticket, &max_early_data_size, properties->client.session_ticket.base, + properties->client.session_ticket.base + properties->client.session_ticket.len) == 0) { + tls->client.offered_psk = 1; + /* key-share selected by HRR should not be overridden */ + if (tls->key_share == NULL) + tls->key_share = key_share; + tls->cipher_suite = cipher_suite; + if (!is_second_flight && max_early_data_size != 0 && properties->client.max_early_data_size != NULL) { + tls->client.using_early_data = 1; + *properties->client.max_early_data_size = max_early_data_size; + } + } else { + resumption_secret = ptls_iovec_init(NULL, 0); + } + } + if (tls->client.using_early_data) { + properties->client.early_data_acceptance = PTLS_EARLY_DATA_ACCEPTANCE_UNKNOWN; + } else { + if (properties->client.max_early_data_size != NULL) + *properties->client.max_early_data_size = 0; + properties->client.early_data_acceptance = PTLS_EARLY_DATA_REJECTED; + } + } + + /* use the default key share if still not undetermined */ + if (tls->key_share == NULL && !(properties != NULL && properties->client.negotiate_before_key_exchange)) + tls->key_share = tls->ctx->key_exchanges[0]; + + if (!is_second_flight) { + tls->key_schedule = key_schedule_new(tls->cipher_suite, tls->ctx->cipher_suites, tls->ctx->hkdf_label_prefix__obsolete); + if ((ret = key_schedule_extract(tls->key_schedule, resumption_secret)) != 0) + goto Exit; + } + + msghash_off = emitter->buf->off + emitter->record_header_length; + ptls_push_message(emitter, NULL, PTLS_HANDSHAKE_TYPE_CLIENT_HELLO, { + ptls_buffer_t *sendbuf = emitter->buf; + /* legacy_version */ + ptls_buffer_push16(sendbuf, 0x0303); + /* random_bytes */ + ptls_buffer_pushv(sendbuf, tls->client_random, sizeof(tls->client_random)); + /* lecagy_session_id */ + ptls_buffer_push_block( + sendbuf, 1, { ptls_buffer_pushv(sendbuf, tls->client.legacy_session_id.base, tls->client.legacy_session_id.len); }); + /* cipher_suites */ + ptls_buffer_push_block(sendbuf, 2, { + ptls_cipher_suite_t **cs = tls->ctx->cipher_suites; + for (; *cs != NULL; ++cs) + ptls_buffer_push16(sendbuf, (*cs)->id); + }); + /* legacy_compression_methods */ + ptls_buffer_push_block(sendbuf, 1, { ptls_buffer_push(sendbuf, 0); }); + /* extensions */ + ptls_buffer_push_block(sendbuf, 2, { + struct { + size_t off; + size_t len; + } key_share_client_hello; + buffer_push_extension(sendbuf, PTLS_EXTENSION_TYPE_KEY_SHARE, { + key_share_client_hello.off = sendbuf->off; + ptls_buffer_push_block(sendbuf, 2, { + if (tls->key_share != NULL) { + if ((ret = tls->key_share->create(tls->key_share, &tls->client.key_share_ctx)) != 0) + goto Exit; + if ((ret = push_key_share_entry(sendbuf, tls->key_share->id, tls->client.key_share_ctx->pubkey)) != 0) + goto Exit; + } + }); + key_share_client_hello.len = sendbuf->off - key_share_client_hello.off; + }); + if (send_sni) { + if (tls->esni != NULL) { + if (published_sni != NULL) { + buffer_push_extension(sendbuf, PTLS_EXTENSION_TYPE_SERVER_NAME, { + if ((ret = emit_server_name_extension(sendbuf, published_sni)) != 0) + goto Exit; + }); + } + buffer_push_extension(sendbuf, PTLS_EXTENSION_TYPE_ENCRYPTED_SERVER_NAME, { + if ((ret = emit_esni_extension(tls->esni, sendbuf, properties->client.esni_keys, tls->server_name, + key_share_client_hello.off, key_share_client_hello.len)) != 0) + goto Exit; + }); + } else { + buffer_push_extension(sendbuf, PTLS_EXTENSION_TYPE_SERVER_NAME, { + if ((ret = emit_server_name_extension(sendbuf, tls->server_name)) != 0) + goto Exit; + }); + } + } + if (properties != NULL && properties->client.negotiated_protocols.count != 0) { + buffer_push_extension(sendbuf, PTLS_EXTENSION_TYPE_ALPN, { + ptls_buffer_push_block(sendbuf, 2, { + size_t i; + for (i = 0; i != properties->client.negotiated_protocols.count; ++i) { + ptls_buffer_push_block(sendbuf, 1, { + ptls_iovec_t p = properties->client.negotiated_protocols.list[i]; + ptls_buffer_pushv(sendbuf, p.base, p.len); + }); + } + }); + }); + } + if (tls->ctx->decompress_certificate != NULL) { + buffer_push_extension(sendbuf, PTLS_EXTENSION_TYPE_COMPRESS_CERTIFICATE, { + ptls_buffer_push_block(sendbuf, 1, { + const uint16_t *algo = tls->ctx->decompress_certificate->supported_algorithms; + assert(*algo != UINT16_MAX); + for (; *algo != UINT16_MAX; ++algo) + ptls_buffer_push16(sendbuf, *algo); + }); + }); + } + buffer_push_extension(sendbuf, PTLS_EXTENSION_TYPE_SUPPORTED_VERSIONS, { + ptls_buffer_push_block(sendbuf, 1, { + size_t i; + for (i = 0; i != PTLS_ELEMENTSOF(supported_versions); ++i) + ptls_buffer_push16(sendbuf, supported_versions[i]); + }); + }); + buffer_push_extension(sendbuf, PTLS_EXTENSION_TYPE_SIGNATURE_ALGORITHMS, { + if ((ret = push_signature_algorithms(sendbuf)) != 0) + goto Exit; + }); + buffer_push_extension(sendbuf, PTLS_EXTENSION_TYPE_SUPPORTED_GROUPS, { + ptls_key_exchange_algorithm_t **algo = tls->ctx->key_exchanges; + ptls_buffer_push_block(sendbuf, 2, { + for (; *algo != NULL; ++algo) + ptls_buffer_push16(sendbuf, (*algo)->id); + }); + }); + if (cookie != NULL && cookie->base != NULL) { + buffer_push_extension(sendbuf, PTLS_EXTENSION_TYPE_COOKIE, { + ptls_buffer_push_block(sendbuf, 2, { ptls_buffer_pushv(sendbuf, cookie->base, cookie->len); }); + }); + } + if ((ret = push_additional_extensions(properties, sendbuf)) != 0) + goto Exit; + if (tls->ctx->save_ticket != NULL || resumption_secret.base != NULL) { + buffer_push_extension(sendbuf, PTLS_EXTENSION_TYPE_PSK_KEY_EXCHANGE_MODES, { + ptls_buffer_push_block(sendbuf, 1, { + if (!tls->ctx->require_dhe_on_psk) + ptls_buffer_push(sendbuf, PTLS_PSK_KE_MODE_PSK); + ptls_buffer_push(sendbuf, PTLS_PSK_KE_MODE_PSK_DHE); + }); + }); + } + if (resumption_secret.base != NULL) { + if (tls->client.using_early_data && !is_second_flight) + buffer_push_extension(sendbuf, PTLS_EXTENSION_TYPE_EARLY_DATA, {}); + /* pre-shared key "MUST be the last extension in the ClientHello" (draft-17 section 4.2.6) */ + buffer_push_extension(sendbuf, PTLS_EXTENSION_TYPE_PRE_SHARED_KEY, { + ptls_buffer_push_block(sendbuf, 2, { + ptls_buffer_push_block(sendbuf, 2, + { ptls_buffer_pushv(sendbuf, resumption_ticket.base, resumption_ticket.len); }); + ptls_buffer_push32(sendbuf, obfuscated_ticket_age); + }); + /* allocate space for PSK binder. the space is filled at the bottom of the function */ + ptls_buffer_push_block(sendbuf, 2, { + ptls_buffer_push_block(sendbuf, 1, { + if ((ret = ptls_buffer_reserve(sendbuf, tls->key_schedule->hashes[0].algo->digest_size)) != 0) + goto Exit; + sendbuf->off += tls->key_schedule->hashes[0].algo->digest_size; + }); + }); + }); + } + }); + }); + + /* update the message hash, filling in the PSK binder HMAC if necessary */ + if (resumption_secret.base != NULL) { + size_t psk_binder_off = emitter->buf->off - (3 + tls->key_schedule->hashes[0].algo->digest_size); + if ((ret = derive_secret_with_empty_digest(tls->key_schedule, binder_key, "res binder")) != 0) + goto Exit; + ptls__key_schedule_update_hash(tls->key_schedule, emitter->buf->base + msghash_off, psk_binder_off - msghash_off); + msghash_off = psk_binder_off; + if ((ret = calc_verify_data(emitter->buf->base + psk_binder_off + 3, tls->key_schedule, binder_key)) != 0) + goto Exit; + } + ptls__key_schedule_update_hash(tls->key_schedule, emitter->buf->base + msghash_off, emitter->buf->off - msghash_off); + + if (tls->client.using_early_data) { + assert(!is_second_flight); + if ((ret = setup_traffic_protection(tls, 1, "c e traffic", 1, 0)) != 0) + goto Exit; + if ((ret = push_change_cipher_spec(tls, emitter)) != 0) + goto Exit; + } + if (resumption_secret.base != NULL && !is_second_flight) { + if ((ret = derive_exporter_secret(tls, 1)) != 0) + goto Exit; + } + tls->state = cookie == NULL ? PTLS_STATE_CLIENT_EXPECT_SERVER_HELLO : PTLS_STATE_CLIENT_EXPECT_SECOND_SERVER_HELLO; + ret = PTLS_ERROR_IN_PROGRESS; + +Exit: + if (published_sni != NULL) { + free(published_sni); + } + ptls_clear_memory(binder_key, sizeof(binder_key)); + return ret; +} + +static ptls_cipher_suite_t *find_cipher_suite(ptls_context_t *ctx, uint16_t id) +{ + ptls_cipher_suite_t **cs; + + for (cs = ctx->cipher_suites; *cs != NULL && (*cs)->id != id; ++cs) + ; + return *cs; +} + +static int decode_server_hello(ptls_t *tls, struct st_ptls_server_hello_t *sh, const uint8_t *src, const uint8_t *const end) +{ + int ret; + + *sh = (struct st_ptls_server_hello_t){{0}}; + + /* ignore legacy-version */ + if (end - src < 2) { + ret = PTLS_ALERT_DECODE_ERROR; + goto Exit; + } + src += 2; + + /* random */ + if (end - src < PTLS_HELLO_RANDOM_SIZE) { + ret = PTLS_ALERT_DECODE_ERROR; + goto Exit; + } + sh->is_retry_request = memcmp(src, hello_retry_random, PTLS_HELLO_RANDOM_SIZE) == 0; + src += PTLS_HELLO_RANDOM_SIZE; + + /* legacy_session_id */ + ptls_decode_open_block(src, end, 1, { + if (end - src > 32) { + ret = PTLS_ALERT_DECODE_ERROR; + goto Exit; + } + sh->legacy_session_id = ptls_iovec_init(src, end - src); + src = end; + }); + + { /* select cipher_suite */ + uint16_t csid; + if ((ret = ptls_decode16(&csid, &src, end)) != 0) + goto Exit; + if ((tls->cipher_suite = find_cipher_suite(tls->ctx, csid)) == NULL) { + ret = PTLS_ALERT_ILLEGAL_PARAMETER; + goto Exit; + } + } + + /* legacy_compression_method */ + if (src == end || *src++ != 0) { + ret = PTLS_ALERT_ILLEGAL_PARAMETER; + goto Exit; + } + + if (sh->is_retry_request) + sh->retry_request.selected_group = UINT16_MAX; + + uint16_t exttype, found_version = UINT16_MAX, selected_psk_identity = UINT16_MAX; + decode_extensions(src, end, PTLS_HANDSHAKE_TYPE_SERVER_HELLO, &exttype, { + if (tls->ctx->on_extension != NULL && + (ret = tls->ctx->on_extension->cb(tls->ctx->on_extension, tls, PTLS_HANDSHAKE_TYPE_SERVER_HELLO, exttype, + ptls_iovec_init(src, end - src)) != 0)) + goto Exit; + switch (exttype) { + case PTLS_EXTENSION_TYPE_SUPPORTED_VERSIONS: + if ((ret = ptls_decode16(&found_version, &src, end)) != 0) + goto Exit; + break; + case PTLS_EXTENSION_TYPE_KEY_SHARE: + if (sh->is_retry_request) { + if ((ret = ptls_decode16(&sh->retry_request.selected_group, &src, end)) != 0) + goto Exit; + } else { + uint16_t group; + if ((ret = decode_key_share_entry(&group, &sh->peerkey, &src, end)) != 0) + goto Exit; + if (src != end) { + ret = PTLS_ALERT_DECODE_ERROR; + goto Exit; + } + if (tls->key_share == NULL || tls->key_share->id != group) { + ret = PTLS_ALERT_ILLEGAL_PARAMETER; + goto Exit; + } + } + break; + case PTLS_EXTENSION_TYPE_COOKIE: + if (sh->is_retry_request) { + ptls_decode_block(src, end, 2, { + if (src == end) { + ret = PTLS_ALERT_DECODE_ERROR; + goto Exit; + } + sh->retry_request.cookie = ptls_iovec_init(src, end - src); + src = end; + }); + } else { + ret = PTLS_ALERT_ILLEGAL_PARAMETER; + goto Exit; + } + break; + case PTLS_EXTENSION_TYPE_PRE_SHARED_KEY: + if (sh->is_retry_request) { + ret = PTLS_ALERT_ILLEGAL_PARAMETER; + goto Exit; + } else { + if ((ret = ptls_decode16(&selected_psk_identity, &src, end)) != 0) + goto Exit; + } + break; + default: + src = end; + break; + } + }); + + if (!is_supported_version(found_version)) { + ret = PTLS_ALERT_ILLEGAL_PARAMETER; + goto Exit; + } + if (!sh->is_retry_request) { + if (selected_psk_identity != UINT16_MAX) { + if (!tls->client.offered_psk) { + ret = PTLS_ALERT_ILLEGAL_PARAMETER; + goto Exit; + } + if (selected_psk_identity != 0) { + ret = PTLS_ALERT_ILLEGAL_PARAMETER; + goto Exit; + } + tls->is_psk_handshake = 1; + } + if (sh->peerkey.base == NULL && !tls->is_psk_handshake) { + ret = PTLS_ALERT_ILLEGAL_PARAMETER; + goto Exit; + } + } + + ret = 0; +Exit: + return ret; +} + +static int handle_hello_retry_request(ptls_t *tls, ptls_message_emitter_t *emitter, struct st_ptls_server_hello_t *sh, + ptls_iovec_t message, ptls_handshake_properties_t *properties) +{ + int ret; + + if (tls->client.key_share_ctx != NULL) { + tls->client.key_share_ctx->on_exchange(&tls->client.key_share_ctx, 1, NULL, ptls_iovec_init(NULL, 0)); + tls->client.key_share_ctx = NULL; + } + if (tls->client.using_early_data) { + /* release traffic encryption key so that 2nd CH goes out in cleartext, but keep the epoch at 1 since we've already + * called derive-secret */ + if (tls->ctx->update_traffic_key == NULL) { + assert(tls->traffic_protection.enc.aead != NULL); + ptls_aead_free(tls->traffic_protection.enc.aead); + tls->traffic_protection.enc.aead = NULL; + } + tls->client.using_early_data = 0; + } + + if (sh->retry_request.selected_group != UINT16_MAX) { + /* we offer the first key_exchanges[0] as KEY_SHARE unless client.negotiate_before_key_exchange is set */ + ptls_key_exchange_algorithm_t **cand; + for (cand = tls->ctx->key_exchanges; *cand != NULL; ++cand) + if ((*cand)->id == sh->retry_request.selected_group) + break; + if (*cand == NULL) { + ret = PTLS_ALERT_ILLEGAL_PARAMETER; + goto Exit; + } + tls->key_share = *cand; + } else if (tls->key_share != NULL) { + /* retain the key-share using in first CH, if server does not specify one */ + } else { + ret = PTLS_ALERT_ILLEGAL_PARAMETER; + goto Exit; + } + + key_schedule_transform_post_ch1hash(tls->key_schedule); + ptls__key_schedule_update_hash(tls->key_schedule, message.base, message.len); + ret = send_client_hello(tls, emitter, properties, &sh->retry_request.cookie); + +Exit: + return ret; +} + +static int client_handle_hello(ptls_t *tls, ptls_message_emitter_t *emitter, ptls_iovec_t message, + ptls_handshake_properties_t *properties) +{ + struct st_ptls_server_hello_t sh; + ptls_iovec_t ecdh_secret = {NULL}; + int ret; + + if ((ret = decode_server_hello(tls, &sh, message.base + PTLS_HANDSHAKE_HEADER_SIZE, message.base + message.len)) != 0) + goto Exit; + if (!(sh.legacy_session_id.len == tls->client.legacy_session_id.len && + ptls_mem_equal(sh.legacy_session_id.base, tls->client.legacy_session_id.base, tls->client.legacy_session_id.len))) { + ret = PTLS_ALERT_ILLEGAL_PARAMETER; + goto Exit; + } + + if (sh.is_retry_request) { + if ((ret = key_schedule_select_one(tls->key_schedule, tls->cipher_suite, 0)) != 0) + goto Exit; + return handle_hello_retry_request(tls, emitter, &sh, message, properties); + } + + if ((ret = key_schedule_select_one(tls->key_schedule, tls->cipher_suite, tls->client.offered_psk && !tls->is_psk_handshake)) != + 0) + goto Exit; + + if (sh.peerkey.base != NULL) { + if ((ret = tls->client.key_share_ctx->on_exchange(&tls->client.key_share_ctx, 1, &ecdh_secret, sh.peerkey)) != 0) + goto Exit; + } + + ptls__key_schedule_update_hash(tls->key_schedule, message.base, message.len); + + if ((ret = key_schedule_extract(tls->key_schedule, ecdh_secret)) != 0) + goto Exit; + if ((ret = setup_traffic_protection(tls, 0, "s hs traffic", 2, 0)) != 0) + goto Exit; + if (tls->client.using_early_data) { + if ((tls->pending_handshake_secret = malloc(PTLS_MAX_DIGEST_SIZE)) == NULL) { + ret = PTLS_ERROR_NO_MEMORY; + goto Exit; + } + if ((ret = derive_secret(tls->key_schedule, tls->pending_handshake_secret, "c hs traffic")) != 0) + goto Exit; + if (tls->ctx->update_traffic_key != NULL && + (ret = tls->ctx->update_traffic_key->cb(tls->ctx->update_traffic_key, tls, 1, 2, tls->pending_handshake_secret)) != 0) + goto Exit; + } else { + if ((ret = setup_traffic_protection(tls, 1, "c hs traffic", 2, 0)) != 0) + goto Exit; + } + + tls->state = PTLS_STATE_CLIENT_EXPECT_ENCRYPTED_EXTENSIONS; + ret = PTLS_ERROR_IN_PROGRESS; + +Exit: + if (ecdh_secret.base != NULL) { + ptls_clear_memory(ecdh_secret.base, ecdh_secret.len); + free(ecdh_secret.base); + } + return ret; +} + +static int should_collect_unknown_extension(ptls_t *tls, ptls_handshake_properties_t *properties, uint16_t type) +{ + return properties != NULL && properties->collect_extension != NULL && properties->collect_extension(tls, properties, type); +} + +static int collect_unknown_extension(ptls_t *tls, uint16_t type, const uint8_t *src, const uint8_t *const end, + ptls_raw_extension_t *slots) +{ + size_t i; + for (i = 0; slots[i].type != UINT16_MAX; ++i) { + assert(i < MAX_UNKNOWN_EXTENSIONS); + if (slots[i].type == type) + return PTLS_ALERT_ILLEGAL_PARAMETER; + } + if (i < MAX_UNKNOWN_EXTENSIONS) { + slots[i].type = type; + slots[i].data = ptls_iovec_init(src, end - src); + slots[i + 1].type = UINT16_MAX; + } + return 0; +} + +static int report_unknown_extensions(ptls_t *tls, ptls_handshake_properties_t *properties, ptls_raw_extension_t *slots) +{ + if (properties != NULL && properties->collect_extension != NULL) { + assert(properties->collected_extensions != NULL); + return properties->collected_extensions(tls, properties, slots); + } else { + return 0; + } +} + +static int client_handle_encrypted_extensions(ptls_t *tls, ptls_iovec_t message, ptls_handshake_properties_t *properties) +{ + const uint8_t *src = message.base + PTLS_HANDSHAKE_HEADER_SIZE, *const end = message.base + message.len, *esni_nonce = NULL; + uint16_t type; + static const ptls_raw_extension_t no_unknown_extensions = {UINT16_MAX}; + ptls_raw_extension_t *unknown_extensions = (ptls_raw_extension_t *)&no_unknown_extensions; + int ret, skip_early_data = 1; + + decode_extensions(src, end, PTLS_HANDSHAKE_TYPE_ENCRYPTED_EXTENSIONS, &type, { + if (tls->ctx->on_extension != NULL && + (ret = tls->ctx->on_extension->cb(tls->ctx->on_extension, tls, PTLS_HANDSHAKE_TYPE_ENCRYPTED_EXTENSIONS, type, + ptls_iovec_init(src, end - src)) != 0)) + goto Exit; + switch (type) { + case PTLS_EXTENSION_TYPE_SERVER_NAME: + if (src != end) { + ret = PTLS_ALERT_DECODE_ERROR; + goto Exit; + } + if (!(tls->server_name != NULL && !ptls_server_name_is_ipaddr(tls->server_name))) { + ret = PTLS_ALERT_ILLEGAL_PARAMETER; + goto Exit; + } + break; + case PTLS_EXTENSION_TYPE_ENCRYPTED_SERVER_NAME: + if (*src == PTLS_ESNI_RESPONSE_TYPE_ACCEPT) { + if (end - src != PTLS_ESNI_NONCE_SIZE + 1) { + ret = PTLS_ALERT_ILLEGAL_PARAMETER; + goto Exit; + } + esni_nonce = src + 1; + } else { + /* TODO: provide API to parse the RETRY REQUEST response */ + ret = PTLS_ERROR_ESNI_RETRY; + goto Exit; + } + break; + case PTLS_EXTENSION_TYPE_ALPN: + ptls_decode_block(src, end, 2, { + ptls_decode_open_block(src, end, 1, { + if (src == end) { + ret = PTLS_ALERT_DECODE_ERROR; + goto Exit; + } + if ((ret = ptls_set_negotiated_protocol(tls, (const char *)src, end - src)) != 0) + goto Exit; + src = end; + }); + if (src != end) { + ret = PTLS_ALERT_HANDSHAKE_FAILURE; + goto Exit; + } + }); + break; + case PTLS_EXTENSION_TYPE_EARLY_DATA: + if (!tls->client.using_early_data) { + ret = PTLS_ALERT_ILLEGAL_PARAMETER; + goto Exit; + } + skip_early_data = 0; + break; + default: + if (should_collect_unknown_extension(tls, properties, type)) { + if (unknown_extensions == &no_unknown_extensions) { + if ((unknown_extensions = malloc(sizeof(*unknown_extensions) * (MAX_UNKNOWN_EXTENSIONS + 1))) == NULL) { + ret = PTLS_ERROR_NO_MEMORY; + goto Exit; + } + unknown_extensions[0].type = UINT16_MAX; + } + if ((ret = collect_unknown_extension(tls, type, src, end, unknown_extensions)) != 0) + goto Exit; + } + break; + } + src = end; + }); + + if (tls->esni != NULL) { + if (esni_nonce == NULL || !ptls_mem_equal(esni_nonce, tls->esni->nonce, PTLS_ESNI_NONCE_SIZE)) { + ret = PTLS_ALERT_ILLEGAL_PARAMETER; + goto Exit; + } + free_esni_secret(&tls->esni, 0); + } else { + if (esni_nonce != NULL) { + ret = PTLS_ALERT_ILLEGAL_PARAMETER; + goto Exit; + } + } + + if (tls->client.using_early_data) { + if (skip_early_data) + tls->client.using_early_data = 0; + if (properties != NULL) + properties->client.early_data_acceptance = skip_early_data ? PTLS_EARLY_DATA_REJECTED : PTLS_EARLY_DATA_ACCEPTED; + } + if ((ret = report_unknown_extensions(tls, properties, unknown_extensions)) != 0) + goto Exit; + + ptls__key_schedule_update_hash(tls->key_schedule, message.base, message.len); + tls->state = + tls->is_psk_handshake ? PTLS_STATE_CLIENT_EXPECT_FINISHED : PTLS_STATE_CLIENT_EXPECT_CERTIFICATE_REQUEST_OR_CERTIFICATE; + ret = PTLS_ERROR_IN_PROGRESS; + +Exit: + if (unknown_extensions != &no_unknown_extensions) + free(unknown_extensions); + return ret; +} + +static int decode_certificate_request(ptls_t *tls, struct st_ptls_certificate_request_t *cr, const uint8_t *src, + const uint8_t *const end) +{ + int ret; + uint16_t exttype = 0; + + /* certificate request context */ + ptls_decode_open_block(src, end, 1, { + size_t len = end - src; + if (len > 255) { + ret = PTLS_ALERT_DECODE_ERROR; + goto Exit; + } + if ((cr->context.base = malloc(len != 0 ? len : 1)) == NULL) { + ret = PTLS_ERROR_NO_MEMORY; + goto Exit; + } + cr->context.len = len; + memcpy(cr->context.base, src, len); + src = end; + }); + + /* decode extensions */ + decode_extensions(src, end, PTLS_HANDSHAKE_TYPE_CERTIFICATE_REQUEST, &exttype, { + if (tls->ctx->on_extension != NULL && + (ret = tls->ctx->on_extension->cb(tls->ctx->on_extension, tls, PTLS_HANDSHAKE_TYPE_CERTIFICATE_REQUEST, exttype, + ptls_iovec_init(src, end - src)) != 0)) + goto Exit; + switch (exttype) { + case PTLS_EXTENSION_TYPE_SIGNATURE_ALGORITHMS: + if ((ret = decode_signature_algorithms(&cr->signature_algorithms, &src, end)) != 0) + goto Exit; + break; + } + src = end; + }); + + if (cr->signature_algorithms.count == 0) { + ret = PTLS_ALERT_MISSING_EXTENSION; + goto Exit; + } + + ret = 0; +Exit: + return ret; +} + +int ptls_build_certificate_message(ptls_buffer_t *buf, ptls_iovec_t context, ptls_iovec_t *certificates, size_t num_certificates, + ptls_iovec_t ocsp_status) +{ + int ret; + + ptls_buffer_push_block(buf, 1, { ptls_buffer_pushv(buf, context.base, context.len); }); + ptls_buffer_push_block(buf, 3, { + size_t i; + for (i = 0; i != num_certificates; ++i) { + ptls_buffer_push_block(buf, 3, { ptls_buffer_pushv(buf, certificates[i].base, certificates[i].len); }); + ptls_buffer_push_block(buf, 2, { + if (i == 0 && ocsp_status.len != 0) { + buffer_push_extension(buf, PTLS_EXTENSION_TYPE_STATUS_REQUEST, { + ptls_buffer_push(buf, 1); /* status_type == ocsp */ + ptls_buffer_push_block(buf, 3, { ptls_buffer_pushv(buf, ocsp_status.base, ocsp_status.len); }); + }); + } + }); + } + }); + + ret = 0; +Exit: + return ret; +} + +static int default_emit_certificate_cb(ptls_emit_certificate_t *_self, ptls_t *tls, ptls_message_emitter_t *emitter, + ptls_key_schedule_t *key_sched, ptls_iovec_t context, int push_status_request, + const uint16_t *compress_algos, size_t num_compress_algos) +{ + int ret; + + ptls_push_message(emitter, key_sched, PTLS_HANDSHAKE_TYPE_CERTIFICATE, { + if ((ret = ptls_build_certificate_message(emitter->buf, context, tls->ctx->certificates.list, tls->ctx->certificates.count, + ptls_iovec_init(NULL, 0))) != 0) + goto Exit; + }); + + ret = 0; +Exit: + return ret; +} + +static int send_certificate_and_certificate_verify(ptls_t *tls, ptls_message_emitter_t *emitter, + struct st_ptls_signature_algorithms_t *signature_algorithms, + ptls_iovec_t context, const char *context_string, int push_status_request, + const uint16_t *compress_algos, size_t num_compress_algos) +{ + int ret; + + if (signature_algorithms->count == 0) { + ret = PTLS_ALERT_MISSING_EXTENSION; + goto Exit; + } + + { /* send Certificate (or the equivalent) */ + static ptls_emit_certificate_t default_emit_certificate = {default_emit_certificate_cb}; + ptls_emit_certificate_t *emit_certificate = + tls->ctx->emit_certificate != NULL ? tls->ctx->emit_certificate : &default_emit_certificate; + Redo: + if ((ret = emit_certificate->cb(emit_certificate, tls, emitter, tls->key_schedule, context, push_status_request, + compress_algos, num_compress_algos)) != 0) { + if (ret == PTLS_ERROR_DELEGATE) { + assert(emit_certificate != &default_emit_certificate); + emit_certificate = &default_emit_certificate; + goto Redo; + } + goto Exit; + } + } + + /* build and send CertificateVerify */ + if (tls->ctx->sign_certificate != NULL) { + ptls_push_message(emitter, tls->key_schedule, PTLS_HANDSHAKE_TYPE_CERTIFICATE_VERIFY, { + ptls_buffer_t *sendbuf = emitter->buf; + size_t algo_off = sendbuf->off; + ptls_buffer_push16(sendbuf, 0); /* filled in later */ + ptls_buffer_push_block(sendbuf, 2, { + uint16_t algo; + uint8_t data[PTLS_MAX_CERTIFICATE_VERIFY_SIGNDATA_SIZE]; + size_t datalen = build_certificate_verify_signdata(data, tls->key_schedule, context_string); + if ((ret = tls->ctx->sign_certificate->cb(tls->ctx->sign_certificate, tls, &algo, sendbuf, + ptls_iovec_init(data, datalen), signature_algorithms->list, + signature_algorithms->count)) != 0) { + goto Exit; + } + sendbuf->base[algo_off] = (uint8_t)(algo >> 8); + sendbuf->base[algo_off + 1] = (uint8_t)algo; + }); + }); + } + +Exit: + return ret; +} + +static int client_handle_certificate_request(ptls_t *tls, ptls_iovec_t message, ptls_handshake_properties_t *properties) +{ + const uint8_t *src = message.base + PTLS_HANDSHAKE_HEADER_SIZE, *const end = message.base + message.len; + int ret = 0; + + if ((ret = decode_certificate_request(tls, &tls->client.certificate_request, src, end)) != 0) + return ret; + + /* This field SHALL be zero length unless used for the post-handshake authentication exchanges (section 4.3.2) */ + if (tls->client.certificate_request.context.len != 0) + return PTLS_ALERT_ILLEGAL_PARAMETER; + + tls->state = PTLS_STATE_CLIENT_EXPECT_CERTIFICATE; + ptls__key_schedule_update_hash(tls->key_schedule, message.base, message.len); + + return PTLS_ERROR_IN_PROGRESS; +} + +static int handle_certificate(ptls_t *tls, const uint8_t *src, const uint8_t *end, int *got_certs) +{ + ptls_iovec_t certs[16]; + size_t num_certs = 0; + int ret = 0; + + /* certificate request context */ + ptls_decode_open_block(src, end, 1, { + if (src != end) { + ret = PTLS_ALERT_ILLEGAL_PARAMETER; + goto Exit; + } + }); + /* certificate_list */ + ptls_decode_block(src, end, 3, { + while (src != end) { + ptls_decode_open_block(src, end, 3, { + if (num_certs < PTLS_ELEMENTSOF(certs)) + certs[num_certs++] = ptls_iovec_init(src, end - src); + src = end; + }); + uint16_t type; + decode_open_extensions(src, end, PTLS_HANDSHAKE_TYPE_CERTIFICATE, &type, { + if (tls->ctx->on_extension != NULL && + (ret = tls->ctx->on_extension->cb(tls->ctx->on_extension, tls, PTLS_HANDSHAKE_TYPE_CERTIFICATE, type, + ptls_iovec_init(src, end - src)) != 0)) + goto Exit; + src = end; + }); + } + }); + + if (num_certs != 0 && tls->ctx->verify_certificate != NULL) { + if ((ret = tls->ctx->verify_certificate->cb(tls->ctx->verify_certificate, tls, &tls->certificate_verify.cb, + &tls->certificate_verify.verify_ctx, certs, num_certs)) != 0) + goto Exit; + } + + *got_certs = num_certs != 0; + +Exit: + return ret; +} + +static int client_do_handle_certificate(ptls_t *tls, const uint8_t *src, const uint8_t *end) +{ + int got_certs, ret; + + if ((ret = handle_certificate(tls, src, end, &got_certs)) != 0) + return ret; + if (!got_certs) + return PTLS_ALERT_ILLEGAL_PARAMETER; + + return 0; +} + +static int client_handle_certificate(ptls_t *tls, ptls_iovec_t message) +{ + int ret; + + if ((ret = client_do_handle_certificate(tls, message.base + PTLS_HANDSHAKE_HEADER_SIZE, message.base + message.len)) != 0) + return ret; + + ptls__key_schedule_update_hash(tls->key_schedule, message.base, message.len); + + tls->state = PTLS_STATE_CLIENT_EXPECT_CERTIFICATE_VERIFY; + return PTLS_ERROR_IN_PROGRESS; +} + +static int client_handle_compressed_certificate(ptls_t *tls, ptls_iovec_t message) +{ + const uint8_t *src = message.base + PTLS_HANDSHAKE_HEADER_SIZE, *const end = message.base + message.len; + uint16_t algo; + uint32_t uncompressed_size; + uint8_t *uncompressed = NULL; + int ret; + + if (tls->ctx->decompress_certificate == NULL) { + ret = PTLS_ALERT_UNEXPECTED_MESSAGE; + goto Exit; + } + + /* decode */ + if ((ret = ptls_decode16(&algo, &src, end)) != 0) + goto Exit; + if ((ret = ptls_decode24(&uncompressed_size, &src, end)) != 0) + goto Exit; + if (uncompressed_size > 65536) { /* TODO find a sensible number */ + ret = PTLS_ALERT_BAD_CERTIFICATE; + goto Exit; + } + if ((uncompressed = malloc(uncompressed_size)) == NULL) { + ret = PTLS_ERROR_NO_MEMORY; + goto Exit; + } + ptls_decode_block(src, end, 3, { + if ((ret = tls->ctx->decompress_certificate->cb(tls->ctx->decompress_certificate, tls, algo, + ptls_iovec_init(uncompressed, uncompressed_size), + ptls_iovec_init(src, end - src))) != 0) + goto Exit; + src = end; + }); + + /* handle */ + if ((ret = client_do_handle_certificate(tls, uncompressed, uncompressed + uncompressed_size)) != 0) + goto Exit; + + ptls__key_schedule_update_hash(tls->key_schedule, message.base, message.len); + tls->state = PTLS_STATE_CLIENT_EXPECT_CERTIFICATE_VERIFY; + ret = PTLS_ERROR_IN_PROGRESS; + +Exit: + free(uncompressed); + return ret; +} + +static int server_handle_certificate(ptls_t *tls, ptls_iovec_t message) +{ + int got_certs, ret; + + if ((ret = handle_certificate(tls, message.base + PTLS_HANDSHAKE_HEADER_SIZE, message.base + message.len, &got_certs)) != 0) + return ret; + if (!got_certs) + return PTLS_ALERT_CERTIFICATE_REQUIRED; + + ptls__key_schedule_update_hash(tls->key_schedule, message.base, message.len); + + tls->state = PTLS_STATE_SERVER_EXPECT_CERTIFICATE_VERIFY; + return PTLS_ERROR_IN_PROGRESS; +} + +static int handle_certificate_verify(ptls_t *tls, ptls_iovec_t message, const char *context_string) +{ + const uint8_t *src = message.base + PTLS_HANDSHAKE_HEADER_SIZE, *const end = message.base + message.len; + uint16_t algo; + ptls_iovec_t signature; + uint8_t signdata[PTLS_MAX_CERTIFICATE_VERIFY_SIGNDATA_SIZE]; + size_t signdata_size; + int ret; + + /* decode */ + if ((ret = ptls_decode16(&algo, &src, end)) != 0) + goto Exit; + ptls_decode_block(src, end, 2, { + signature = ptls_iovec_init(src, end - src); + src = end; + }); + + /* validate */ + switch (algo) { + case PTLS_SIGNATURE_RSA_PSS_RSAE_SHA256: + case PTLS_SIGNATURE_ECDSA_SECP256R1_SHA256: + /* ok */ + break; + default: + ret = PTLS_ALERT_ILLEGAL_PARAMETER; + goto Exit; + } + signdata_size = build_certificate_verify_signdata(signdata, tls->key_schedule, context_string); + if (tls->certificate_verify.cb != NULL) { + ret = tls->certificate_verify.cb(tls->certificate_verify.verify_ctx, ptls_iovec_init(signdata, signdata_size), signature); + } else { + ret = 0; + } + ptls_clear_memory(signdata, signdata_size); + tls->certificate_verify.cb = NULL; + if (ret != 0) { + goto Exit; + } + + ptls__key_schedule_update_hash(tls->key_schedule, message.base, message.len); + +Exit: + return ret; +} + +static int client_handle_certificate_verify(ptls_t *tls, ptls_iovec_t message) +{ + int ret = handle_certificate_verify(tls, message, PTLS_SERVER_CERTIFICATE_VERIFY_CONTEXT_STRING); + + if (ret == 0) { + tls->state = PTLS_STATE_CLIENT_EXPECT_FINISHED; + ret = PTLS_ERROR_IN_PROGRESS; + } + + return ret; +} + +static int server_handle_certificate_verify(ptls_t *tls, ptls_iovec_t message) +{ + int ret = handle_certificate_verify(tls, message, PTLS_CLIENT_CERTIFICATE_VERIFY_CONTEXT_STRING); + + if (ret == 0) { + tls->state = PTLS_STATE_SERVER_EXPECT_FINISHED; + ret = PTLS_ERROR_IN_PROGRESS; + } + + return ret; +} + +static int client_handle_finished(ptls_t *tls, ptls_message_emitter_t *emitter, ptls_iovec_t message) +{ + uint8_t send_secret[PTLS_MAX_DIGEST_SIZE]; + int ret; + + if ((ret = verify_finished(tls, message)) != 0) + goto Exit; + ptls__key_schedule_update_hash(tls->key_schedule, message.base, message.len); + + /* update traffic keys by using messages upto ServerFinished, but commission them after sending ClientFinished */ + if ((ret = key_schedule_extract(tls->key_schedule, ptls_iovec_init(NULL, 0))) != 0) + goto Exit; + if ((ret = setup_traffic_protection(tls, 0, "s ap traffic", 3, 0)) != 0) + goto Exit; + if ((ret = derive_secret(tls->key_schedule, send_secret, "c ap traffic")) != 0) + goto Exit; + if ((ret = derive_exporter_secret(tls, 0)) != 0) + goto Exit; + + /* if sending early data, emit EOED and commision the client handshake traffic secret */ + if (tls->pending_handshake_secret != NULL) { + assert(tls->traffic_protection.enc.aead != NULL || tls->ctx->update_traffic_key != NULL); + if (tls->client.using_early_data && !tls->ctx->omit_end_of_early_data) + ptls_push_message(emitter, tls->key_schedule, PTLS_HANDSHAKE_TYPE_END_OF_EARLY_DATA, {}); + tls->client.using_early_data = 0; + if ((ret = commission_handshake_secret(tls)) != 0) + goto Exit; + } + + if ((ret = push_change_cipher_spec(tls, emitter)) != 0) + goto Exit; + + if (tls->client.certificate_request.context.base != NULL) { + /* If this is a resumed session, the server must not send the certificate request in the handshake */ + if (tls->is_psk_handshake) { + ret = PTLS_ALERT_ILLEGAL_PARAMETER; + goto Exit; + } + ret = send_certificate_and_certificate_verify(tls, emitter, &tls->client.certificate_request.signature_algorithms, + tls->client.certificate_request.context, + PTLS_CLIENT_CERTIFICATE_VERIFY_CONTEXT_STRING, 0, NULL, 0); + free(tls->client.certificate_request.context.base); + tls->client.certificate_request.context = ptls_iovec_init(NULL, 0); + if (ret != 0) + goto Exit; + } + + ret = send_finished(tls, emitter); + + memcpy(tls->traffic_protection.enc.secret, send_secret, sizeof(send_secret)); + if ((ret = setup_traffic_protection(tls, 1, NULL, 3, 0)) != 0) + goto Exit; + + tls->state = PTLS_STATE_CLIENT_POST_HANDSHAKE; + +Exit: + ptls_clear_memory(send_secret, sizeof(send_secret)); + return ret; +} + +static int client_handle_new_session_ticket(ptls_t *tls, ptls_iovec_t message) +{ + const uint8_t *src = message.base + PTLS_HANDSHAKE_HEADER_SIZE, *const end = message.base + message.len; + ptls_iovec_t ticket_nonce; + int ret; + + { /* verify the format */ + uint32_t ticket_lifetime, ticket_age_add, max_early_data_size; + ptls_iovec_t ticket; + if ((ret = decode_new_session_ticket(tls, &ticket_lifetime, &ticket_age_add, &ticket_nonce, &ticket, &max_early_data_size, + src, end)) != 0) + return ret; + } + + /* do nothing if use of session ticket is disabled */ + if (tls->ctx->save_ticket == NULL) + return 0; + + /* save the extension, along with the key of myself */ + ptls_buffer_t ticket_buf; + ptls_buffer_init(&ticket_buf, "", 0); + ptls_buffer_push64(&ticket_buf, tls->ctx->get_time->cb(tls->ctx->get_time)); + ptls_buffer_push16(&ticket_buf, tls->key_share->id); + ptls_buffer_push16(&ticket_buf, tls->cipher_suite->id); + ptls_buffer_push_block(&ticket_buf, 3, { ptls_buffer_pushv(&ticket_buf, src, end - src); }); + ptls_buffer_push_block(&ticket_buf, 2, { + if ((ret = ptls_buffer_reserve(&ticket_buf, tls->key_schedule->hashes[0].algo->digest_size)) != 0) + goto Exit; + if ((ret = derive_resumption_secret(tls->key_schedule, ticket_buf.base + ticket_buf.off, ticket_nonce)) != 0) + goto Exit; + ticket_buf.off += tls->key_schedule->hashes[0].algo->digest_size; + }); + + if ((ret = tls->ctx->save_ticket->cb(tls->ctx->save_ticket, tls, ptls_iovec_init(ticket_buf.base, ticket_buf.off))) != 0) + goto Exit; + + ret = 0; +Exit: + ptls_buffer_dispose(&ticket_buf); + return ret; +} + +static int client_hello_decode_server_name(ptls_iovec_t *name, const uint8_t **src, const uint8_t *const end) +{ + int ret = 0; + + ptls_decode_open_block(*src, end, 2, { + if (*src == end) { + ret = PTLS_ALERT_DECODE_ERROR; + goto Exit; + } + do { + uint8_t type = *(*src)++; + ptls_decode_open_block(*src, end, 2, { + switch (type) { + case PTLS_SERVER_NAME_TYPE_HOSTNAME: + if (memchr(*src, '\0', end - *src) != 0) { + ret = PTLS_ALERT_ILLEGAL_PARAMETER; + goto Exit; + } + *name = ptls_iovec_init(*src, end - *src); + break; + default: + break; + } + *src = end; + }); + } while (*src != end); + }); + +Exit: + return ret; +} + +static int client_hello_decrypt_esni(ptls_context_t *ctx, ptls_iovec_t *server_name, ptls_esni_secret_t **secret, + struct st_ptls_client_hello_t *ch) +{ + ptls_esni_context_t **esni; + ptls_key_exchange_context_t **key_share_ctx; + uint8_t *decrypted = NULL; + ptls_aead_context_t *aead = NULL; + int ret; + + /* allocate secret */ + assert(*secret == NULL); + if ((*secret = malloc(sizeof(**secret))) == NULL) + return PTLS_ERROR_NO_MEMORY; + memset(*secret, 0, sizeof(**secret)); + + /* find the matching esni structure */ + for (esni = ctx->esni; *esni != NULL; ++esni) { + size_t i; + for (i = 0; (*esni)->cipher_suites[i].cipher_suite != NULL; ++i) + if ((*esni)->cipher_suites[i].cipher_suite->id == ch->esni.cipher->id) + break; + if ((*esni)->cipher_suites[i].cipher_suite == NULL) { + ret = PTLS_ALERT_ILLEGAL_PARAMETER; + goto Exit; + } + if (memcmp((*esni)->cipher_suites[i].record_digest, ch->esni.record_digest, ch->esni.cipher->hash->digest_size) == 0) { + (*secret)->version = (*esni)->version; + break; + } + } + if (*esni == NULL) { + ret = PTLS_ALERT_ILLEGAL_PARAMETER; + goto Exit; + } + + /* find the matching private key for ESNI decryption */ + for (key_share_ctx = (*esni)->key_exchanges; *key_share_ctx != NULL; ++key_share_ctx) + if ((*key_share_ctx)->algo->id == ch->esni.key_share->id) + break; + if (*key_share_ctx == NULL) { + ret = PTLS_ALERT_ILLEGAL_PARAMETER; + goto Exit; + } + + /* calculate ESNIContents */ + if ((ret = build_esni_contents_hash(ch->esni.cipher->hash, (*secret)->esni_contents_hash, ch->esni.record_digest, + ch->esni.key_share->id, ch->esni.peer_key, ch->random_bytes)) != 0) + goto Exit; + /* derive the shared secret */ + if ((ret = (*key_share_ctx)->on_exchange(key_share_ctx, 0, &(*secret)->secret, ch->esni.peer_key)) != 0) + goto Exit; + /* decrypt */ + if (ch->esni.encrypted_sni.len - ch->esni.cipher->aead->tag_size != (*esni)->padded_length + PTLS_ESNI_NONCE_SIZE) { + ret = PTLS_ALERT_ILLEGAL_PARAMETER; + goto Exit; + } + if ((decrypted = malloc((*esni)->padded_length + PTLS_ESNI_NONCE_SIZE)) == NULL) { + ret = PTLS_ERROR_NO_MEMORY; + goto Exit; + } + if ((ret = create_esni_aead(&aead, 0, ch->esni.cipher, (*secret)->secret, (*secret)->esni_contents_hash)) != 0) + goto Exit; + if (ptls_aead_decrypt(aead, decrypted, ch->esni.encrypted_sni.base, ch->esni.encrypted_sni.len, 0, ch->key_shares.base, + ch->key_shares.len) != (*esni)->padded_length + PTLS_ESNI_NONCE_SIZE) { + ret = PTLS_ALERT_DECRYPT_ERROR; + goto Exit; + } + ptls_aead_free(aead); + aead = NULL; + + { /* decode sni */ + const uint8_t *src = decrypted, *const end = src + (*esni)->padded_length; + ptls_iovec_t found_name; + if (end - src < PTLS_ESNI_NONCE_SIZE) { + ret = PTLS_ALERT_ILLEGAL_PARAMETER; + goto Exit; + } + memcpy((*secret)->nonce, src, PTLS_ESNI_NONCE_SIZE); + src += PTLS_ESNI_NONCE_SIZE; + if ((ret = client_hello_decode_server_name(&found_name, &src, end)) != 0) + goto Exit; + for (; src != end; ++src) { + if (*src != '\0') { + ret = PTLS_ALERT_ILLEGAL_PARAMETER; + goto Exit; + } + } + /* if successful, reuse memory allocated for padded_server_name for storing the found name (freed by the caller) */ + memmove(decrypted, found_name.base, found_name.len); + *server_name = ptls_iovec_init(decrypted, found_name.len); + decrypted = NULL; + } + + ret = 0; +Exit: + if (decrypted != NULL) + free(decrypted); + if (aead != NULL) + ptls_aead_free(aead); + if (ret != 0 && *secret != NULL) + free_esni_secret(secret, 1); + return ret; +} + +static int select_negotiated_group(ptls_key_exchange_algorithm_t **selected, ptls_key_exchange_algorithm_t **candidates, + const uint8_t *src, const uint8_t *const end) +{ + int ret; + + ptls_decode_block(src, end, 2, { + while (src != end) { + uint16_t group; + if ((ret = ptls_decode16(&group, &src, end)) != 0) + goto Exit; + ptls_key_exchange_algorithm_t **c = candidates; + for (; *c != NULL; ++c) { + if ((*c)->id == group) { + *selected = *c; + return 0; + } + } + } + }); + + ret = PTLS_ALERT_HANDSHAKE_FAILURE; + +Exit: + return ret; +} + +static int decode_client_hello(ptls_t *tls, struct st_ptls_client_hello_t *ch, const uint8_t *src, const uint8_t *const end, + ptls_handshake_properties_t *properties) +{ + uint16_t exttype = 0; + int ret; + + /* decode protocol version (do not bare to decode something older than TLS 1.0) */ + if ((ret = ptls_decode16(&ch->legacy_version, &src, end)) != 0) + goto Exit; + if (ch->legacy_version < 0x0301) { + ret = PTLS_ALERT_PROTOCOL_VERSION; + goto Exit; + } + + /* skip random */ + if (end - src < PTLS_HELLO_RANDOM_SIZE) { + ret = PTLS_ALERT_DECODE_ERROR; + goto Exit; + } + ch->random_bytes = src; + src += PTLS_HELLO_RANDOM_SIZE; + + /* skip legacy_session_id */ + ptls_decode_open_block(src, end, 1, { + if (end - src > 32) { + ret = PTLS_ALERT_DECODE_ERROR; + goto Exit; + } + ch->legacy_session_id = ptls_iovec_init(src, end - src); + src = end; + }); + + /* decode and select from ciphersuites */ + ptls_decode_open_block(src, end, 2, { + ch->cipher_suites = ptls_iovec_init(src, end - src); + uint16_t *id = ch->client_ciphers.list; + do { + if ((ret = ptls_decode16(id, &src, end)) != 0) + goto Exit; + id++; + ch->client_ciphers.count++; + if (id >= ch->client_ciphers.list + MAX_CLIENT_CIPHERS) { + src = end; + break; + } + } while (src != end); + }); + + /* decode legacy_compression_methods */ + ptls_decode_open_block(src, end, 1, { + if (src == end) { + ret = PTLS_ALERT_DECODE_ERROR; + goto Exit; + } + ch->compression_methods.ids = src; + ch->compression_methods.count = end - src; + src = end; + }); + + /* decode extensions */ + decode_extensions(src, end, PTLS_HANDSHAKE_TYPE_CLIENT_HELLO, &exttype, { + ch->psk.is_last_extension = 0; + if (tls->ctx->on_extension != NULL && + (ret = tls->ctx->on_extension->cb(tls->ctx->on_extension, tls, PTLS_HANDSHAKE_TYPE_CLIENT_HELLO, exttype, + ptls_iovec_init(src, end - src)) != 0)) + goto Exit; + switch (exttype) { + case PTLS_EXTENSION_TYPE_SERVER_NAME: + if ((ret = client_hello_decode_server_name(&ch->server_name, &src, end)) != 0) + goto Exit; + if (src != end) { + ret = PTLS_ALERT_DECODE_ERROR; + goto Exit; + } + break; + case PTLS_EXTENSION_TYPE_ENCRYPTED_SERVER_NAME: { + ptls_cipher_suite_t **cipher; + if (ch->esni.cipher != NULL) { + ret = PTLS_ALERT_ILLEGAL_PARAMETER; + goto Exit; + } + { /* cipher-suite */ + uint16_t csid; + if ((ret = ptls_decode16(&csid, &src, end)) != 0) + goto Exit; + for (cipher = tls->ctx->cipher_suites; *cipher != NULL; ++cipher) + if ((*cipher)->id == csid) + break; + if (*cipher == NULL) { + ret = PTLS_ALERT_ILLEGAL_PARAMETER; + goto Exit; + } + } + /* key-share (including peer-key) */ + if ((ret = select_key_share(&ch->esni.key_share, &ch->esni.peer_key, tls->ctx->key_exchanges, &src, end, 1)) != 0) + goto Exit; + ptls_decode_open_block(src, end, 2, { + size_t len = end - src; + if (len != (*cipher)->hash->digest_size) { + ret = PTLS_ALERT_ILLEGAL_PARAMETER; + goto Exit; + } + ch->esni.record_digest = src; + src += len; + }); + ptls_decode_block(src, end, 2, { + size_t len = end - src; + if (len < (*cipher)->aead->tag_size) { + ret = PTLS_ALERT_ILLEGAL_PARAMETER; + goto Exit; + } + ch->esni.encrypted_sni = ptls_iovec_init(src, len); + src += len; + }); + ch->esni.cipher = *cipher; /* set only after successful parsing */ + } break; + case PTLS_EXTENSION_TYPE_ALPN: + ptls_decode_block(src, end, 2, { + do { + ptls_decode_open_block(src, end, 1, { + /* rfc7301 3.1: empty strings MUST NOT be included */ + if (src == end) { + ret = PTLS_ALERT_DECODE_ERROR; + goto Exit; + } + if (ch->alpn.count < PTLS_ELEMENTSOF(ch->alpn.list)) + ch->alpn.list[ch->alpn.count++] = ptls_iovec_init(src, end - src); + src = end; + }); + } while (src != end); + }); + break; + case PTLS_EXTENSION_TYPE_COMPRESS_CERTIFICATE: + ptls_decode_block(src, end, 1, { + do { + uint16_t id; + if ((ret = ptls_decode16(&id, &src, end)) != 0) + goto Exit; + if (ch->cert_compression_algos.count < PTLS_ELEMENTSOF(ch->cert_compression_algos.list)) + ch->cert_compression_algos.list[ch->cert_compression_algos.count++] = id; + } while (src != end); + }); + break; + case PTLS_EXTENSION_TYPE_SUPPORTED_GROUPS: + ch->negotiated_groups = ptls_iovec_init(src, end - src); + break; + case PTLS_EXTENSION_TYPE_SIGNATURE_ALGORITHMS: + if ((ret = decode_signature_algorithms(&ch->signature_algorithms, &src, end)) != 0) + goto Exit; + break; + case PTLS_EXTENSION_TYPE_KEY_SHARE: + ch->key_shares = ptls_iovec_init(src, end - src); + break; + case PTLS_EXTENSION_TYPE_SUPPORTED_VERSIONS: + ptls_decode_block(src, end, 1, { + size_t selected_index = PTLS_ELEMENTSOF(supported_versions); + do { + size_t i; + uint16_t v; + if ((ret = ptls_decode16(&v, &src, end)) != 0) + goto Exit; + for (i = 0; i != selected_index; ++i) { + if (supported_versions[i] == v) { + selected_index = i; + break; + } + } + } while (src != end); + if (selected_index != PTLS_ELEMENTSOF(supported_versions)) + ch->selected_version = supported_versions[selected_index]; + }); + break; + case PTLS_EXTENSION_TYPE_COOKIE: + if (properties == NULL || properties->server.cookie.key == NULL) { + ret = PTLS_ALERT_ILLEGAL_PARAMETER; + goto Exit; + } + ch->cookie.all = ptls_iovec_init(src, end - src); + ptls_decode_block(src, end, 2, { + ch->cookie.tbs.base = (void *)src; + ptls_decode_open_block(src, end, 2, { + ptls_decode_open_block(src, end, 1, { + ch->cookie.ch1_hash = ptls_iovec_init(src, end - src); + src = end; + }); + if (src == end) { + ret = PTLS_ALERT_DECODE_ERROR; + goto Exit; + } + switch (*src++) { + case 0: + assert(!ch->cookie.sent_key_share); + break; + case 1: + ch->cookie.sent_key_share = 1; + break; + default: + ret = PTLS_ALERT_DECODE_ERROR; + goto Exit; + } + }); + ch->cookie.tbs.len = src - ch->cookie.tbs.base; + ptls_decode_block(src, end, 1, { + ch->cookie.signature = ptls_iovec_init(src, end - src); + src = end; + }); + }); + break; + case PTLS_EXTENSION_TYPE_PRE_SHARED_KEY: { + size_t num_identities = 0; + ptls_decode_open_block(src, end, 2, { + do { + struct st_ptls_client_hello_psk_t psk = {{NULL}}; + ptls_decode_open_block(src, end, 2, { + psk.identity = ptls_iovec_init(src, end - src); + src = end; + }); + if ((ret = ptls_decode32(&psk.obfuscated_ticket_age, &src, end)) != 0) + goto Exit; + if (ch->psk.identities.count < PTLS_ELEMENTSOF(ch->psk.identities.list)) + ch->psk.identities.list[ch->psk.identities.count++] = psk; + ++num_identities; + } while (src != end); + }); + ch->psk.hash_end = src; + ptls_decode_block(src, end, 2, { + size_t num_binders = 0; + do { + ptls_decode_open_block(src, end, 1, { + if (num_binders < ch->psk.identities.count) + ch->psk.identities.list[num_binders].binder = ptls_iovec_init(src, end - src); + src = end; + }); + ++num_binders; + } while (src != end); + if (num_identities != num_binders) { + ret = PTLS_ALERT_ILLEGAL_PARAMETER; + goto Exit; + } + }); + ch->psk.is_last_extension = 1; + } break; + case PTLS_EXTENSION_TYPE_PSK_KEY_EXCHANGE_MODES: + ptls_decode_block(src, end, 1, { + if (src == end) { + ret = PTLS_ALERT_DECODE_ERROR; + goto Exit; + } + for (; src != end; ++src) { + if (*src < sizeof(ch->psk.ke_modes) * 8) + ch->psk.ke_modes |= 1u << *src; + } + }); + break; + case PTLS_EXTENSION_TYPE_EARLY_DATA: + ch->psk.early_data_indication = 1; + break; + case PTLS_EXTENSION_TYPE_STATUS_REQUEST: + ch->status_request = 1; + break; + default: + if (should_collect_unknown_extension(tls, properties, exttype)) { + if ((ret = collect_unknown_extension(tls, exttype, src, end, ch->unknown_extensions)) != 0) + goto Exit; + } + break; + } + src = end; + }); + + ret = 0; +Exit: + return ret; +} + +static int vec_is_string(ptls_iovec_t x, const char *y) +{ + return strncmp((const char *)x.base, y, x.len) == 0 && y[x.len] == '\0'; +} + +static int try_psk_handshake(ptls_t *tls, size_t *psk_index, int *accept_early_data, struct st_ptls_client_hello_t *ch, + ptls_iovec_t ch_trunc) +{ + ptls_buffer_t decbuf; + ptls_iovec_t ticket_psk, ticket_server_name, ticket_negotiated_protocol; + uint64_t issue_at, now = tls->ctx->get_time->cb(tls->ctx->get_time); + uint32_t age_add; + uint16_t ticket_key_exchange_id, ticket_csid; + uint8_t binder_key[PTLS_MAX_DIGEST_SIZE]; + int ret; + + ptls_buffer_init(&decbuf, "", 0); + + for (*psk_index = 0; *psk_index < ch->psk.identities.count; ++*psk_index) { + struct st_ptls_client_hello_psk_t *identity = ch->psk.identities.list + *psk_index; + /* decrypt and decode */ + int can_accept_early_data = 1; + decbuf.off = 0; + switch (tls->ctx->encrypt_ticket->cb(tls->ctx->encrypt_ticket, tls, 0, &decbuf, identity->identity)) { + case 0: /* decrypted */ + break; + case PTLS_ERROR_REJECT_EARLY_DATA: /* decrypted, but early data is rejected */ + can_accept_early_data = 0; + break; + default: /* decryption failure */ + continue; + } + if (decode_session_identifier(&issue_at, &ticket_psk, &age_add, &ticket_server_name, &ticket_key_exchange_id, &ticket_csid, + &ticket_negotiated_protocol, decbuf.base, decbuf.base + decbuf.off) != 0) + continue; + /* check age */ + if (now < issue_at) + continue; + if (now - issue_at > (uint64_t)tls->ctx->ticket_lifetime * 1000) + continue; + *accept_early_data = 0; + if (ch->psk.early_data_indication && can_accept_early_data) { + /* accept early-data if abs(diff) between the reported age and the actual age is within += 10 seconds */ + int64_t delta = (now - issue_at) - (identity->obfuscated_ticket_age - age_add); + if (delta < 0) + delta = -delta; + if (delta <= PTLS_EARLY_DATA_MAX_DELAY) + *accept_early_data = 1; + } + /* check server-name */ + if (ticket_server_name.len != 0) { + if (tls->server_name == NULL) + continue; + if (!vec_is_string(ticket_server_name, tls->server_name)) + continue; + } else { + if (tls->server_name != NULL) + continue; + } + { /* check key-exchange */ + ptls_key_exchange_algorithm_t **a; + for (a = tls->ctx->key_exchanges; *a != NULL && (*a)->id != ticket_key_exchange_id; ++a) + ; + if (*a == NULL) + continue; + tls->key_share = *a; + } + /* check cipher-suite */ + if (ticket_csid != tls->cipher_suite->id) + continue; + /* check negotiated-protocol */ + if (ticket_negotiated_protocol.len != 0) { + if (tls->negotiated_protocol == NULL) + continue; + if (!vec_is_string(ticket_negotiated_protocol, tls->negotiated_protocol)) + continue; + } + /* check the length of the decrypted psk and the PSK binder */ + if (ticket_psk.len != tls->key_schedule->hashes[0].algo->digest_size) + continue; + if (ch->psk.identities.list[*psk_index].binder.len != tls->key_schedule->hashes[0].algo->digest_size) + continue; + + /* found */ + goto Found; + } + + /* not found */ + *psk_index = SIZE_MAX; + *accept_early_data = 0; + tls->key_share = NULL; + ret = 0; + goto Exit; + +Found: + if ((ret = key_schedule_extract(tls->key_schedule, ticket_psk)) != 0) + goto Exit; + if ((ret = derive_secret(tls->key_schedule, binder_key, "res binder")) != 0) + goto Exit; + ptls__key_schedule_update_hash(tls->key_schedule, ch_trunc.base, ch_trunc.len); + if ((ret = calc_verify_data(binder_key /* to conserve space, reuse binder_key for storing verify_data */, tls->key_schedule, + binder_key)) != 0) + goto Exit; + if (!ptls_mem_equal(ch->psk.identities.list[*psk_index].binder.base, binder_key, + tls->key_schedule->hashes[0].algo->digest_size)) { + ret = PTLS_ALERT_DECRYPT_ERROR; + goto Exit; + } + ret = 0; + +Exit: + ptls_buffer_dispose(&decbuf); + ptls_clear_memory(binder_key, sizeof(binder_key)); + return ret; +} + +static int calc_cookie_signature(ptls_t *tls, ptls_handshake_properties_t *properties, + ptls_key_exchange_algorithm_t *negotiated_group, ptls_iovec_t tbs, uint8_t *sig) +{ + ptls_hash_algorithm_t *algo = tls->ctx->cipher_suites[0]->hash; + ptls_hash_context_t *hctx; + + if ((hctx = ptls_hmac_create(algo, properties->server.cookie.key, algo->digest_size)) == NULL) + return PTLS_ERROR_NO_MEMORY; + +#define UPDATE_BLOCK(p, _len) \ + do { \ + size_t len = (_len); \ + assert(len < UINT8_MAX); \ + uint8_t len8 = (uint8_t)len; \ + hctx->update(hctx, &len8, 1); \ + hctx->update(hctx, (p), len); \ + } while (0) +#define UPDATE16(_v) \ + do { \ + uint16_t v = (_v); \ + uint8_t b[2] = {v >> 8, v & 0xff}; \ + hctx->update(hctx, b, 2); \ + } while (0) + + UPDATE_BLOCK(tls->client_random, sizeof(tls->client_random)); + UPDATE_BLOCK(tls->server_name, tls->server_name != NULL ? strlen(tls->server_name) : 0); + UPDATE16(tls->cipher_suite->id); + UPDATE16(negotiated_group->id); + UPDATE_BLOCK(properties->server.cookie.additional_data.base, properties->server.cookie.additional_data.len); + + UPDATE_BLOCK(tbs.base, tbs.len); + +#undef UPDATE_BLOCK +#undef UPDATE16 + + hctx->final(hctx, sig, PTLS_HASH_FINAL_MODE_FREE); + return 0; +} + +static int server_handle_hello(ptls_t *tls, ptls_message_emitter_t *emitter, ptls_iovec_t message, + ptls_handshake_properties_t *properties) +{ +#define EMIT_SERVER_HELLO(sched, fill_rand, extensions) \ + ptls_push_message(emitter, (sched), PTLS_HANDSHAKE_TYPE_SERVER_HELLO, { \ + ptls_buffer_push16(emitter->buf, 0x0303 /* legacy version */); \ + if ((ret = ptls_buffer_reserve(emitter->buf, PTLS_HELLO_RANDOM_SIZE)) != 0) \ + goto Exit; \ + do { \ + fill_rand \ + } while (0); \ + emitter->buf->off += PTLS_HELLO_RANDOM_SIZE; \ + ptls_buffer_push_block(emitter->buf, 1, \ + { ptls_buffer_pushv(emitter->buf, ch->legacy_session_id.base, ch->legacy_session_id.len); }); \ + ptls_buffer_push16(emitter->buf, tls->cipher_suite->id); \ + ptls_buffer_push(emitter->buf, 0); \ + ptls_buffer_push_block(emitter->buf, 2, { \ + buffer_push_extension(emitter->buf, PTLS_EXTENSION_TYPE_SUPPORTED_VERSIONS, \ + { ptls_buffer_push16(emitter->buf, ch->selected_version); }); \ + do { \ + extensions \ + } while (0); \ + }); \ + }); + +#define EMIT_HELLO_RETRY_REQUEST(sched, negotiated_group, additional_extensions) \ + EMIT_SERVER_HELLO((sched), { memcpy(emitter->buf->base + emitter->buf->off, hello_retry_random, PTLS_HELLO_RANDOM_SIZE); }, \ + { \ + ptls_key_exchange_algorithm_t *_negotiated_group = (negotiated_group); \ + if (_negotiated_group != NULL) { \ + buffer_push_extension(emitter->buf, PTLS_EXTENSION_TYPE_KEY_SHARE, \ + { ptls_buffer_push16(emitter->buf, _negotiated_group->id); }); \ + } \ + do { \ + additional_extensions \ + } while (0); \ + }) + struct st_ptls_client_hello_t *ch; + struct { + ptls_key_exchange_algorithm_t *algorithm; + ptls_iovec_t peer_key; + } key_share = {NULL}; + enum { HANDSHAKE_MODE_FULL, HANDSHAKE_MODE_PSK, HANDSHAKE_MODE_PSK_DHE } mode; + size_t psk_index = SIZE_MAX; + ptls_iovec_t pubkey = {0}, ecdh_secret = {0}; + int accept_early_data = 0, is_second_flight = tls->state == PTLS_STATE_SERVER_EXPECT_SECOND_CLIENT_HELLO, ret; + + if ((ch = malloc(sizeof(*ch))) == NULL) { + ret = PTLS_ERROR_NO_MEMORY; + goto Exit; + } + *ch = (struct st_ptls_client_hello_t){0, NULL, {NULL}, {NULL}, 0, {NULL}, {NULL}, {NULL}, {{0}}, + {NULL}, {NULL}, {{{NULL}}}, {{0}}, {{0}}, {{NULL}}, {NULL}, {{UINT16_MAX}}}; + + /* decode ClientHello */ + if ((ret = decode_client_hello(tls, ch, message.base + PTLS_HANDSHAKE_HEADER_SIZE, message.base + message.len, properties)) != + 0) + goto Exit; + + /* bail out if CH cannot be handled as TLS 1.3, providing the application the raw CH and SNI, to help them fallback */ + if (!is_supported_version(ch->selected_version)) { + if (!is_second_flight && tls->ctx->on_client_hello != NULL) { + ptls_on_client_hello_parameters_t params = { + .server_name = ch->server_name, + .raw_message = message, + .negotiated_protocols = {ch->alpn.list, ch->alpn.count}, + .incompatible_version = 1, + }; + if ((ret = tls->ctx->on_client_hello->cb(tls->ctx->on_client_hello, tls, ¶ms)) != 0) + goto Exit; + } + ret = PTLS_ALERT_PROTOCOL_VERSION; + goto Exit; + } + + /* Check TLS 1.3-specific constraints. Hereafter, we might exit without calling on_client_hello. That's fine because this CH is + * ought to be rejected. */ + if (ch->legacy_version <= 0x0300) { + /* RFC 8446 Appendix D.5: any endpoint receiving a Hello message with legacy_version set to 0x0300 MUST abort the handshake + * with a "protocol_version" alert. */ + ret = PTLS_ALERT_PROTOCOL_VERSION; + goto Exit; + } + if (!(ch->compression_methods.count == 1 && ch->compression_methods.ids[0] == 0)) { + ret = PTLS_ALERT_ILLEGAL_PARAMETER; + goto Exit; + } + /* esni */ + if (ch->esni.cipher != NULL) { + if (ch->key_shares.base == NULL) { + ret = PTLS_ALERT_ILLEGAL_PARAMETER; + goto Exit; + } + } + /* pre-shared key */ + if (ch->psk.hash_end != NULL) { + /* PSK must be the last extension */ + if (!ch->psk.is_last_extension) { + ret = PTLS_ALERT_ILLEGAL_PARAMETER; + goto Exit; + } + } else { + if (ch->psk.early_data_indication) { + ret = PTLS_ALERT_ILLEGAL_PARAMETER; + goto Exit; + } + } + + if (tls->ctx->require_dhe_on_psk) + ch->psk.ke_modes &= ~(1u << PTLS_PSK_KE_MODE_PSK); + + /* handle client_random, legacy_session_id, SNI, ESNI */ + if (!is_second_flight) { + memcpy(tls->client_random, ch->random_bytes, sizeof(tls->client_random)); + log_client_random(tls); + if (ch->legacy_session_id.len != 0) + tls->send_change_cipher_spec = 1; + ptls_iovec_t server_name = {NULL}; + int is_esni = 0; + if (ch->esni.cipher != NULL && tls->ctx->esni != NULL) { + if ((ret = client_hello_decrypt_esni(tls->ctx, &server_name, &tls->esni, ch)) != 0) + goto Exit; + if (tls->ctx->update_esni_key != NULL) { + if ((ret = tls->ctx->update_esni_key->cb(tls->ctx->update_esni_key, tls, tls->esni->secret, ch->esni.cipher->hash, + tls->esni->esni_contents_hash)) != 0) + goto Exit; + } + is_esni = 1; + } else if (ch->server_name.base != NULL) { + server_name = ch->server_name; + } + if (tls->ctx->on_client_hello != NULL) { + ptls_on_client_hello_parameters_t params = {server_name, + message, + {ch->alpn.list, ch->alpn.count}, + {ch->signature_algorithms.list, ch->signature_algorithms.count}, + {ch->cert_compression_algos.list, ch->cert_compression_algos.count}, + {ch->client_ciphers.list, ch->client_ciphers.count}, + is_esni}; + ret = tls->ctx->on_client_hello->cb(tls->ctx->on_client_hello, tls, ¶ms); + } else { + ret = 0; + } + if (is_esni) + free(server_name.base); + if (ret != 0) + goto Exit; + } else { + if (ch->psk.early_data_indication) { + ret = PTLS_ALERT_DECODE_ERROR; + goto Exit; + } + /* the following check is necessary so that we would be able to track the connection in SSLKEYLOGFILE, even though it + * might not be for the safety of the protocol */ + if (!ptls_mem_equal(tls->client_random, ch->random_bytes, sizeof(tls->client_random))) { + ret = PTLS_ALERT_HANDSHAKE_FAILURE; + goto Exit; + } + /* We compare SNI only when the value is saved by the on_client_hello callback. This should be OK because we are + * ignoring the value unless the callback saves the server-name. */ + if (tls->server_name != NULL) { + size_t l = strlen(tls->server_name); + if (!(ch->server_name.len == l && memcmp(ch->server_name.base, tls->server_name, l) == 0)) { + ret = PTLS_ALERT_HANDSHAKE_FAILURE; + goto Exit; + } + } + } + + { /* select (or check) cipher-suite, create key_schedule */ + ptls_cipher_suite_t *cs; + if ((ret = select_cipher(&cs, tls->ctx->cipher_suites, ch->cipher_suites.base, + ch->cipher_suites.base + ch->cipher_suites.len)) != 0) + goto Exit; + if (!is_second_flight) { + tls->cipher_suite = cs; + tls->key_schedule = key_schedule_new(cs, NULL, tls->ctx->hkdf_label_prefix__obsolete); + } else { + if (tls->cipher_suite != cs) { + ret = PTLS_ALERT_HANDSHAKE_FAILURE; + goto Exit; + } + } + } + + /* select key_share */ + if (key_share.algorithm == NULL && ch->key_shares.base != NULL) { + const uint8_t *src = ch->key_shares.base, *const end = src + ch->key_shares.len; + ptls_decode_block(src, end, 2, { + if ((ret = select_key_share(&key_share.algorithm, &key_share.peer_key, tls->ctx->key_exchanges, &src, end, 0)) != 0) + goto Exit; + }); + } + + if (!is_second_flight) { + if (ch->cookie.all.len != 0 && key_share.algorithm != NULL) { + + /* use cookie to check the integrity of the handshake, and update the context */ + size_t sigsize = tls->ctx->cipher_suites[0]->hash->digest_size; + uint8_t *sig = alloca(sigsize); + if ((ret = calc_cookie_signature(tls, properties, key_share.algorithm, ch->cookie.tbs, sig)) != 0) + goto Exit; + if (!(ch->cookie.signature.len == sigsize && ptls_mem_equal(ch->cookie.signature.base, sig, sigsize))) { + ret = PTLS_ALERT_HANDSHAKE_FAILURE; + goto Exit; + } + /* integrity check passed; update states */ + key_schedule_update_ch1hash_prefix(tls->key_schedule); + ptls__key_schedule_update_hash(tls->key_schedule, ch->cookie.ch1_hash.base, ch->cookie.ch1_hash.len); + key_schedule_extract(tls->key_schedule, ptls_iovec_init(NULL, 0)); + /* ... reusing sendbuf to rebuild HRR for hash calculation */ + size_t hrr_start = emitter->buf->off; + EMIT_HELLO_RETRY_REQUEST(tls->key_schedule, ch->cookie.sent_key_share ? key_share.algorithm : NULL, { + buffer_push_extension(emitter->buf, PTLS_EXTENSION_TYPE_COOKIE, + { ptls_buffer_pushv(emitter->buf, ch->cookie.all.base, ch->cookie.all.len); }); + }); + emitter->buf->off = hrr_start; + is_second_flight = 1; + + } else if (key_share.algorithm == NULL || (properties != NULL && properties->server.enforce_retry)) { + + /* send HelloRetryRequest */ + if (ch->negotiated_groups.base == NULL) { + ret = PTLS_ALERT_MISSING_EXTENSION; + goto Exit; + } + ptls_key_exchange_algorithm_t *negotiated_group; + if ((ret = select_negotiated_group(&negotiated_group, tls->ctx->key_exchanges, ch->negotiated_groups.base, + ch->negotiated_groups.base + ch->negotiated_groups.len)) != 0) + goto Exit; + ptls__key_schedule_update_hash(tls->key_schedule, message.base, message.len); + assert(tls->key_schedule->generation == 0); + if (properties != NULL && properties->server.retry_uses_cookie) { + /* emit HRR with cookie (note: we MUST omit KeyShare if the client has specified the correct one; see 46554f0) + */ + EMIT_HELLO_RETRY_REQUEST(NULL, key_share.algorithm != NULL ? NULL : negotiated_group, { + ptls_buffer_t *sendbuf = emitter->buf; + buffer_push_extension(sendbuf, PTLS_EXTENSION_TYPE_COOKIE, { + ptls_buffer_push_block(sendbuf, 2, { + /* push to-be-signed data */ + size_t tbs_start = sendbuf->off; + ptls_buffer_push_block(sendbuf, 2, { + /* first block of the cookie data is the hash(ch1) */ + ptls_buffer_push_block(sendbuf, 1, { + size_t sz = tls->cipher_suite->hash->digest_size; + if ((ret = ptls_buffer_reserve(sendbuf, sz)) != 0) + goto Exit; + key_schedule_extract_ch1hash(tls->key_schedule, sendbuf->base + sendbuf->off); + sendbuf->off += sz; + }); + /* second is if we have sent key_share extension */ + ptls_buffer_push(sendbuf, key_share.algorithm == NULL); + /* we can add more data here */ + }); + size_t tbs_len = sendbuf->off - tbs_start; + /* push the signature */ + ptls_buffer_push_block(sendbuf, 1, { + size_t sz = tls->ctx->cipher_suites[0]->hash->digest_size; + if ((ret = ptls_buffer_reserve(sendbuf, sz)) != 0) + goto Exit; + if ((ret = calc_cookie_signature(tls, properties, negotiated_group, + ptls_iovec_init(sendbuf->base + tbs_start, tbs_len), + sendbuf->base + sendbuf->off)) != 0) + goto Exit; + sendbuf->off += sz; + }); + }); + }); + }); + if ((ret = push_change_cipher_spec(tls, emitter)) != 0) + goto Exit; + ret = PTLS_ERROR_STATELESS_RETRY; + } else { + /* invoking stateful retry; roll the key schedule and emit HRR */ + key_schedule_transform_post_ch1hash(tls->key_schedule); + key_schedule_extract(tls->key_schedule, ptls_iovec_init(NULL, 0)); + EMIT_HELLO_RETRY_REQUEST(tls->key_schedule, key_share.algorithm != NULL ? NULL : negotiated_group, {}); + if ((ret = push_change_cipher_spec(tls, emitter)) != 0) + goto Exit; + tls->state = PTLS_STATE_SERVER_EXPECT_SECOND_CLIENT_HELLO; + if (ch->psk.early_data_indication) + tls->server.early_data_skipped_bytes = 0; + ret = PTLS_ERROR_IN_PROGRESS; + } + goto Exit; + } + } + + /* handle unknown extensions */ + if ((ret = report_unknown_extensions(tls, properties, ch->unknown_extensions)) != 0) + goto Exit; + + /* try psk handshake */ + if (!is_second_flight && ch->psk.hash_end != 0 && + (ch->psk.ke_modes & ((1u << PTLS_PSK_KE_MODE_PSK) | (1u << PTLS_PSK_KE_MODE_PSK_DHE))) != 0 && + tls->ctx->encrypt_ticket != NULL && !tls->ctx->require_client_authentication) { + if ((ret = try_psk_handshake(tls, &psk_index, &accept_early_data, ch, + ptls_iovec_init(message.base, ch->psk.hash_end - message.base))) != 0) { + goto Exit; + } + } + + /* If client authentication is enabled, we always force a full handshake. + * TODO: Check for `post_handshake_auth` extension and if that is present, do not force full handshake! + * Remove also the check `!require_client_authentication` above. + * + * adjust key_schedule, determine handshake mode + */ + if (psk_index == SIZE_MAX || tls->ctx->require_client_authentication) { + ptls__key_schedule_update_hash(tls->key_schedule, message.base, message.len); + if (!is_second_flight) { + assert(tls->key_schedule->generation == 0); + key_schedule_extract(tls->key_schedule, ptls_iovec_init(NULL, 0)); + } + mode = HANDSHAKE_MODE_FULL; + if (properties != NULL) + properties->server.selected_psk_binder.len = 0; + } else { + ptls__key_schedule_update_hash(tls->key_schedule, ch->psk.hash_end, message.base + message.len - ch->psk.hash_end); + if ((ch->psk.ke_modes & (1u << PTLS_PSK_KE_MODE_PSK)) != 0) { + mode = HANDSHAKE_MODE_PSK; + } else { + assert((ch->psk.ke_modes & (1u << PTLS_PSK_KE_MODE_PSK_DHE)) != 0); + mode = HANDSHAKE_MODE_PSK_DHE; + } + tls->is_psk_handshake = 1; + if (properties != NULL) { + ptls_iovec_t *selected = &ch->psk.identities.list[psk_index].binder; + memcpy(properties->server.selected_psk_binder.base, selected->base, selected->len); + properties->server.selected_psk_binder.len = selected->len; + } + } + + if (accept_early_data && tls->ctx->max_early_data_size != 0 && psk_index == 0) { + if ((tls->pending_handshake_secret = malloc(PTLS_MAX_DIGEST_SIZE)) == NULL) { + ret = PTLS_ERROR_NO_MEMORY; + goto Exit; + } + if ((ret = derive_exporter_secret(tls, 1)) != 0) + goto Exit; + if ((ret = setup_traffic_protection(tls, 0, "c e traffic", 1, 0)) != 0) + goto Exit; + } + + /* run key-exchange, to obtain pubkey and secret */ + if (mode != HANDSHAKE_MODE_PSK) { + if (key_share.algorithm == NULL) { + ret = ch->key_shares.base != NULL ? PTLS_ALERT_HANDSHAKE_FAILURE : PTLS_ALERT_MISSING_EXTENSION; + goto Exit; + } + if ((ret = key_share.algorithm->exchange(key_share.algorithm, &pubkey, &ecdh_secret, key_share.peer_key)) != 0) + goto Exit; + tls->key_share = key_share.algorithm; + } + + /* send ServerHello */ + EMIT_SERVER_HELLO(tls->key_schedule, + { tls->ctx->random_bytes(emitter->buf->base + emitter->buf->off, PTLS_HELLO_RANDOM_SIZE); }, + { + ptls_buffer_t *sendbuf = emitter->buf; + if (mode != HANDSHAKE_MODE_PSK) { + buffer_push_extension(sendbuf, PTLS_EXTENSION_TYPE_KEY_SHARE, { + ptls_buffer_push16(sendbuf, key_share.algorithm->id); + ptls_buffer_push_block(sendbuf, 2, { ptls_buffer_pushv(sendbuf, pubkey.base, pubkey.len); }); + }); + } + if (mode != HANDSHAKE_MODE_FULL) { + buffer_push_extension(sendbuf, PTLS_EXTENSION_TYPE_PRE_SHARED_KEY, + { ptls_buffer_push16(sendbuf, (uint16_t)psk_index); }); + } + }); + if ((ret = push_change_cipher_spec(tls, emitter)) != 0) + goto Exit; + + /* create protection contexts for the handshake */ + assert(tls->key_schedule->generation == 1); + key_schedule_extract(tls->key_schedule, ecdh_secret); + if ((ret = setup_traffic_protection(tls, 1, "s hs traffic", 2, 0)) != 0) + goto Exit; + if (tls->pending_handshake_secret != NULL) { + if ((ret = derive_secret(tls->key_schedule, tls->pending_handshake_secret, "c hs traffic")) != 0) + goto Exit; + if (tls->ctx->update_traffic_key != NULL && + (ret = tls->ctx->update_traffic_key->cb(tls->ctx->update_traffic_key, tls, 0, 2, tls->pending_handshake_secret)) != 0) + goto Exit; + } else { + if ((ret = setup_traffic_protection(tls, 0, "c hs traffic", 2, 0)) != 0) + goto Exit; + if (ch->psk.early_data_indication) + tls->server.early_data_skipped_bytes = 0; + } + + /* send EncryptedExtensions */ + ptls_push_message(emitter, tls->key_schedule, PTLS_HANDSHAKE_TYPE_ENCRYPTED_EXTENSIONS, { + ptls_buffer_t *sendbuf = emitter->buf; + ptls_buffer_push_block(sendbuf, 2, { + if (tls->esni != NULL) { + /* the extension is sent even if the application does not handle server name, because otherwise the handshake + * would fail (FIXME ch->esni.nonce will be zero on HRR) */ + buffer_push_extension(sendbuf, PTLS_EXTENSION_TYPE_ENCRYPTED_SERVER_NAME, { + uint8_t response_type = PTLS_ESNI_RESPONSE_TYPE_ACCEPT; + ptls_buffer_pushv(sendbuf, &response_type, 1); + ptls_buffer_pushv(sendbuf, tls->esni->nonce, PTLS_ESNI_NONCE_SIZE); + }); + free_esni_secret(&tls->esni, 1); + } else if (tls->server_name != NULL) { + /* In this event, the server SHALL include an extension of type "server_name" in the (extended) server hello. + * The "extension_data" field of this extension SHALL be empty. (RFC 6066 section 3) */ + buffer_push_extension(sendbuf, PTLS_EXTENSION_TYPE_SERVER_NAME, {}); + } + if (tls->negotiated_protocol != NULL) { + buffer_push_extension(sendbuf, PTLS_EXTENSION_TYPE_ALPN, { + ptls_buffer_push_block(sendbuf, 2, { + ptls_buffer_push_block(sendbuf, 1, { + ptls_buffer_pushv(sendbuf, tls->negotiated_protocol, strlen(tls->negotiated_protocol)); + }); + }); + }); + } + if (tls->pending_handshake_secret != NULL) + buffer_push_extension(sendbuf, PTLS_EXTENSION_TYPE_EARLY_DATA, {}); + if ((ret = push_additional_extensions(properties, sendbuf)) != 0) + goto Exit; + }); + }); + + if (mode == HANDSHAKE_MODE_FULL) { + /* send certificate request if client authentication is activated */ + if (tls->ctx->require_client_authentication) { + ptls_push_message(emitter, tls->key_schedule, PTLS_HANDSHAKE_TYPE_CERTIFICATE_REQUEST, { + /* certificate_request_context, this field SHALL be zero length, unless the certificate + * request is used for post-handshake authentication. + */ + ptls_buffer_t *sendbuf = emitter->buf; + ptls_buffer_push(sendbuf, 0); + /* extensions */ + ptls_buffer_push_block(sendbuf, 2, { + buffer_push_extension(sendbuf, PTLS_EXTENSION_TYPE_SIGNATURE_ALGORITHMS, { + if ((ret = push_signature_algorithms(sendbuf)) != 0) + goto Exit; + }); + }); + }); + + if (ret != 0) { + goto Exit; + } + } + + ret = send_certificate_and_certificate_verify(tls, emitter, &ch->signature_algorithms, ptls_iovec_init(NULL, 0), + PTLS_SERVER_CERTIFICATE_VERIFY_CONTEXT_STRING, ch->status_request, + ch->cert_compression_algos.list, ch->cert_compression_algos.count); + + if (ret != 0) { + goto Exit; + } + } + + if ((ret = send_finished(tls, emitter)) != 0) + goto Exit; + + assert(tls->key_schedule->generation == 2); + if ((ret = key_schedule_extract(tls->key_schedule, ptls_iovec_init(NULL, 0))) != 0) + goto Exit; + if ((ret = setup_traffic_protection(tls, 1, "s ap traffic", 3, 0)) != 0) + goto Exit; + if ((ret = derive_secret(tls->key_schedule, tls->server.pending_traffic_secret, "c ap traffic")) != 0) + goto Exit; + if ((ret = derive_exporter_secret(tls, 0)) != 0) + goto Exit; + + if (tls->pending_handshake_secret != NULL) { + if (tls->ctx->omit_end_of_early_data) { + if ((ret = commission_handshake_secret(tls)) != 0) + goto Exit; + tls->state = PTLS_STATE_SERVER_EXPECT_FINISHED; + } else { + tls->state = PTLS_STATE_SERVER_EXPECT_END_OF_EARLY_DATA; + } + } else if (tls->ctx->require_client_authentication) { + tls->state = PTLS_STATE_SERVER_EXPECT_CERTIFICATE; + } else { + tls->state = PTLS_STATE_SERVER_EXPECT_FINISHED; + } + + /* send session ticket if necessary */ + if (ch->psk.ke_modes != 0 && tls->ctx->ticket_lifetime != 0) { + if ((ret = send_session_ticket(tls, emitter)) != 0) + goto Exit; + } + + if (tls->ctx->require_client_authentication) { + ret = PTLS_ERROR_IN_PROGRESS; + } else { + ret = 0; + } + +Exit: + free(pubkey.base); + if (ecdh_secret.base != NULL) { + ptls_clear_memory(ecdh_secret.base, ecdh_secret.len); + free(ecdh_secret.base); + } + free(ch); + return ret; + +#undef EMIT_SERVER_HELLO +#undef EMIT_HELLO_RETRY_REQUEST +} + +static int server_handle_end_of_early_data(ptls_t *tls, ptls_iovec_t message) +{ + int ret; + + if ((ret = commission_handshake_secret(tls)) != 0) + goto Exit; + + ptls__key_schedule_update_hash(tls->key_schedule, message.base, message.len); + tls->state = PTLS_STATE_SERVER_EXPECT_FINISHED; + ret = PTLS_ERROR_IN_PROGRESS; + +Exit: + return ret; +} + +static int server_handle_finished(ptls_t *tls, ptls_iovec_t message) +{ + int ret; + + if ((ret = verify_finished(tls, message)) != 0) + return ret; + + memcpy(tls->traffic_protection.dec.secret, tls->server.pending_traffic_secret, sizeof(tls->server.pending_traffic_secret)); + ptls_clear_memory(tls->server.pending_traffic_secret, sizeof(tls->server.pending_traffic_secret)); + if ((ret = setup_traffic_protection(tls, 0, NULL, 3, 0)) != 0) + return ret; + + ptls__key_schedule_update_hash(tls->key_schedule, message.base, message.len); + + tls->state = PTLS_STATE_SERVER_POST_HANDSHAKE; + return 0; +} + +static int update_traffic_key(ptls_t *tls, int is_enc) +{ + struct st_ptls_traffic_protection_t *tp = is_enc ? &tls->traffic_protection.enc : &tls->traffic_protection.dec; + uint8_t secret[PTLS_MAX_DIGEST_SIZE]; + int ret; + + ptls_hash_algorithm_t *hash = tls->key_schedule->hashes[0].algo; + if ((ret = hkdf_expand_label(hash, secret, hash->digest_size, ptls_iovec_init(tp->secret, hash->digest_size), "traffic upd", + ptls_iovec_init(NULL, 0), tls->key_schedule->hkdf_label_prefix)) != 0) + goto Exit; + memcpy(tp->secret, secret, sizeof(secret)); + ret = setup_traffic_protection(tls, is_enc, NULL, 3, 1); + +Exit: + ptls_clear_memory(secret, sizeof(secret)); + return ret; +} + +static int handle_key_update(ptls_t *tls, ptls_message_emitter_t *emitter, ptls_iovec_t message) +{ + const uint8_t *src = message.base + PTLS_HANDSHAKE_HEADER_SIZE, *const end = message.base + message.len; + int ret; + + /* validate */ + if (end - src != 1 || *src > 1) + return PTLS_ALERT_DECODE_ERROR; + + /* update receive key */ + if ((ret = update_traffic_key(tls, 0)) != 0) + return ret; + + if (*src) { + if (tls->ctx->update_traffic_key != NULL) + return PTLS_ALERT_UNEXPECTED_MESSAGE; + tls->needs_key_update = 1; + } + + return 0; +} + +static int parse_record_header(struct st_ptls_record_t *rec, const uint8_t *src) +{ + rec->type = src[0]; + rec->version = ntoh16(src + 1); + rec->length = ntoh16(src + 3); + + if (rec->length > + (size_t)(rec->type == PTLS_CONTENT_TYPE_APPDATA ? PTLS_MAX_ENCRYPTED_RECORD_SIZE : PTLS_MAX_PLAINTEXT_RECORD_SIZE)) + return PTLS_ALERT_DECODE_ERROR; + + return 0; +} + +static int parse_record(ptls_t *tls, struct st_ptls_record_t *rec, const uint8_t *src, size_t *len) +{ + int ret; + + if (tls->recvbuf.rec.base == NULL && *len >= 5) { + /* fast path */ + if ((ret = parse_record_header(rec, src)) != 0) + return ret; + if (5 + rec->length <= *len) { + rec->fragment = src + 5; + *len = rec->length + 5; + return 0; + } + } + + /* slow path */ + const uint8_t *const end = src + *len; + *rec = (struct st_ptls_record_t){0}; + + if (tls->recvbuf.rec.base == NULL) { + ptls_buffer_init(&tls->recvbuf.rec, "", 0); + if ((ret = ptls_buffer_reserve(&tls->recvbuf.rec, 5)) != 0) + return ret; + } + + /* fill and parse the header */ + while (tls->recvbuf.rec.off < 5) { + if (src == end) + return PTLS_ERROR_IN_PROGRESS; + tls->recvbuf.rec.base[tls->recvbuf.rec.off++] = *src++; + } + if ((ret = parse_record_header(rec, tls->recvbuf.rec.base)) != 0) + return ret; + + /* fill the fragment */ + size_t addlen = rec->length + 5 - tls->recvbuf.rec.off; + if (addlen != 0) { + if ((ret = ptls_buffer_reserve(&tls->recvbuf.rec, addlen)) != 0) + return ret; + if (addlen > (size_t)(end - src)) + addlen = end - src; + if (addlen != 0) { + memcpy(tls->recvbuf.rec.base + tls->recvbuf.rec.off, src, addlen); + tls->recvbuf.rec.off += addlen; + src += addlen; + } + } + + /* set rec->fragment if a complete record has been parsed */ + if (tls->recvbuf.rec.off == rec->length + 5) { + rec->fragment = tls->recvbuf.rec.base + 5; + ret = 0; + } else { + ret = PTLS_ERROR_IN_PROGRESS; + } + + *len -= end - src; + return ret; +} + +static void update_open_count(ptls_context_t *ctx, ssize_t delta) +{ + if (ctx->update_open_count != NULL) + ctx->update_open_count->cb(ctx->update_open_count, delta); +} + +static ptls_t *new_instance(ptls_context_t *ctx, int is_server) +{ + ptls_t *tls; + + assert(ctx->get_time != NULL && "please set ctx->get_time to `&ptls_get_time`; see #92"); + + if ((tls = malloc(sizeof(*tls))) == NULL) + return NULL; + + update_open_count(ctx, 1); + *tls = (ptls_t){ctx}; + tls->is_server = is_server; + tls->send_change_cipher_spec = ctx->send_change_cipher_spec; + tls->skip_tracing = ptls_default_skip_tracing; + return tls; +} + +ptls_t *ptls_client_new(ptls_context_t *ctx) +{ + ptls_t *tls = new_instance(ctx, 0); + tls->state = PTLS_STATE_CLIENT_HANDSHAKE_START; + tls->ctx->random_bytes(tls->client_random, sizeof(tls->client_random)); + log_client_random(tls); + if (tls->send_change_cipher_spec) { + tls->client.legacy_session_id = + ptls_iovec_init(tls->client.legacy_session_id_buf, sizeof(tls->client.legacy_session_id_buf)); + tls->ctx->random_bytes(tls->client.legacy_session_id.base, tls->client.legacy_session_id.len); + } + + PTLS_PROBE(NEW, tls, 0); + return tls; +} + +ptls_t *ptls_server_new(ptls_context_t *ctx) +{ + ptls_t *tls = new_instance(ctx, 1); + tls->state = PTLS_STATE_SERVER_EXPECT_CLIENT_HELLO; + tls->server.early_data_skipped_bytes = UINT32_MAX; + + PTLS_PROBE(NEW, tls, 1); + return tls; +} + +void ptls_free(ptls_t *tls) +{ + PTLS_PROBE0(FREE, tls); + ptls_buffer_dispose(&tls->recvbuf.rec); + ptls_buffer_dispose(&tls->recvbuf.mess); + free_exporter_master_secret(tls, 1); + free_exporter_master_secret(tls, 0); + if (tls->esni != NULL) + free_esni_secret(&tls->esni, tls->is_server); + if (tls->key_schedule != NULL) + key_schedule_free(tls->key_schedule); + if (tls->traffic_protection.dec.aead != NULL) + ptls_aead_free(tls->traffic_protection.dec.aead); + if (tls->traffic_protection.enc.aead != NULL) + ptls_aead_free(tls->traffic_protection.enc.aead); + free(tls->server_name); + free(tls->negotiated_protocol); + if (tls->is_server) { + /* nothing to do */ + } else { + if (tls->client.key_share_ctx != NULL) + tls->client.key_share_ctx->on_exchange(&tls->client.key_share_ctx, 1, NULL, ptls_iovec_init(NULL, 0)); + if (tls->client.certificate_request.context.base != NULL) + free(tls->client.certificate_request.context.base); + } + if (tls->certificate_verify.cb != NULL) { + tls->certificate_verify.cb(tls->certificate_verify.verify_ctx, ptls_iovec_init(NULL, 0), ptls_iovec_init(NULL, 0)); + } + if (tls->pending_handshake_secret != NULL) { + ptls_clear_memory(tls->pending_handshake_secret, PTLS_MAX_DIGEST_SIZE); + free(tls->pending_handshake_secret); + } + update_open_count(tls->ctx, -1); + ptls_clear_memory(tls, sizeof(*tls)); + free(tls); +} + +ptls_context_t *ptls_get_context(ptls_t *tls) +{ + return tls->ctx; +} + +void ptls_set_context(ptls_t *tls, ptls_context_t *ctx) +{ + update_open_count(ctx, 1); + update_open_count(tls->ctx, -1); + tls->ctx = ctx; +} + +ptls_iovec_t ptls_get_client_random(ptls_t *tls) +{ + return ptls_iovec_init(tls->client_random, PTLS_HELLO_RANDOM_SIZE); +} + +ptls_cipher_suite_t *ptls_get_cipher(ptls_t *tls) +{ + return tls->cipher_suite; +} + +const char *ptls_get_server_name(ptls_t *tls) +{ + return tls->server_name; +} + +int ptls_set_server_name(ptls_t *tls, const char *server_name, size_t server_name_len) +{ + char *duped = NULL; + + if (server_name != NULL) { + if (server_name_len == 0) + server_name_len = strlen(server_name); + if ((duped = malloc(server_name_len + 1)) == NULL) + return PTLS_ERROR_NO_MEMORY; + memcpy(duped, server_name, server_name_len); + duped[server_name_len] = '\0'; + } + + free(tls->server_name); + tls->server_name = duped; + + return 0; +} + +const char *ptls_get_negotiated_protocol(ptls_t *tls) +{ + return tls->negotiated_protocol; +} + +int ptls_set_negotiated_protocol(ptls_t *tls, const char *protocol, size_t protocol_len) +{ + char *duped = NULL; + + if (protocol != NULL) { + if (protocol_len == 0) + protocol_len = strlen(protocol); + if ((duped = malloc(protocol_len + 1)) == NULL) + return PTLS_ERROR_NO_MEMORY; + memcpy(duped, protocol, protocol_len); + duped[protocol_len] = '\0'; + } + + free(tls->negotiated_protocol); + tls->negotiated_protocol = duped; + + return 0; +} + +int ptls_handshake_is_complete(ptls_t *tls) +{ + return tls->state >= PTLS_STATE_POST_HANDSHAKE_MIN; +} + +int ptls_is_psk_handshake(ptls_t *tls) +{ + return tls->is_psk_handshake; +} + +void **ptls_get_data_ptr(ptls_t *tls) +{ + return &tls->data_ptr; +} + +int ptls_skip_tracing(ptls_t *tls) +{ + return tls->skip_tracing; +} + +void ptls_set_skip_tracing(ptls_t *tls, int skip_tracing) +{ + tls->skip_tracing = skip_tracing; +} + +static int handle_client_handshake_message(ptls_t *tls, ptls_message_emitter_t *emitter, ptls_iovec_t message, int is_end_of_record, + ptls_handshake_properties_t *properties) +{ + uint8_t type = message.base[0]; + int ret; + + switch (tls->state) { + case PTLS_STATE_CLIENT_EXPECT_SERVER_HELLO: + case PTLS_STATE_CLIENT_EXPECT_SECOND_SERVER_HELLO: + if (type == PTLS_HANDSHAKE_TYPE_SERVER_HELLO && is_end_of_record) { + ret = client_handle_hello(tls, emitter, message, properties); + } else { + ret = PTLS_ALERT_UNEXPECTED_MESSAGE; + } + break; + case PTLS_STATE_CLIENT_EXPECT_ENCRYPTED_EXTENSIONS: + if (type == PTLS_HANDSHAKE_TYPE_ENCRYPTED_EXTENSIONS) { + ret = client_handle_encrypted_extensions(tls, message, properties); + } else { + ret = PTLS_ALERT_UNEXPECTED_MESSAGE; + } + break; + case PTLS_STATE_CLIENT_EXPECT_CERTIFICATE_REQUEST_OR_CERTIFICATE: + if (type == PTLS_HANDSHAKE_TYPE_CERTIFICATE_REQUEST) { + ret = client_handle_certificate_request(tls, message, properties); + break; + } + /* fall through */ + case PTLS_STATE_CLIENT_EXPECT_CERTIFICATE: + switch (type) { + case PTLS_HANDSHAKE_TYPE_CERTIFICATE: + ret = client_handle_certificate(tls, message); + break; + case PTLS_HANDSHAKE_TYPE_COMPRESSED_CERTIFICATE: + ret = client_handle_compressed_certificate(tls, message); + break; + default: + ret = PTLS_ALERT_UNEXPECTED_MESSAGE; + break; + } + break; + case PTLS_STATE_CLIENT_EXPECT_CERTIFICATE_VERIFY: + if (type == PTLS_HANDSHAKE_TYPE_CERTIFICATE_VERIFY) { + ret = client_handle_certificate_verify(tls, message); + } else { + ret = PTLS_ALERT_UNEXPECTED_MESSAGE; + } + break; + case PTLS_STATE_CLIENT_EXPECT_FINISHED: + if (type == PTLS_HANDSHAKE_TYPE_FINISHED && is_end_of_record) { + ret = client_handle_finished(tls, emitter, message); + } else { + ret = PTLS_ALERT_UNEXPECTED_MESSAGE; + } + break; + case PTLS_STATE_CLIENT_POST_HANDSHAKE: + switch (type) { + case PTLS_HANDSHAKE_TYPE_NEW_SESSION_TICKET: + ret = client_handle_new_session_ticket(tls, message); + break; + case PTLS_HANDSHAKE_TYPE_KEY_UPDATE: + ret = handle_key_update(tls, emitter, message); + break; + default: + ret = PTLS_ALERT_UNEXPECTED_MESSAGE; + break; + } + break; + default: + assert(!"unexpected state"); + ret = PTLS_ALERT_INTERNAL_ERROR; + break; + } + + PTLS_PROBE(RECEIVE_MESSAGE, tls, message.base[0], message.base + PTLS_HANDSHAKE_HEADER_SIZE, + message.len - PTLS_HANDSHAKE_HEADER_SIZE, ret); + + return ret; +} + +static int handle_server_handshake_message(ptls_t *tls, ptls_message_emitter_t *emitter, ptls_iovec_t message, int is_end_of_record, + ptls_handshake_properties_t *properties) +{ + uint8_t type = message.base[0]; + int ret; + + switch (tls->state) { + case PTLS_STATE_SERVER_EXPECT_CLIENT_HELLO: + case PTLS_STATE_SERVER_EXPECT_SECOND_CLIENT_HELLO: + if (type == PTLS_HANDSHAKE_TYPE_CLIENT_HELLO && is_end_of_record) { + ret = server_handle_hello(tls, emitter, message, properties); + } else { + ret = PTLS_ALERT_HANDSHAKE_FAILURE; + } + break; + case PTLS_STATE_SERVER_EXPECT_CERTIFICATE: + if (type == PTLS_HANDSHAKE_TYPE_CERTIFICATE) { + ret = server_handle_certificate(tls, message); + } else { + ret = PTLS_ALERT_UNEXPECTED_MESSAGE; + } + break; + case PTLS_STATE_SERVER_EXPECT_CERTIFICATE_VERIFY: + if (type == PTLS_HANDSHAKE_TYPE_CERTIFICATE_VERIFY) { + ret = server_handle_certificate_verify(tls, message); + } else { + ret = PTLS_ALERT_UNEXPECTED_MESSAGE; + } + break; + case PTLS_STATE_SERVER_EXPECT_END_OF_EARLY_DATA: + assert(!tls->ctx->omit_end_of_early_data); + if (type == PTLS_HANDSHAKE_TYPE_END_OF_EARLY_DATA) { + ret = server_handle_end_of_early_data(tls, message); + } else { + ret = PTLS_ALERT_UNEXPECTED_MESSAGE; + } + break; + case PTLS_STATE_SERVER_EXPECT_FINISHED: + if (type == PTLS_HANDSHAKE_TYPE_FINISHED && is_end_of_record) { + ret = server_handle_finished(tls, message); + } else { + ret = PTLS_ALERT_HANDSHAKE_FAILURE; + } + break; + case PTLS_STATE_SERVER_POST_HANDSHAKE: + switch (type) { + case PTLS_HANDSHAKE_TYPE_KEY_UPDATE: + ret = handle_key_update(tls, emitter, message); + break; + default: + ret = PTLS_ALERT_UNEXPECTED_MESSAGE; + break; + } + break; + default: + assert(!"unexpected state"); + ret = PTLS_ALERT_INTERNAL_ERROR; + break; + } + + PTLS_PROBE(RECEIVE_MESSAGE, tls, message.base[0], message.base + PTLS_HANDSHAKE_HEADER_SIZE, + message.len - PTLS_HANDSHAKE_HEADER_SIZE, ret); + + return ret; +} + +static int handle_alert(ptls_t *tls, const uint8_t *src, size_t len) +{ + if (len != 2) + return PTLS_ALERT_DECODE_ERROR; + + uint8_t desc = src[1]; + + /* all fatal alerts and USER_CANCELLED warning tears down the connection immediately, regardless of the transmitted level */ + return PTLS_ALERT_TO_PEER_ERROR(desc); +} + +static int message_buffer_is_overflow(ptls_context_t *ctx, size_t size) +{ + if (ctx->max_buffer_size == 0) + return 0; + if (size <= ctx->max_buffer_size) + return 0; + return 1; +} + +static int handle_handshake_record(ptls_t *tls, + int (*cb)(ptls_t *tls, ptls_message_emitter_t *emitter, ptls_iovec_t message, + int is_end_of_record, ptls_handshake_properties_t *properties), + ptls_message_emitter_t *emitter, struct st_ptls_record_t *rec, + ptls_handshake_properties_t *properties) +{ + int ret; + + /* handshake */ + if (rec->type != PTLS_CONTENT_TYPE_HANDSHAKE) + return PTLS_ALERT_DECODE_ERROR; + + /* flatten the unhandled messages */ + const uint8_t *src, *src_end; + if (tls->recvbuf.mess.base == NULL) { + src = rec->fragment; + src_end = src + rec->length; + } else { + if (message_buffer_is_overflow(tls->ctx, tls->recvbuf.mess.off + rec->length)) + return PTLS_ALERT_HANDSHAKE_FAILURE; + if ((ret = ptls_buffer_reserve(&tls->recvbuf.mess, rec->length)) != 0) + return ret; + memcpy(tls->recvbuf.mess.base + tls->recvbuf.mess.off, rec->fragment, rec->length); + tls->recvbuf.mess.off += rec->length; + src = tls->recvbuf.mess.base; + src_end = src + tls->recvbuf.mess.off; + } + + /* handle the messages */ + ret = PTLS_ERROR_IN_PROGRESS; + while (src_end - src >= 4) { + size_t mess_len = 4 + ntoh24(src + 1); + if (src_end - src < (int)mess_len) + break; + ret = cb(tls, emitter, ptls_iovec_init(src, mess_len), src_end - src == mess_len, properties); + switch (ret) { + case 0: + case PTLS_ERROR_IN_PROGRESS: + break; + default: + ptls_buffer_dispose(&tls->recvbuf.mess); + return ret; + } + src += mess_len; + } + + /* keep last partial message in buffer */ + if (src != src_end) { + size_t new_size = src_end - src; + if (message_buffer_is_overflow(tls->ctx, new_size)) + return PTLS_ALERT_HANDSHAKE_FAILURE; + if (tls->recvbuf.mess.base == NULL) { + ptls_buffer_init(&tls->recvbuf.mess, "", 0); + if ((ret = ptls_buffer_reserve(&tls->recvbuf.mess, new_size)) != 0) + return ret; + memcpy(tls->recvbuf.mess.base, src, new_size); + } else { + memmove(tls->recvbuf.mess.base, src, new_size); + } + tls->recvbuf.mess.off = new_size; + ret = PTLS_ERROR_IN_PROGRESS; + } else { + ptls_buffer_dispose(&tls->recvbuf.mess); + } + + return ret; +} + +static int handle_input(ptls_t *tls, ptls_message_emitter_t *emitter, ptls_buffer_t *decryptbuf, const void *input, size_t *inlen, + ptls_handshake_properties_t *properties) +{ + struct st_ptls_record_t rec; + int ret; + + /* extract the record */ + if ((ret = parse_record(tls, &rec, input, inlen)) != 0) + return ret; + assert(rec.fragment != NULL); + + /* decrypt the record */ + if (rec.type == PTLS_CONTENT_TYPE_CHANGE_CIPHER_SPEC) { + if (tls->state < PTLS_STATE_POST_HANDSHAKE_MIN) { + if (!(rec.length == 1 && rec.fragment[0] == 0x01)) + return PTLS_ALERT_ILLEGAL_PARAMETER; + } else { + return PTLS_ALERT_HANDSHAKE_FAILURE; + } + ret = PTLS_ERROR_IN_PROGRESS; + goto NextRecord; + } + if (tls->traffic_protection.dec.aead != NULL && rec.type != PTLS_CONTENT_TYPE_ALERT) { + size_t decrypted_length; + if (rec.type != PTLS_CONTENT_TYPE_APPDATA) + return PTLS_ALERT_HANDSHAKE_FAILURE; + if ((ret = ptls_buffer_reserve(decryptbuf, 5 + rec.length)) != 0) + return ret; + if ((ret = aead_decrypt(&tls->traffic_protection.dec, decryptbuf->base + decryptbuf->off, &decrypted_length, rec.fragment, + rec.length)) != 0) { + if (tls->is_server && tls->server.early_data_skipped_bytes != UINT32_MAX) + goto ServerSkipEarlyData; + return ret; + } + rec.length = decrypted_length; + rec.fragment = decryptbuf->base + decryptbuf->off; + /* skip padding */ + for (; rec.length != 0; --rec.length) + if (rec.fragment[rec.length - 1] != 0) + break; + if (rec.length == 0) + return PTLS_ALERT_UNEXPECTED_MESSAGE; + rec.type = rec.fragment[--rec.length]; + } else if (rec.type == PTLS_CONTENT_TYPE_APPDATA && tls->is_server && tls->server.early_data_skipped_bytes != UINT32_MAX) { + goto ServerSkipEarlyData; + } + + if (tls->recvbuf.mess.base != NULL || rec.type == PTLS_CONTENT_TYPE_HANDSHAKE) { + /* handshake record */ + ret = handle_handshake_record(tls, tls->is_server ? handle_server_handshake_message : handle_client_handshake_message, + emitter, &rec, properties); + } else { + /* handling of an alert or an application record */ + switch (rec.type) { + case PTLS_CONTENT_TYPE_APPDATA: + if (tls->state >= PTLS_STATE_POST_HANDSHAKE_MIN) { + decryptbuf->off += rec.length; + ret = 0; + } else if (tls->state == PTLS_STATE_SERVER_EXPECT_END_OF_EARLY_DATA) { + if (tls->traffic_protection.dec.aead != NULL) + decryptbuf->off += rec.length; + ret = 0; + } else { + ret = PTLS_ALERT_UNEXPECTED_MESSAGE; + } + break; + case PTLS_CONTENT_TYPE_ALERT: + ret = handle_alert(tls, rec.fragment, rec.length); + break; + default: + ret = PTLS_ALERT_UNEXPECTED_MESSAGE; + break; + } + } + +NextRecord: + ptls_buffer_dispose(&tls->recvbuf.rec); + return ret; + +ServerSkipEarlyData: + tls->server.early_data_skipped_bytes += (uint32_t)rec.length; + if (tls->server.early_data_skipped_bytes > PTLS_MAX_EARLY_DATA_SKIP_SIZE) + return PTLS_ALERT_HANDSHAKE_FAILURE; + ret = PTLS_ERROR_IN_PROGRESS; + goto NextRecord; +} + +static void init_record_message_emitter(ptls_t *tls, struct st_ptls_record_message_emitter_t *emitter, ptls_buffer_t *sendbuf) +{ + *emitter = (struct st_ptls_record_message_emitter_t){ + {sendbuf, &tls->traffic_protection.enc, 5, begin_record_message, commit_record_message}}; +} + +int ptls_handshake(ptls_t *tls, ptls_buffer_t *_sendbuf, const void *input, size_t *inlen, ptls_handshake_properties_t *properties) +{ + struct st_ptls_record_message_emitter_t emitter; + int ret; + + assert(tls->state < PTLS_STATE_POST_HANDSHAKE_MIN); + + init_record_message_emitter(tls, &emitter, _sendbuf); + size_t sendbuf_orig_off = emitter.super.buf->off; + + /* special handlings */ + switch (tls->state) { + case PTLS_STATE_CLIENT_HANDSHAKE_START: { + assert(input == NULL || *inlen == 0); + assert(tls->ctx->key_exchanges[0] != NULL); + return send_client_hello(tls, &emitter.super, properties, NULL); + } + default: + break; + } + + const uint8_t *src = input, *const src_end = src + *inlen; + ptls_buffer_t decryptbuf; + + ptls_buffer_init(&decryptbuf, "", 0); + + /* perform handhake until completion or until all the input has been swallowed */ + ret = PTLS_ERROR_IN_PROGRESS; + while (ret == PTLS_ERROR_IN_PROGRESS && src != src_end) { + size_t consumed = src_end - src; + ret = handle_input(tls, &emitter.super, &decryptbuf, src, &consumed, properties); + src += consumed; + assert(decryptbuf.off == 0); + } + + ptls_buffer_dispose(&decryptbuf); + + switch (ret) { + case 0: + case PTLS_ERROR_IN_PROGRESS: + case PTLS_ERROR_STATELESS_RETRY: + break; + default: + /* flush partially written response */ + ptls_clear_memory(emitter.super.buf->base + sendbuf_orig_off, emitter.super.buf->off - sendbuf_orig_off); + emitter.super.buf->off = sendbuf_orig_off; + /* send alert immediately */ + if (PTLS_ERROR_GET_CLASS(ret) != PTLS_ERROR_CLASS_PEER_ALERT) + if (ptls_send_alert(tls, emitter.super.buf, PTLS_ALERT_LEVEL_FATAL, + PTLS_ERROR_GET_CLASS(ret) == PTLS_ERROR_CLASS_SELF_ALERT ? ret : PTLS_ALERT_INTERNAL_ERROR) != 0) + emitter.super.buf->off = sendbuf_orig_off; + break; + } + + *inlen -= src_end - src; + return ret; +} + +int ptls_receive(ptls_t *tls, ptls_buffer_t *decryptbuf, const void *_input, size_t *inlen) +{ + const uint8_t *input = (const uint8_t *)_input, *const end = input + *inlen; + size_t decryptbuf_orig_size = decryptbuf->off; + int ret = 0; + + assert(tls->state >= PTLS_STATE_SERVER_EXPECT_END_OF_EARLY_DATA); + + /* loop until we decrypt some application data (or an error) */ + while (ret == 0 && input != end && decryptbuf_orig_size == decryptbuf->off) { + size_t consumed = end - input; + ret = handle_input(tls, NULL, decryptbuf, input, &consumed, NULL); + input += consumed; + + switch (ret) { + case 0: + break; + case PTLS_ERROR_IN_PROGRESS: + ret = 0; + break; + case PTLS_ERROR_CLASS_PEER_ALERT + PTLS_ALERT_CLOSE_NOTIFY: + /* TODO send close alert */ + break; + default: + if (PTLS_ERROR_GET_CLASS(ret) == PTLS_ERROR_CLASS_SELF_ALERT) { + /* TODO send alert */ + } + break; + } + } + + *inlen -= end - input; + + return ret; +} + +static int update_send_key(ptls_t *tls, ptls_buffer_t *_sendbuf, int request_update) +{ + struct st_ptls_record_message_emitter_t emitter; + int ret; + + init_record_message_emitter(tls, &emitter, _sendbuf); + size_t sendbuf_orig_off = emitter.super.buf->off; + + ptls_push_message(&emitter.super, NULL, PTLS_HANDSHAKE_TYPE_KEY_UPDATE, + { ptls_buffer_push(emitter.super.buf, !!request_update); }); + if ((ret = update_traffic_key(tls, 1)) != 0) + goto Exit; + ret = 0; + +Exit: + if (ret != 0) + emitter.super.buf->off = sendbuf_orig_off; + return ret; +} + +int ptls_send(ptls_t *tls, ptls_buffer_t *sendbuf, const void *input, size_t inlen) +{ + assert(tls->traffic_protection.enc.aead != NULL); + + /* "For AES-GCM, up to 2^24.5 full-size records (about 24 million) may be encrypted on a given connection while keeping a + * safety margin of approximately 2^-57 for Authenticated Encryption (AE) security." (RFC 8446 section 5.5) + */ + if (tls->traffic_protection.enc.seq >= 16777216) + tls->needs_key_update = 1; + + if (tls->needs_key_update) { + int ret; + if ((ret = update_send_key(tls, sendbuf, tls->key_update_send_request)) != 0) + return ret; + tls->needs_key_update = 0; + tls->key_update_send_request = 0; + } + + return buffer_push_encrypted_records(sendbuf, PTLS_CONTENT_TYPE_APPDATA, input, inlen, &tls->traffic_protection.enc); +} + +int ptls_update_key(ptls_t *tls, int request_update) +{ + assert(tls->ctx->update_traffic_key == NULL); + tls->needs_key_update = 1; + tls->key_update_send_request = request_update; + return 0; +} + +size_t ptls_get_record_overhead(ptls_t *tls) +{ + return 6 + tls->traffic_protection.enc.aead->algo->tag_size; +} + +int ptls_send_alert(ptls_t *tls, ptls_buffer_t *sendbuf, uint8_t level, uint8_t description) +{ + size_t rec_start = sendbuf->off; + int ret = 0; + + buffer_push_record(sendbuf, PTLS_CONTENT_TYPE_ALERT, { ptls_buffer_push(sendbuf, level, description); }); + /* encrypt the alert if we have the encryption keys, unless when it is the early data key */ + if (tls->traffic_protection.enc.aead != NULL && !(tls->state <= PTLS_STATE_CLIENT_EXPECT_FINISHED)) { + if ((ret = buffer_encrypt_record(sendbuf, rec_start, &tls->traffic_protection.enc)) != 0) + goto Exit; + } + +Exit: + return ret; +} + +int ptls_export_secret(ptls_t *tls, void *output, size_t outlen, const char *label, ptls_iovec_t context_value, int is_early) +{ + ptls_hash_algorithm_t *algo = tls->key_schedule->hashes[0].algo; + uint8_t *master_secret = is_early ? tls->exporter_master_secret.early : tls->exporter_master_secret.one_rtt, + derived_secret[PTLS_MAX_DIGEST_SIZE], context_value_hash[PTLS_MAX_DIGEST_SIZE]; + int ret; + + if (master_secret == NULL) { + if (is_early) { + switch (tls->state) { + case PTLS_STATE_CLIENT_HANDSHAKE_START: + case PTLS_STATE_SERVER_EXPECT_CLIENT_HELLO: + ret = PTLS_ERROR_IN_PROGRESS; + break; + default: + ret = PTLS_ERROR_NOT_AVAILABLE; + break; + } + } else { + ret = PTLS_ERROR_IN_PROGRESS; + } + return ret; + } + + if ((ret = ptls_calc_hash(algo, context_value_hash, context_value.base, context_value.len)) != 0) + return ret; + + if ((ret = hkdf_expand_label(algo, derived_secret, algo->digest_size, ptls_iovec_init(master_secret, algo->digest_size), label, + ptls_iovec_init(algo->empty_digest, algo->digest_size), tls->key_schedule->hkdf_label_prefix)) != + 0) + goto Exit; + ret = hkdf_expand_label(algo, output, outlen, ptls_iovec_init(derived_secret, algo->digest_size), "exporter", + ptls_iovec_init(context_value_hash, algo->digest_size), tls->key_schedule->hkdf_label_prefix); + +Exit: + ptls_clear_memory(derived_secret, sizeof(derived_secret)); + ptls_clear_memory(context_value_hash, sizeof(context_value_hash)); + return ret; +} + +struct st_picotls_hmac_context_t { + ptls_hash_context_t super; + ptls_hash_algorithm_t *algo; + ptls_hash_context_t *hash; + uint8_t key[1]; +}; + +static void hmac_update(ptls_hash_context_t *_ctx, const void *src, size_t len) +{ + struct st_picotls_hmac_context_t *ctx = (struct st_picotls_hmac_context_t *)_ctx; + ctx->hash->update(ctx->hash, src, len); +} + +static void hmac_apply_key(struct st_picotls_hmac_context_t *ctx, uint8_t pad) +{ + size_t i; + + for (i = 0; i != ctx->algo->block_size; ++i) + ctx->key[i] ^= pad; + ctx->hash->update(ctx->hash, ctx->key, ctx->algo->block_size); + for (i = 0; i != ctx->algo->block_size; ++i) + ctx->key[i] ^= pad; +} + +static void hmac_final(ptls_hash_context_t *_ctx, void *md, ptls_hash_final_mode_t mode) +{ + struct st_picotls_hmac_context_t *ctx = (struct st_picotls_hmac_context_t *)_ctx; + + assert(mode != PTLS_HASH_FINAL_MODE_SNAPSHOT || !"not supported"); + + if (md != NULL) { + ctx->hash->final(ctx->hash, md, PTLS_HASH_FINAL_MODE_RESET); + hmac_apply_key(ctx, 0x5c); + ctx->hash->update(ctx->hash, md, ctx->algo->digest_size); + } + ctx->hash->final(ctx->hash, md, mode); + + switch (mode) { + case PTLS_HASH_FINAL_MODE_FREE: + ptls_clear_memory(ctx->key, ctx->algo->block_size); + free(ctx); + break; + case PTLS_HASH_FINAL_MODE_RESET: + hmac_apply_key(ctx, 0x36); + break; + default: + assert(!"FIXME"); + break; + } +} + +int ptls_calc_hash(ptls_hash_algorithm_t *algo, void *output, const void *src, size_t len) +{ + ptls_hash_context_t *ctx; + + if ((ctx = algo->create()) == NULL) + return PTLS_ERROR_NO_MEMORY; + ctx->update(ctx, src, len); + ctx->final(ctx, output, PTLS_HASH_FINAL_MODE_FREE); + return 0; +} + +ptls_hash_context_t *ptls_hmac_create(ptls_hash_algorithm_t *algo, const void *key, size_t key_size) +{ + struct st_picotls_hmac_context_t *ctx; + + assert(key_size <= algo->block_size); + + if ((ctx = malloc(offsetof(struct st_picotls_hmac_context_t, key) + algo->block_size)) == NULL) + return NULL; + + *ctx = (struct st_picotls_hmac_context_t){{hmac_update, hmac_final}, algo}; + if ((ctx->hash = algo->create()) == NULL) { + free(ctx); + return NULL; + } + memset(ctx->key, 0, algo->block_size); + memcpy(ctx->key, key, key_size); + + hmac_apply_key(ctx, 0x36); + + return &ctx->super; +} + +int ptls_hkdf_extract(ptls_hash_algorithm_t *algo, void *output, ptls_iovec_t salt, ptls_iovec_t ikm) +{ + ptls_hash_context_t *hash; + + if (salt.len == 0) + salt = ptls_iovec_init(zeroes_of_max_digest_size, algo->digest_size); + + if ((hash = ptls_hmac_create(algo, salt.base, salt.len)) == NULL) + return PTLS_ERROR_NO_MEMORY; + hash->update(hash, ikm.base, ikm.len); + hash->final(hash, output, PTLS_HASH_FINAL_MODE_FREE); + return 0; +} + +int ptls_hkdf_expand(ptls_hash_algorithm_t *algo, void *output, size_t outlen, ptls_iovec_t prk, ptls_iovec_t info) +{ + ptls_hash_context_t *hmac = NULL; + size_t i; + uint8_t digest[PTLS_MAX_DIGEST_SIZE]; + + for (i = 0; (i * algo->digest_size) < outlen; ++i) { + if (hmac == NULL) { + if ((hmac = ptls_hmac_create(algo, prk.base, prk.len)) == NULL) + return PTLS_ERROR_NO_MEMORY; + } else { + hmac->update(hmac, digest, algo->digest_size); + } + hmac->update(hmac, info.base, info.len); + uint8_t gen = (uint8_t)(i + 1); + hmac->update(hmac, &gen, 1); + hmac->final(hmac, digest, 1); + + size_t off_start = i * algo->digest_size, off_end = off_start + algo->digest_size; + if (off_end > outlen) + off_end = outlen; + memcpy((uint8_t *)output + off_start, digest, off_end - off_start); + } + + if (hmac != NULL) + hmac->final(hmac, NULL, PTLS_HASH_FINAL_MODE_FREE); + + ptls_clear_memory(digest, algo->digest_size); + + return 0; +} + +int hkdf_expand_label(ptls_hash_algorithm_t *algo, void *output, size_t outlen, ptls_iovec_t secret, const char *label, + ptls_iovec_t hash_value, const char *label_prefix) +{ + ptls_buffer_t hkdf_label; + uint8_t hkdf_label_buf[80]; + int ret; + + assert(label_prefix != NULL); + + ptls_buffer_init(&hkdf_label, hkdf_label_buf, sizeof(hkdf_label_buf)); + + ptls_buffer_push16(&hkdf_label, (uint16_t)outlen); + ptls_buffer_push_block(&hkdf_label, 1, { + ptls_buffer_pushv(&hkdf_label, label_prefix, strlen(label_prefix)); + ptls_buffer_pushv(&hkdf_label, label, strlen(label)); + }); + ptls_buffer_push_block(&hkdf_label, 1, { ptls_buffer_pushv(&hkdf_label, hash_value.base, hash_value.len); }); + + ret = ptls_hkdf_expand(algo, output, outlen, secret, ptls_iovec_init(hkdf_label.base, hkdf_label.off)); + +Exit: + ptls_buffer_dispose(&hkdf_label); + return ret; +} + +int ptls_hkdf_expand_label(ptls_hash_algorithm_t *algo, void *output, size_t outlen, ptls_iovec_t secret, const char *label, + ptls_iovec_t hash_value, const char *label_prefix) +{ + /* the handshake layer should call hkdf_expand_label directly, always setting key_schedule->hkdf_label_prefix as the + * argument */ + if (label_prefix == NULL) + label_prefix = PTLS_HKDF_EXPAND_LABEL_PREFIX; + return hkdf_expand_label(algo, output, outlen, secret, label, hash_value, label_prefix); +} + +ptls_cipher_context_t *ptls_cipher_new(ptls_cipher_algorithm_t *algo, int is_enc, const void *key) +{ + ptls_cipher_context_t *ctx; + + if ((ctx = (ptls_cipher_context_t *)malloc(algo->context_size)) == NULL) + return NULL; + *ctx = (ptls_cipher_context_t){algo}; + if (algo->setup_crypto(ctx, is_enc, key) != 0) { + free(ctx); + ctx = NULL; + } + return ctx; +} + +void ptls_cipher_free(ptls_cipher_context_t *ctx) +{ + ctx->do_dispose(ctx); + free(ctx); +} + +ptls_aead_context_t *new_aead(ptls_aead_algorithm_t *aead, ptls_hash_algorithm_t *hash, int is_enc, const void *secret, + ptls_iovec_t hash_value, const char *label_prefix) +{ + ptls_aead_context_t *ctx = NULL; + uint8_t key_iv[PTLS_MAX_SECRET_SIZE + PTLS_MAX_IV_SIZE]; + int ret; + + if ((ret = get_traffic_key(hash, key_iv, aead->key_size, 0, secret, hash_value, label_prefix)) != 0) + goto Exit; + if ((ret = get_traffic_key(hash, key_iv + aead->key_size, aead->iv_size, 1, secret, hash_value, label_prefix)) != 0) + goto Exit; + ctx = ptls_aead_new_direct(aead, is_enc, key_iv, key_iv + aead->key_size); + +Exit: + ptls_clear_memory(key_iv, sizeof(key_iv)); + return ctx; +} + +ptls_aead_context_t *ptls_aead_new(ptls_aead_algorithm_t *aead, ptls_hash_algorithm_t *hash, int is_enc, const void *secret, + const char *label_prefix) +{ + return new_aead(aead, hash, is_enc, secret, ptls_iovec_init(NULL, 0), label_prefix); +} + +ptls_aead_context_t *ptls_aead_new_direct(ptls_aead_algorithm_t *aead, int is_enc, const void *key, const void *iv) +{ + ptls_aead_context_t *ctx; + + if ((ctx = (ptls_aead_context_t *)malloc(aead->context_size)) == NULL) + return NULL; + + *ctx = (ptls_aead_context_t){aead}; + + if (aead->setup_crypto(ctx, is_enc, key, iv) != 0) { + free(ctx); + return NULL; + } + + return ctx; +} + +void ptls_aead_free(ptls_aead_context_t *ctx) +{ + ctx->dispose_crypto(ctx); + free(ctx); +} + +void ptls_aead__build_iv(ptls_aead_algorithm_t *algo, uint8_t *iv, const uint8_t *static_iv, uint64_t seq) +{ + size_t iv_size = algo->iv_size, i; + const uint8_t *s = static_iv; + uint8_t *d = iv; + + /* build iv */ + for (i = iv_size - 8; i != 0; --i) + *d++ = *s++; + i = 64; + do { + i -= 8; + *d++ = *s++ ^ (uint8_t)(seq >> i); + } while (i != 0); +} + +static void clear_memory(void *p, size_t len) +{ + if (len != 0) + memset(p, 0, len); +} + +void (*volatile ptls_clear_memory)(void *p, size_t len) = clear_memory; + +static int mem_equal(const void *_x, const void *_y, size_t len) +{ + const volatile uint8_t *x = _x, *y = _y; + uint8_t t = 0; + + for (; len != 0; --len) + t |= *x++ ^ *y++; + + return t == 0; +} + +int (*volatile ptls_mem_equal)(const void *x, const void *y, size_t len) = mem_equal; + +static uint64_t get_time(ptls_get_time_t *self) +{ + struct timeval tv; + gettimeofday(&tv, NULL); + return (uint64_t)tv.tv_sec * 1000 + tv.tv_usec / 1000; +} + +ptls_get_time_t ptls_get_time = {get_time}; +#if PICOTLS_USE_DTRACE +PTLS_THREADLOCAL unsigned ptls_default_skip_tracing = 0; +#endif + +int ptls_is_server(ptls_t *tls) +{ + return tls->is_server; +} + +struct st_ptls_raw_message_emitter_t { + ptls_message_emitter_t super; + size_t start_off; + size_t *epoch_offsets; +}; + +static int begin_raw_message(ptls_message_emitter_t *_self) +{ + struct st_ptls_raw_message_emitter_t *self = (void *)_self; + + self->start_off = self->super.buf->off; + return 0; +} + +static int commit_raw_message(ptls_message_emitter_t *_self) +{ + struct st_ptls_raw_message_emitter_t *self = (void *)_self; + size_t epoch; + + /* epoch is the key epoch, with the only exception being 2nd CH generated after 0-RTT key */ + epoch = self->super.enc->epoch; + if (epoch == 1 && self->super.buf->base[self->start_off] == PTLS_HANDSHAKE_TYPE_CLIENT_HELLO) + epoch = 0; + + for (++epoch; epoch < 5; ++epoch) { + assert(self->epoch_offsets[epoch] == self->start_off); + self->epoch_offsets[epoch] = self->super.buf->off; + } + + self->start_off = SIZE_MAX; + + return 0; +} + +size_t ptls_get_read_epoch(ptls_t *tls) +{ + switch (tls->state) { + case PTLS_STATE_CLIENT_HANDSHAKE_START: + case PTLS_STATE_CLIENT_EXPECT_SERVER_HELLO: + case PTLS_STATE_CLIENT_EXPECT_SECOND_SERVER_HELLO: + case PTLS_STATE_SERVER_EXPECT_CLIENT_HELLO: + case PTLS_STATE_SERVER_EXPECT_SECOND_CLIENT_HELLO: + return 0; /* plaintext */ + case PTLS_STATE_SERVER_EXPECT_END_OF_EARLY_DATA: + assert(!tls->ctx->omit_end_of_early_data); + return 1; /* 0-rtt */ + case PTLS_STATE_CLIENT_EXPECT_ENCRYPTED_EXTENSIONS: + case PTLS_STATE_CLIENT_EXPECT_CERTIFICATE_REQUEST_OR_CERTIFICATE: + case PTLS_STATE_CLIENT_EXPECT_CERTIFICATE: + case PTLS_STATE_CLIENT_EXPECT_CERTIFICATE_VERIFY: + case PTLS_STATE_CLIENT_EXPECT_FINISHED: + case PTLS_STATE_SERVER_EXPECT_CERTIFICATE: + case PTLS_STATE_SERVER_EXPECT_CERTIFICATE_VERIFY: + case PTLS_STATE_SERVER_EXPECT_FINISHED: + return 2; /* handshake */ + case PTLS_STATE_CLIENT_POST_HANDSHAKE: + case PTLS_STATE_SERVER_POST_HANDSHAKE: + return 3; /* 1-rtt */ + default: + assert(!"invalid state"); + return SIZE_MAX; + } +} + +int ptls_handle_message(ptls_t *tls, ptls_buffer_t *sendbuf, size_t epoch_offsets[5], size_t in_epoch, const void *input, + size_t inlen, ptls_handshake_properties_t *properties) +{ + return tls->is_server ? ptls_server_handle_message(tls, sendbuf, epoch_offsets, in_epoch, input, inlen, properties) + : ptls_client_handle_message(tls, sendbuf, epoch_offsets, in_epoch, input, inlen, properties); +} + +int ptls_client_handle_message(ptls_t *tls, ptls_buffer_t *sendbuf, size_t epoch_offsets[5], size_t in_epoch, const void *input, + size_t inlen, ptls_handshake_properties_t *properties) +{ + assert(!tls->is_server); + + struct st_ptls_raw_message_emitter_t emitter = { + {sendbuf, &tls->traffic_protection.enc, 0, begin_raw_message, commit_raw_message}, SIZE_MAX, epoch_offsets}; + struct st_ptls_record_t rec = {PTLS_CONTENT_TYPE_HANDSHAKE, 0, inlen, input}; + + if (input == NULL) + return send_client_hello(tls, &emitter.super, properties, NULL); + + if (ptls_get_read_epoch(tls) != in_epoch) + return PTLS_ALERT_UNEXPECTED_MESSAGE; + + return handle_handshake_record(tls, handle_client_handshake_message, &emitter.super, &rec, properties); +} + +int ptls_server_handle_message(ptls_t *tls, ptls_buffer_t *sendbuf, size_t epoch_offsets[5], size_t in_epoch, const void *input, + size_t inlen, ptls_handshake_properties_t *properties) +{ + assert(tls->is_server); + + struct st_ptls_raw_message_emitter_t emitter = { + {sendbuf, &tls->traffic_protection.enc, 0, begin_raw_message, commit_raw_message}, SIZE_MAX, epoch_offsets}; + struct st_ptls_record_t rec = {PTLS_CONTENT_TYPE_HANDSHAKE, 0, inlen, input}; + + assert(input); + + if (ptls_get_read_epoch(tls) != in_epoch) + return PTLS_ALERT_UNEXPECTED_MESSAGE; + + return handle_handshake_record(tls, handle_server_handshake_message, &emitter.super, &rec, properties); +} + +int ptls_esni_init_context(ptls_context_t *ctx, ptls_esni_context_t *esni, ptls_iovec_t esni_keys, + ptls_key_exchange_context_t **key_exchanges) +{ + const uint8_t *src = esni_keys.base, *const end = src + esni_keys.len; + size_t num_key_exchanges, num_cipher_suites = 0; + int ret; + + for (num_key_exchanges = 0; key_exchanges[num_key_exchanges] != NULL; ++num_key_exchanges) + ; + + memset(esni, 0, sizeof(*esni)); + if ((esni->key_exchanges = malloc(sizeof(*esni->key_exchanges) * (num_key_exchanges + 1))) == NULL) { + ret = PTLS_ERROR_NO_MEMORY; + goto Exit; + } + memcpy(esni->key_exchanges, key_exchanges, sizeof(*esni->key_exchanges) * (num_key_exchanges + 1)); + + /* ESNIKeys */ + if ((ret = ptls_decode16(&esni->version, &src, end)) != 0) + goto Exit; + /* Skip checksum fields */ + if (end - src < 4) { + ret = PTLS_ALERT_DECRYPT_ERROR; + goto Exit; + } + src += 4; + /* Published SNI field */ + ptls_decode_open_block(src, end, 2, { src = end; }); + + /* Process the list of KeyShareEntries, verify for each of them that the ciphersuite is supported. */ + ptls_decode_open_block(src, end, 2, { + do { + /* parse */ + uint16_t id; + if ((ret = ptls_decode16(&id, &src, end)) != 0) + goto Exit; + ptls_decode_open_block(src, end, 2, { src = end; }); + /* check that matching key-share exists */ + ptls_key_exchange_context_t **found; + for (found = key_exchanges; *found != NULL; ++found) + if ((*found)->algo->id == id) + break; + if (found == NULL) { + ret = PTLS_ERROR_INCOMPATIBLE_KEY; + goto Exit; + } + } while (src != end); + }); + /* Process the list of cipher_suites. If they are supported, store in esni context */ + ptls_decode_open_block(src, end, 2, { + void *newp; + do { + uint16_t id; + if ((ret = ptls_decode16(&id, &src, end)) != 0) + goto Exit; + size_t i; + for (i = 0; ctx->cipher_suites[i] != NULL; ++i) + if (ctx->cipher_suites[i]->id == id) + break; + if (ctx->cipher_suites[i] != NULL) { + if ((newp = realloc(esni->cipher_suites, sizeof(*esni->cipher_suites) * (num_cipher_suites + 1))) == NULL) { + ret = PTLS_ERROR_NO_MEMORY; + goto Exit; + } + esni->cipher_suites = newp; + esni->cipher_suites[num_cipher_suites++].cipher_suite = ctx->cipher_suites[i]; + } + } while (src != end); + if ((newp = realloc(esni->cipher_suites, sizeof(*esni->cipher_suites) * (num_cipher_suites + 1))) == NULL) { + ret = PTLS_ERROR_NO_MEMORY; + goto Exit; + } + esni->cipher_suites = newp; + esni->cipher_suites[num_cipher_suites].cipher_suite = NULL; + }); + /* Parse the padded length, not before, not after parameters */ + if ((ret = ptls_decode16(&esni->padded_length, &src, end)) != 0) + goto Exit; + if ((ret = ptls_decode64(&esni->not_before, &src, end)) != 0) + goto Exit; + if ((ret = ptls_decode64(&esni->not_after, &src, end)) != 0) + goto Exit; + /* Skip the extension fields */ + ptls_decode_block(src, end, 2, { + while (src != end) { + uint16_t ext_type; + if ((ret = ptls_decode16(&ext_type, &src, end)) != 0) + goto Exit; + ptls_decode_open_block(src, end, 2, { src = end; }); + } + }); + + { /* calculate digests for every cipher-suite */ + size_t i; + for (i = 0; esni->cipher_suites[i].cipher_suite != NULL; ++i) { + if ((ret = ptls_calc_hash(esni->cipher_suites[i].cipher_suite->hash, esni->cipher_suites[i].record_digest, + esni_keys.base, esni_keys.len)) != 0) + goto Exit; + } + } + + ret = 0; +Exit: + if (ret != 0) + ptls_esni_dispose_context(esni); + return ret; +} + +void ptls_esni_dispose_context(ptls_esni_context_t *esni) +{ + size_t i; + + if (esni->key_exchanges != NULL) { + for (i = 0; esni->key_exchanges[i] != NULL; ++i) + esni->key_exchanges[i]->on_exchange(esni->key_exchanges + i, 1, NULL, ptls_iovec_init(NULL, 0)); + free(esni->key_exchanges); + } + free(esni->cipher_suites); +} + +/** + * Obtain the ESNI secrets negotiated during the handshake. + */ +ptls_esni_secret_t *ptls_get_esni_secret(ptls_t *ctx) +{ + return ctx->esni; +} + +/** + * checks if given name looks like an IP address + */ +int ptls_server_name_is_ipaddr(const char *name) +{ +#ifdef AF_INET + struct sockaddr_in sin; + if (inet_pton(AF_INET, name, &sin) == 1) + return 1; +#endif +#ifdef AF_INET6 + struct sockaddr_in6 sin6; + if (inet_pton(AF_INET6, name, &sin6) == 1) + return 1; +#endif + return 0; +} + +char *ptls_hexdump(char *buf, const void *_src, size_t len) +{ + char *dst = buf; + const uint8_t *src = _src; + size_t i; + + for (i = 0; i != len; ++i) { + *dst++ = "0123456789abcdef"[src[i] >> 4]; + *dst++ = "0123456789abcdef"[src[i] & 0xf]; + } + *dst++ = '\0'; + return buf; +} diff --git a/deps/picoquic/picotls/lib/ptlsbcrypt.c b/deps/picoquic/picotls/lib/ptlsbcrypt.c new file mode 100644 index 0000000..2a9a4d0 --- /dev/null +++ b/deps/picoquic/picotls/lib/ptlsbcrypt.c @@ -0,0 +1,803 @@ +/* + * Copyright (c) 2016 DeNA Co., Ltd., Kazuho Oku + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#ifndef _WINDOWS +/* This module is only defined for windows. + * It is an implementation of the main crypto algorithms + * using windows crypto libraries */ + +int ptls_bcrypt_init() +{ + return -1; +} + +void ptlc_bcrypt_dispose() +{ +} + +#else + +#include "wincompat.h" +#include +#include "picotls.h" + +/** + * Initialize the brcrypt libraries, creates the + * required common variables, etc. */ +int ptls_bcrypt_init() +{ + return 0; +} + +/** + * Clear the initialization of the bcrypt libraries */ + +void ptlc_bcrypt_dispose() +{ +} + +/** + * Random number generation */ + +void ptls_bcrypt_random_bytes(void *buf, size_t len) +{ + /* TODO: Crypto gen random */ +} + +/* + * Support for symmetric ciphers + */ + +struct ptls_bcrypt_symmetric_param_t { + HANDLE hKey; + DWORD dwFlags; + ULONG cbKeyObject; + uint8_t iv[PTLS_MAX_IV_SIZE]; + uint8_t *key_object; + int is_enc; +}; + +struct ptls_bcrypt_symmetric_context_t { + ptls_cipher_context_t super; + struct ptls_bcrypt_symmetric_param_t bctx; +}; + +static void ptls_bcrypt_cipher_init_ctr(ptls_cipher_context_t *_ctx, const void *iv) +{ + struct ptls_bcrypt_symmetric_context_t *ctx = (struct ptls_bcrypt_symmetric_context_t *)_ctx; + /* Copy the IV to inside structure */ + if (iv != NULL) { + memcpy(ctx->bctx.iv, iv, ctx->super.algo->block_size); + } else { + memset(ctx->bctx.iv, 0, ctx->super.algo->block_size); + } +} + +static void ptls_bcrypt_cipher_dispose(ptls_cipher_context_t *_ctx) +{ + struct ptls_bcrypt_symmetric_context_t *ctx = (struct ptls_bcrypt_symmetric_context_t *)_ctx; + + if (ctx->bctx.hKey != NULL) { + (void)BCryptDestroyKey(ctx->bctx.hKey); + } + + if (ctx->bctx.key_object != NULL) { + free(ctx->bctx.key_object); + } + + memset(&ctx->bctx, 0, sizeof(struct ptls_bcrypt_symmetric_param_t)); +} + +static void ptls_bcrypt_cipher_transform_ecb(ptls_cipher_context_t *_ctx, void *output, const void *input, size_t len) +{ + struct ptls_bcrypt_symmetric_context_t *ctx = (struct ptls_bcrypt_symmetric_context_t *)_ctx; + ULONG cbResult; + NTSTATUS ret; + + assert((len % ctx->super.algo->block_size) == 0); + + /* Call the encryption */ + if (ctx->bctx.is_enc) { + ret = BCryptEncrypt(ctx->bctx.hKey, (PUCHAR)input, (ULONG)len, NULL, NULL, 0, output, (ULONG)len, &cbResult, 0); + } else { + ret = BCryptDecrypt(ctx->bctx.hKey, (PUCHAR)input, (ULONG)len, NULL, NULL, 0, output, (ULONG)len, &cbResult, 0); + } + + assert(BCRYPT_SUCCESS(ret)); + + if (!BCRYPT_SUCCESS(ret)) { + memset(output, 0, cbResult); + } +} + +static void ptls_bcrypt_cipher_transform_ctr(ptls_cipher_context_t *_ctx, void *output, const void *input, size_t len) +{ + struct ptls_bcrypt_symmetric_context_t *ctx = (struct ptls_bcrypt_symmetric_context_t *)_ctx; + ULONG cbResult; + NTSTATUS ret; + uint8_t eiv[PTLS_MAX_IV_SIZE]; + int i; + uint64_t seq = 0; + size_t processed = 0; + uint8_t const *v_in = input; + uint8_t *v_out = output; + + assert(ctx->super.algo->block_size > 0); + assert(ctx->super.algo->block_size <= PTLS_MAX_IV_SIZE); + + while (processed < len) { + + ret = BCryptEncrypt(ctx->bctx.hKey, (PUCHAR)ctx->bctx.iv, (ULONG)ctx->super.algo->block_size, NULL, NULL, 0, eiv, + (ULONG)(ULONG)ctx->super.algo->block_size, &cbResult, 0); + assert(BCRYPT_SUCCESS(ret)); + + if (BCRYPT_SUCCESS(ret)) { + for (i = 0; processed < len && i < ctx->super.algo->block_size; i++, processed++) { + v_out[processed] = v_in[processed] ^ eiv[i]; + } + + /* Increment the iv block */ + i = (int)ctx->super.algo->block_size - 1; + while (i >= 0) { + ctx->bctx.iv[i] += 1; + if (ctx->bctx.iv[i] > 0) { + break; + } + i--; + } + } + } +} + +static int ptls_bcrypt_cipher_setup_crypto(ptls_cipher_context_t *_ctx, int is_enc, const void *key, wchar_t const *bcrypt_name, + int is_ctr) +{ + struct ptls_bcrypt_symmetric_context_t *ctx = (struct ptls_bcrypt_symmetric_context_t *)_ctx; + HANDLE hAlgorithm = NULL; + NTSTATUS ret; + + memset(&ctx->bctx, 0, sizeof(struct ptls_bcrypt_symmetric_param_t)); + + ret = BCryptOpenAlgorithmProvider(&hAlgorithm, bcrypt_name, NULL, 0); + + if (BCRYPT_SUCCESS(ret)) { + DWORD ko_size = 0; + ULONG cbResult = 0; + + ret = BCryptGetProperty(hAlgorithm, BCRYPT_OBJECT_LENGTH, (PUCHAR)&ko_size, (ULONG)sizeof(ko_size), &cbResult, 0); + + if (BCRYPT_SUCCESS(ret)) { + ctx->bctx.key_object = (uint8_t *)malloc(ko_size); + if (ctx->bctx.key_object == NULL) { + ret = STATUS_NO_MEMORY; + } else { + ctx->bctx.cbKeyObject = ko_size; + } + } + } + + if (BCRYPT_SUCCESS(ret)) { + ret = BCryptSetProperty(hAlgorithm, BCRYPT_CHAINING_MODE, (PBYTE)BCRYPT_CHAIN_MODE_ECB, sizeof(BCRYPT_CHAIN_MODE_ECB), 0); + } + + if (BCRYPT_SUCCESS(ret)) { + ret = BCryptGenerateSymmetricKey(hAlgorithm, &ctx->bctx.hKey, ctx->bctx.key_object, ctx->bctx.cbKeyObject, (PUCHAR)key, + (ULONG)ctx->super.algo->key_size, 0); + } + + if (hAlgorithm != NULL) { + BCryptCloseAlgorithmProvider(hAlgorithm, 0); + } + + if (BCRYPT_SUCCESS(ret)) { + + ctx->super.do_dispose = ptls_bcrypt_cipher_dispose; + if (is_ctr) { + ctx->super.do_init = ptls_bcrypt_cipher_init_ctr; + ctx->super.do_transform = ptls_bcrypt_cipher_transform_ctr; + } else { + ctx->super.do_init = NULL; + ctx->super.do_transform = ptls_bcrypt_cipher_transform_ecb; + } + ctx->bctx.is_enc = is_enc; + return 0; + } else { + ptls_bcrypt_cipher_dispose(_ctx); + return PTLS_ERROR_LIBRARY; + } +} + +static int ptls_bcrypt_cipher_setup_crypto_aes_ecb(ptls_cipher_context_t *_ctx, int is_enc, const void *key) +{ + return ptls_bcrypt_cipher_setup_crypto(_ctx, is_enc, key, BCRYPT_AES_ALGORITHM, 0); +} + +static int ptls_bcrypt_cipher_setup_crypto_aes_ctr(ptls_cipher_context_t *_ctx, int is_enc, const void *key) +{ + return ptls_bcrypt_cipher_setup_crypto(_ctx, is_enc, key, BCRYPT_AES_ALGORITHM, 1); +} + +/* Picotls assumes that AEAD encryption works as: + * - an "init" call that prepares the encryption context. + * - a series of "update" calls that encrypt segments of the message + * - a "final" call that completes the encryption. + * + * In Bcrypt, the update calls will be implemented as a series of calls + * to BCryptEncrypt. The state necessary to pass these calls is provided + * to the Bcrypt function in two parameters: + * - the "padding info" points to a BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO + * structure + * - the "IV" parameter points to a buffer holding intermediate updates + * of the IV. That buffer shall be initialize to zero before the + * first call. + * The documentation of the AEAD mode on MSDN is slightly obscure, and + * also slightly wrong. After trial and errors and web searches, we find + * that: + * - the Nonce parameter (pbNonce, cbNonce) points to the initial + * vector for the encryption, as passed by Picotls. Picotls combines + * per session IV and sequence number in that nonce prior to the call. + * - The Authdata parameter (pbAuthData, cbAuthData) points to the + * authenticated data passed to the API as aad, aadlen. + * - The cbAAd parameter contains the length of auth data that needs + * to be processed. It is initialized before the first call. + * - The tag parameter (pbTag, cbTag) points to a buffer that + * holds intermediate tag values during chaining. The size must be + * the size of the tag for the algorithm. It must be + * initialized to zero before first call. + * - The Mac Context parameter (pbMacContext, cbMacContext) contains + * a working buffer for the computation of the tag. The size + * must be the maxLength parameter returned retrieved in the + * BCRYPT_AUTH_TAG_LENGTH property of the algorithm. It must be + * initialized to zero before first call. + * - The dwflag parameters must be set to + * BCRYPT_AUTH_MODE_CHAIN_CALLS_FLAG on first call. (The + * MSDN documentation says BCRYPT_AUTH_MODE_IN_PROGRESS_FLAG, + * but that's an error.) + * + * The members of the BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO struct + * should not be modified between calls, except for: + * - the BCRYPT_AUTH_MODE_IN_PROGRESS_FLAG should be cleared + * before the final call. + * + * The Picotls API does not constrain the length of the segments + * passed in the "update" calls, but BCryptEncrypt will fail with + * error STATUS_INVALID_BUFFER_SIZE if the length passed in the + * chained calls is not an integer multiple of block size. This forces + * us to maintain an intermediate buffer of "extra bytes". + * + */ + +struct ptls_bcrypt_aead_param_t { + HANDLE hKey; + ULONG cbKeyObject; + ULONG maxTagLength; + ULONG nbExtraBytes; + uint8_t *key_object; + uint8_t iv_static[PTLS_MAX_IV_SIZE]; + uint8_t extraBytes[PTLS_MAX_DIGEST_SIZE]; + uint8_t iv[PTLS_MAX_IV_SIZE]; + uint8_t ivbuf[PTLS_MAX_IV_SIZE]; + uint8_t tag[PTLS_MAX_DIGEST_SIZE]; + uint8_t auth_tag[PTLS_MAX_DIGEST_SIZE]; + BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO aead_params; +}; + +struct ptls_bcrypt_aead_context_t { + struct st_ptls_aead_context_t super; + struct ptls_bcrypt_aead_param_t bctx; +}; + +static void ptls_bcrypt_aead_dispose_crypto(struct st_ptls_aead_context_t *_ctx) +{ + struct ptls_bcrypt_aead_context_t *ctx = (struct ptls_bcrypt_aead_context_t *)_ctx; + + if (ctx->bctx.hKey != NULL) { + (void)BCryptDestroyKey(ctx->bctx.hKey); + } + + if (ctx->bctx.key_object != NULL) { + free(ctx->bctx.key_object); + } + + memset(&ctx->bctx, 0, sizeof(struct ptls_bcrypt_aead_param_t)); +} + +static void ptls_bcrypt_aead_do_encrypt_init(struct st_ptls_aead_context_t *_ctx, uint64_t seq, const void *aad, size_t aadlen) +{ + struct ptls_bcrypt_aead_context_t *ctx = (struct ptls_bcrypt_aead_context_t *)_ctx; + + /* Build the IV for this encryption */ + ptls_aead__build_iv(ctx->super.algo, ctx->bctx.iv, ctx->bctx.iv_static, seq); + /* Auth tag to NULL */ + memset(ctx->bctx.tag, 0, sizeof(ctx->super.algo->tag_size)); + BCRYPT_INIT_AUTH_MODE_INFO(ctx->bctx.aead_params); + + assert(ctx->super.algo->iv_size <= sizeof(ctx->bctx.ivbuf)); + assert(ctx->super.algo->tag_size <= sizeof(ctx->bctx.tag)); + assert(ctx->bctx.maxTagLength <= sizeof(ctx->bctx.auth_tag)); + + memset(ctx->bctx.ivbuf, 0, ctx->super.algo->iv_size); + memset(ctx->bctx.tag, 0, ctx->super.algo->tag_size); + memset(ctx->bctx.auth_tag, 0, sizeof(ctx->bctx.auth_tag)); + + ctx->bctx.nbExtraBytes = 0; + + ctx->bctx.aead_params.pbNonce = (PUCHAR)&ctx->bctx.iv; + ctx->bctx.aead_params.cbNonce = (ULONG)ctx->super.algo->iv_size; + ctx->bctx.aead_params.pbAuthData = (PUCHAR)aad; + ctx->bctx.aead_params.cbAuthData = (ULONG)aadlen; + ctx->bctx.aead_params.pbTag = (PUCHAR)ctx->bctx.tag; + ctx->bctx.aead_params.cbTag = (ULONG)ctx->super.algo->tag_size; + // ctx->bctx.aead_params.cbAAD = (ULONG)aadlen; + ctx->bctx.aead_params.pbMacContext = (PUCHAR)ctx->bctx.auth_tag; + ctx->bctx.aead_params.cbMacContext = (ULONG)ctx->bctx.maxTagLength; + ctx->bctx.aead_params.dwFlags = BCRYPT_AUTH_MODE_CHAIN_CALLS_FLAG; +} + +static size_t ptls_bcrypt_aead_do_encrypt_update(struct st_ptls_aead_context_t *_ctx, void *output, const void *input, size_t inlen) +{ + struct ptls_bcrypt_aead_context_t *ctx = (struct ptls_bcrypt_aead_context_t *)_ctx; + size_t outlenMax = inlen + ctx->super.algo->tag_size + ctx->bctx.nbExtraBytes; + ULONG cbResult1 = 0; + ULONG cbResult2 = 0; + NTSTATUS ret; + + /* If there are extra bytes, complement and encrypt */ + if (ctx->bctx.nbExtraBytes > 0) { + ULONG requiredBytes = (ULONG)(ctx->super.algo->ecb_cipher->block_size - ctx->bctx.nbExtraBytes); + + if (inlen < requiredBytes) { + memcpy(&ctx->bctx.extraBytes[ctx->bctx.nbExtraBytes], input, inlen); + ctx->bctx.nbExtraBytes += (ULONG)inlen; + inlen = 0; + } else { + memcpy(&ctx->bctx.extraBytes[ctx->bctx.nbExtraBytes], input, requiredBytes); + inlen -= requiredBytes; + input = (void *)(((uint8_t *)input) + requiredBytes); + ret = BCryptEncrypt(ctx->bctx.hKey, (PUCHAR)ctx->bctx.extraBytes, (ULONG)ctx->super.algo->ecb_cipher->block_size, + (void *)&ctx->bctx.aead_params, ctx->bctx.ivbuf, (ULONG)ctx->super.algo->iv_size, output, + (ULONG)outlenMax, &cbResult1, 0); + + assert(BCRYPT_SUCCESS(ret)); + if (!BCRYPT_SUCCESS(ret)) { + memset(output, 0, cbResult1); + } + outlenMax -= cbResult1; + output = (void *)(((uint8_t *)output) + cbResult1); + } + } + + /* If there are trailing bytes, store them in the extra bytes */ + ctx->bctx.nbExtraBytes = (ULONG)(inlen % ctx->super.algo->ecb_cipher->block_size); + if (ctx->bctx.nbExtraBytes > 0) { + inlen -= ctx->bctx.nbExtraBytes; + memcpy(&ctx->bctx.extraBytes, (void *)(((uint8_t *)input) + inlen), ctx->bctx.nbExtraBytes); + } + + if (inlen > 0) { + ret = BCryptEncrypt(ctx->bctx.hKey, (PUCHAR)input, (ULONG)inlen, (void *)&ctx->bctx.aead_params, ctx->bctx.ivbuf, + (ULONG)ctx->super.algo->iv_size, output, (ULONG)outlenMax, &cbResult2, 0); + assert(BCRYPT_SUCCESS(ret)); + + if (!BCRYPT_SUCCESS(ret)) { + memset(output, 0, cbResult2); + } + } + return (size_t)cbResult1 + cbResult2; +} + +static size_t ptls_bcrypt_aead_do_encrypt_final(struct st_ptls_aead_context_t *_ctx, void *output) +{ + struct ptls_bcrypt_aead_context_t *ctx = (struct ptls_bcrypt_aead_context_t *)_ctx; + size_t outlenMax = ctx->super.algo->tag_size + ctx->bctx.nbExtraBytes; + ULONG cbResult = 0; + NTSTATUS ret; + + ctx->bctx.aead_params.dwFlags &= ~BCRYPT_AUTH_MODE_CHAIN_CALLS_FLAG; + + ret = BCryptEncrypt(ctx->bctx.hKey, (PUCHAR)ctx->bctx.extraBytes, (ULONG)ctx->bctx.nbExtraBytes, (void *)&ctx->bctx.aead_params, + ctx->bctx.ivbuf, (ULONG)ctx->super.algo->iv_size, output, (ULONG)outlenMax, &cbResult, 0); + assert(BCRYPT_SUCCESS(ret)); + + if (BCRYPT_SUCCESS(ret)) { + /* Find the tag in the aead parameters and append it to the output */ + assert(cbResult + ctx->bctx.aead_params.cbTag <= outlenMax); + memcpy(((uint8_t *)output) + cbResult, ctx->bctx.aead_params.pbTag, ctx->bctx.aead_params.cbTag); + cbResult += ctx->bctx.aead_params.cbTag; + } + return cbResult; +} + +static size_t ptls_bcrypt_aead_do_decrypt(struct st_ptls_aead_context_t *_ctx, void *output, const void *input, size_t inlen, + uint64_t seq, const void *aad, size_t aadlen) +{ + struct ptls_bcrypt_aead_context_t *ctx = (struct ptls_bcrypt_aead_context_t *)_ctx; + ULONG cbResult; + size_t textLen = inlen - ctx->super.algo->tag_size; + NTSTATUS ret; + + /* Build the IV for this decryption */ + ptls_aead__build_iv(ctx->super.algo, ctx->bctx.iv, ctx->bctx.iv_static, seq); + + /* TODO: pPaddingInfo must point to BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO structure. */ + BCRYPT_INIT_AUTH_MODE_INFO(ctx->bctx.aead_params); + /* TODO: find clarity on handling of ivbuf */ + memset(ctx->bctx.tag, 0, sizeof(ctx->super.algo->tag_size)); + ctx->bctx.aead_params.pbNonce = (PUCHAR)&ctx->bctx.iv; + ctx->bctx.aead_params.cbNonce = (ULONG)ctx->super.algo->iv_size; + ctx->bctx.aead_params.pbAuthData = (PUCHAR)aad; + ctx->bctx.aead_params.cbAuthData = (ULONG)aadlen; + ctx->bctx.aead_params.pbTag = (PUCHAR)(((uint8_t *)input) + textLen); + ctx->bctx.aead_params.cbTag = (ULONG)(ULONG)ctx->super.algo->tag_size; + + /* Call the decryption */ + ret = BCryptDecrypt(ctx->bctx.hKey, (PUCHAR)input, (ULONG)textLen, (void *)&ctx->bctx.aead_params, NULL, 0, (PUCHAR)output, + (ULONG)textLen, &cbResult, 0); + + if (BCRYPT_SUCCESS(ret)) { + return (size_t)cbResult; + } else { + return SIZE_MAX; + } +} + +static int ptls_bcrypt_aead_setup_crypto(ptls_aead_context_t *_ctx, int is_enc, const void *key, const void *iv, + wchar_t const *bcrypt_name, wchar_t const *bcrypt_mode, size_t bcrypt_mode_size) +{ + struct ptls_bcrypt_aead_context_t *ctx = (struct ptls_bcrypt_aead_context_t *)_ctx; + HANDLE hAlgorithm = NULL; + NTSTATUS ret; + + memset(&ctx->bctx, 0, sizeof(struct ptls_bcrypt_symmetric_param_t)); + + ret = BCryptOpenAlgorithmProvider(&hAlgorithm, bcrypt_name, NULL, 0); + + if (BCRYPT_SUCCESS(ret)) { + ret = BCryptSetProperty(hAlgorithm, BCRYPT_CHAINING_MODE, (PBYTE)bcrypt_mode, (ULONG)bcrypt_mode_size, 0); + } + + if (BCRYPT_SUCCESS(ret)) { + DWORD ko_size = 0; + ULONG cbResult = 0; + + ret = BCryptGetProperty(hAlgorithm, BCRYPT_OBJECT_LENGTH, (PUCHAR)&ko_size, (ULONG)sizeof(ko_size), &cbResult, 0); + + if (BCRYPT_SUCCESS(ret)) { + ctx->bctx.key_object = (uint8_t *)malloc(ko_size); + if (ctx->bctx.key_object == NULL) { + ret = STATUS_NO_MEMORY; + } else { + ctx->bctx.cbKeyObject = ko_size; + } + } + } + + if (BCRYPT_SUCCESS(ret)) { + BCRYPT_KEY_LENGTHS_STRUCT atl_st; + ULONG cbResult = 0; + + ret = BCryptGetProperty(hAlgorithm, BCRYPT_AUTH_TAG_LENGTH, (PUCHAR)&atl_st, (ULONG)sizeof(atl_st), &cbResult, 0); + if (BCRYPT_SUCCESS(ret)) { + ctx->bctx.maxTagLength = atl_st.dwMaxLength; + } + } + + if (BCRYPT_SUCCESS(ret)) { + ret = BCryptGenerateSymmetricKey(hAlgorithm, &ctx->bctx.hKey, ctx->bctx.key_object, ctx->bctx.cbKeyObject, (PUCHAR)key, + (ULONG)ctx->super.algo->key_size, 0); + } + + if (hAlgorithm != NULL) { + BCryptCloseAlgorithmProvider(hAlgorithm, 0); + } + + if (BCRYPT_SUCCESS(ret)) { + memcpy(ctx->bctx.iv_static, iv, ctx->super.algo->iv_size); + if (is_enc) { + ctx->super.dispose_crypto = ptls_bcrypt_aead_dispose_crypto; + ctx->super.do_decrypt = NULL; + ctx->super.do_encrypt_init = ptls_bcrypt_aead_do_encrypt_init; + ctx->super.do_encrypt_update = ptls_bcrypt_aead_do_encrypt_update; + ctx->super.do_encrypt_final = ptls_bcrypt_aead_do_encrypt_final; + ctx->super.do_encrypt = ptls_aead__do_encrypt; + } else { + ctx->super.dispose_crypto = ptls_bcrypt_aead_dispose_crypto; + ctx->super.do_decrypt = ptls_bcrypt_aead_do_decrypt; + ctx->super.do_encrypt_init = NULL; + ctx->super.do_encrypt_update = NULL; + ctx->super.do_encrypt_final = NULL; + } + return 0; + } else { + ptls_bcrypt_aead_dispose_crypto(_ctx); + return PTLS_ERROR_LIBRARY; + } +} + +static int ptls_bcrypt_aead_setup_crypto_aesgcm(ptls_aead_context_t *_ctx, int is_enc, const void *key, const void *iv) +{ + return ptls_bcrypt_aead_setup_crypto(_ctx, is_enc, key, iv, BCRYPT_AES_ALGORITHM, BCRYPT_CHAIN_MODE_GCM, + sizeof(BCRYPT_CHAIN_MODE_GCM)); +} + +/* Hash algorithms */ + +struct st_ptls_bcrypt_hash_param_t { + wchar_t const *bcrypt_name; + BCRYPT_HASH_HANDLE hHash; + PUCHAR pbHashObject; + ULONG cbHashObject; + ULONG hash_size; + int has_error; +}; + +struct st_ptls_bcrypt_hash_context_t { + ptls_hash_context_t super; + struct st_ptls_bcrypt_hash_param_t ctx; +}; + +static void ptls_bcrypt_hash_update(struct st_ptls_hash_context_t *_ctx, const void *src, size_t len) +{ + struct st_ptls_bcrypt_hash_context_t *ctx = (struct st_ptls_bcrypt_hash_context_t *)_ctx; + NTSTATUS ret = BCryptHashData(ctx->ctx.hHash, (PUCHAR)src, (ULONG)len, 0); + assert(BCRYPT_SUCCESS(ret)); + + if (!BCRYPT_SUCCESS(ret)) { + ctx->ctx.has_error = 1; + } +} + +static struct st_ptls_bcrypt_hash_context_t *ptls_bcrypt_hash_context_free(struct st_ptls_bcrypt_hash_context_t *ctx) +{ + if (ctx->ctx.pbHashObject != NULL) { + ptls_clear_memory(ctx->ctx.pbHashObject, ctx->ctx.cbHashObject); + free(ctx->ctx.pbHashObject); + } + ptls_clear_memory(&ctx->ctx, sizeof(ctx->ctx)); + + return NULL; +} + +static ptls_hash_context_t *ptls_bcrypt_hash_clone(struct st_ptls_hash_context_t *_ctx); + +static void ptls_bcrypt_hash_final(struct st_ptls_hash_context_t *_ctx, void *md, ptls_hash_final_mode_t mode) +{ + if (mode == PTLS_HASH_FINAL_MODE_SNAPSHOT) { + /* TODO: Copying handle does not change the handle! */ + struct st_ptls_hash_context_t *clone_ctx = ptls_bcrypt_hash_clone(_ctx); + + if (clone_ctx != NULL) { + ptls_bcrypt_hash_final(clone_ctx, md, PTLS_HASH_FINAL_MODE_FREE); + } else { + assert(clone_ctx != NULL); + } + } else { + NTSTATUS ret; + struct st_ptls_bcrypt_hash_context_t *ctx = (struct st_ptls_bcrypt_hash_context_t *)_ctx; + + if (md != NULL) { + ret = BCryptFinishHash(ctx->ctx.hHash, md, ctx->ctx.hash_size, 0); + assert(BCRYPT_SUCCESS(ret)); + if (!BCRYPT_SUCCESS(ret) || ctx->ctx.has_error) { + memset(md, 0, ctx->ctx.hash_size); + } + } + + ret = BCryptDestroyHash(ctx->ctx.hHash); + assert(BCRYPT_SUCCESS(ret)); + + switch (mode) { + case PTLS_HASH_FINAL_MODE_FREE: + ctx = ptls_bcrypt_hash_context_free(ctx); + break; + case PTLS_HASH_FINAL_MODE_RESET: { + BCRYPT_ALG_HANDLE hAlgorithm = NULL; + ret = BCryptOpenAlgorithmProvider(&hAlgorithm, ctx->ctx.bcrypt_name, NULL, 0); + if (BCRYPT_SUCCESS(ret)) { + ctx->ctx.hHash = NULL; + ret = BCryptCreateHash(hAlgorithm, &ctx->ctx.hHash, ctx->ctx.pbHashObject, ctx->ctx.cbHashObject, NULL, 0, 0); + BCryptCloseAlgorithmProvider(hAlgorithm, 0); + } + assert(BCRYPT_SUCCESS(ret)); + if (!BCRYPT_SUCCESS(ret)) { + ctx->ctx.hHash = NULL; + } + break; + } + default: + assert(!"FIXME"); + break; + } + } +} + +static ptls_hash_context_t *ptls_bcrypt_hash_clone(struct st_ptls_hash_context_t *_ctx) +{ + struct st_ptls_bcrypt_hash_context_t *ctx = (struct st_ptls_bcrypt_hash_context_t *)_ctx; + struct st_ptls_bcrypt_hash_context_t *clone_ctx; + + if ((clone_ctx = (struct st_ptls_bcrypt_hash_context_t *)malloc(sizeof(*ctx))) != NULL) { + NTSTATUS ret; + + ptls_clear_memory(&clone_ctx->ctx, sizeof(clone_ctx->ctx)); + clone_ctx->super = (ptls_hash_context_t){ptls_bcrypt_hash_update, ptls_bcrypt_hash_final, ptls_bcrypt_hash_clone}; + clone_ctx->ctx.pbHashObject = (uint8_t *)malloc(ctx->ctx.cbHashObject); + clone_ctx->ctx.cbHashObject = ctx->ctx.cbHashObject; + clone_ctx->ctx.bcrypt_name = ctx->ctx.bcrypt_name; + clone_ctx->ctx.hash_size = ctx->ctx.hash_size; + clone_ctx->ctx.has_error = ctx->ctx.has_error; + + if (clone_ctx->ctx.pbHashObject == NULL) { + ret = STATUS_NO_MEMORY; + } else { + clone_ctx->ctx.hHash = NULL; + ptls_clear_memory(&clone_ctx->ctx.pbHashObject, clone_ctx->ctx.cbHashObject); + ret = BCryptDuplicateHash(ctx->ctx.hHash, &clone_ctx->ctx.hHash, clone_ctx->ctx.pbHashObject, + clone_ctx->ctx.cbHashObject, 0); + } + + if (!BCRYPT_SUCCESS(ret)) { + clone_ctx = ptls_bcrypt_hash_context_free(clone_ctx); + } + } + + return (ptls_hash_context_t *)clone_ctx; +} + +static ptls_hash_context_t *ptls_bcrypt_hash_create(wchar_t const *bcrypt_name, ULONG hash_size) +{ + BCRYPT_ALG_HANDLE hAlgorithm = NULL; + NTSTATUS ret; + struct st_ptls_bcrypt_hash_context_t *ctx; + + if ((ctx = (struct st_ptls_bcrypt_hash_context_t *)malloc(sizeof(*ctx))) != NULL) { + ctx->super = (ptls_hash_context_t){ptls_bcrypt_hash_update, ptls_bcrypt_hash_final, ptls_bcrypt_hash_clone}; + memset(&ctx->ctx, 0, sizeof(struct st_ptls_bcrypt_hash_param_t)); + ctx->ctx.hash_size = hash_size; + ctx->ctx.bcrypt_name = bcrypt_name; + + ret = BCryptOpenAlgorithmProvider(&hAlgorithm, bcrypt_name, NULL, 0); + + if (BCRYPT_SUCCESS(ret)) { + DWORD hb_length = 0; + ULONG cbResult = 0; + + ret = BCryptGetProperty(hAlgorithm, BCRYPT_OBJECT_LENGTH, (PUCHAR)&hb_length, (ULONG)sizeof(hb_length), &cbResult, 0); + + if (BCRYPT_SUCCESS(ret)) { + ctx->ctx.pbHashObject = (uint8_t *)malloc(hb_length); + if (ctx->ctx.pbHashObject == NULL) { + ret = STATUS_NO_MEMORY; + } else { + ctx->ctx.cbHashObject = hb_length; + } + } + } + + if (BCRYPT_SUCCESS(ret)) { + ret = BCryptCreateHash(hAlgorithm, &ctx->ctx.hHash, ctx->ctx.pbHashObject, ctx->ctx.cbHashObject, NULL, 0, 0); + } + + if (!BCRYPT_SUCCESS(ret)) { + ctx = ptls_bcrypt_hash_context_free(ctx); + } + } + + if (hAlgorithm != NULL) { + BCryptCloseAlgorithmProvider(hAlgorithm, 0); + } + + return (ptls_hash_context_t *)ctx; +} + +static ptls_hash_context_t *ptls_bcrypt_sha256_create(void) +{ + return ptls_bcrypt_hash_create(BCRYPT_SHA256_ALGORITHM, PTLS_SHA256_DIGEST_SIZE); +} + +static ptls_hash_context_t *ptls_bcrypt_sha384_create(void) +{ + return ptls_bcrypt_hash_create(BCRYPT_SHA384_ALGORITHM, PTLS_SHA384_DIGEST_SIZE); +} + +/* Declaration of algorithms + */ + +ptls_cipher_algorithm_t ptls_bcrypt_aes128ecb = {"AES128-ECB", + PTLS_AES128_KEY_SIZE, + PTLS_AES_BLOCK_SIZE, + 0 /* iv size */, + sizeof(struct ptls_bcrypt_symmetric_context_t), + ptls_bcrypt_cipher_setup_crypto_aes_ecb}; +ptls_cipher_algorithm_t ptls_bcrypt_aes256ecb = {"AES256-ECB", + PTLS_AES256_KEY_SIZE, + PTLS_AES_BLOCK_SIZE, + 0 /* iv size */, + sizeof(struct ptls_bcrypt_symmetric_context_t), + ptls_bcrypt_cipher_setup_crypto_aes_ecb}; + +ptls_cipher_algorithm_t ptls_bcrypt_aes128ctr = {"AES128-CTR", + PTLS_AES128_KEY_SIZE, + PTLS_AES_BLOCK_SIZE, + 0 /* iv size */, + sizeof(struct ptls_bcrypt_symmetric_context_t), + ptls_bcrypt_cipher_setup_crypto_aes_ctr}; + +ptls_cipher_algorithm_t ptls_bcrypt_aes256ctr = {"AES256-CTR", + PTLS_AES256_KEY_SIZE, + PTLS_AES_BLOCK_SIZE, + 0 /* iv size */, + sizeof(struct ptls_bcrypt_symmetric_context_t), + ptls_bcrypt_cipher_setup_crypto_aes_ctr}; + +ptls_aead_algorithm_t ptls_bcrypt_aes128gcm = {"AES128-GCM", + PTLS_AESGCM_CONFIDENTIALITY_LIMIT, + PTLS_AESGCM_INTEGRITY_LIMIT, + &ptls_bcrypt_aes128ecb, + &ptls_bcrypt_aes128ctr, + PTLS_AES128_KEY_SIZE, + PTLS_AESGCM_IV_SIZE, + PTLS_AESGCM_TAG_SIZE, + sizeof(struct ptls_bcrypt_aead_context_t), + ptls_bcrypt_aead_setup_crypto_aesgcm}; + +ptls_aead_algorithm_t ptls_bcrypt_aes256gcm = {"AES256-GCM", + PTLS_AESGCM_CONFIDENTIALITY_LIMIT, + PTLS_AESGCM_INTEGRITY_LIMIT, + &ptls_bcrypt_aes256ecb, + &ptls_bcrypt_aes256ctr, + PTLS_AES256_KEY_SIZE, + PTLS_AESGCM_IV_SIZE, + PTLS_AESGCM_TAG_SIZE, + sizeof(struct ptls_bcrypt_aead_context_t), + ptls_bcrypt_aead_setup_crypto_aesgcm}; + +ptls_hash_algorithm_t ptls_bcrypt_sha256 = {PTLS_SHA256_BLOCK_SIZE, PTLS_SHA256_DIGEST_SIZE, ptls_bcrypt_sha256_create, + PTLS_ZERO_DIGEST_SHA256}; +ptls_hash_algorithm_t ptls_bcrypt_sha384 = {PTLS_SHA384_BLOCK_SIZE, PTLS_SHA384_DIGEST_SIZE, ptls_bcrypt_sha384_create, + PTLS_ZERO_DIGEST_SHA384}; + +ptls_cipher_suite_t ptls_bcrypt_aes128gcmsha256 = {PTLS_CIPHER_SUITE_AES_128_GCM_SHA256, &ptls_bcrypt_aes128gcm, + &ptls_bcrypt_sha256}; +ptls_cipher_suite_t ptls_bcrypt_aes256gcmsha384 = {PTLS_CIPHER_SUITE_AES_256_GCM_SHA384, &ptls_bcrypt_aes256gcm, + &ptls_bcrypt_sha384}; + +#ifdef PRLS_BCRYPT_TODO +/* TODO: develp these bcrypt functions */ +ptls_key_exchange_algorithm_t ptls_bcrypt_secp256r1 = {PTLS_GROUP_SECP256R1, x9_62_create_key_exchange, secp_key_exchange, + NID_X9_62_prime256v1}; +#if ptls_bcrypt_HAVE_SECP384R1 +ptls_key_exchange_algorithm_t ptls_bcrypt_secp384r1 = {PTLS_GROUP_SECP384R1, x9_62_create_key_exchange, secp_key_exchange, + NID_secp384r1}; +#endif +#if ptls_bcrypt_HAVE_SECP521R1 +ptls_key_exchange_algorithm_t ptls_bcrypt_secp521r1 = {PTLS_GROUP_SECP521R1, x9_62_create_key_exchange, secp_key_exchange, + NID_secp521r1}; +#endif +#if ptls_bcrypt_HAVE_X25519 +ptls_key_exchange_algorithm_t ptls_bcrypt_x25519 = {PTLS_GROUP_X25519, evp_keyex_create, evp_keyex_exchange, NID_X25519}; +#endif + +ptls_key_exchange_algorithm_t *ptls_bcrypt_key_exchanges[] = {&ptls_bcrypt_secp256r1, NULL}; +#endif + +#endif /* _WINDOWS */ \ No newline at end of file diff --git a/deps/picoquic/picotls/lib/uecc.c b/deps/picoquic/picotls/lib/uecc.c new file mode 100644 index 0000000..41718d2 --- /dev/null +++ b/deps/picoquic/picotls/lib/uecc.c @@ -0,0 +1,191 @@ +/* + * Copyright (c) 2016 DeNA Co., Ltd., Kazuho Oku + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +#include +#include +#include +#include +#ifdef _WINDOWS +#include "wincompat.h" +#else +#include +#endif +#include "sha2.h" +#include "uECC.h" +#include "uECC_vli.h" +#include "picotls.h" +#include "picotls/minicrypto.h" + +#define TYPE_UNCOMPRESSED_PUBLIC_KEY 4 + +struct st_secp256r1_key_exhchange_t { + ptls_key_exchange_context_t super; + uint8_t priv[SECP256R1_PRIVATE_KEY_SIZE]; + uint8_t pub[SECP256R1_PUBLIC_KEY_SIZE]; +}; + +static int secp256r1_on_exchange(ptls_key_exchange_context_t **_ctx, int release, ptls_iovec_t *secret, ptls_iovec_t peerkey) +{ + struct st_secp256r1_key_exhchange_t *ctx = (struct st_secp256r1_key_exhchange_t *)*_ctx; + uint8_t *secbytes = NULL; + int ret; + + if (secret == NULL) { + ret = 0; + goto Exit; + } + + if (peerkey.len != SECP256R1_PUBLIC_KEY_SIZE || peerkey.base[0] != TYPE_UNCOMPRESSED_PUBLIC_KEY) { + ret = PTLS_ALERT_DECRYPT_ERROR; + goto Exit; + } + if ((secbytes = (uint8_t *)malloc(SECP256R1_SHARED_SECRET_SIZE)) == NULL) { + ret = PTLS_ERROR_NO_MEMORY; + goto Exit; + } + if (!uECC_shared_secret(peerkey.base + 1, ctx->priv, secbytes, uECC_secp256r1())) { + ret = PTLS_ALERT_DECRYPT_ERROR; + goto Exit; + } + *secret = ptls_iovec_init(secbytes, SECP256R1_SHARED_SECRET_SIZE); + ret = 0; + +Exit: + if (ret != 0) + free(secbytes); + if (release) { + ptls_clear_memory(ctx->priv, sizeof(ctx->priv)); + free(ctx); + *_ctx = NULL; + } + return ret; +} + +static int secp256r1_create_key_exchange(ptls_key_exchange_algorithm_t *algo, ptls_key_exchange_context_t **_ctx) +{ + struct st_secp256r1_key_exhchange_t *ctx; + + if ((ctx = (struct st_secp256r1_key_exhchange_t *)malloc(sizeof(*ctx))) == NULL) + return PTLS_ERROR_NO_MEMORY; + ctx->super = (ptls_key_exchange_context_t){algo, ptls_iovec_init(ctx->pub, sizeof(ctx->pub)), secp256r1_on_exchange}; + ctx->pub[0] = TYPE_UNCOMPRESSED_PUBLIC_KEY; + uECC_make_key(ctx->pub + 1, ctx->priv, uECC_secp256r1()); + + *_ctx = &ctx->super; + return 0; +} + +static int secp256r1_key_exchange(ptls_key_exchange_algorithm_t *algo, ptls_iovec_t *pubkey, ptls_iovec_t *secret, + ptls_iovec_t peerkey) +{ + uint8_t priv[SECP256R1_PRIVATE_KEY_SIZE], *pub = NULL, *secbytes = NULL; + int ret; + + if (peerkey.len != SECP256R1_PUBLIC_KEY_SIZE || peerkey.base[0] != TYPE_UNCOMPRESSED_PUBLIC_KEY) { + ret = PTLS_ALERT_DECRYPT_ERROR; + goto Exit; + } + if ((pub = malloc(SECP256R1_PUBLIC_KEY_SIZE)) == NULL) { + ret = PTLS_ERROR_NO_MEMORY; + goto Exit; + } + if ((secbytes = malloc(SECP256R1_SHARED_SECRET_SIZE)) == NULL) { + ret = PTLS_ERROR_NO_MEMORY; + goto Exit; + } + + pub[0] = TYPE_UNCOMPRESSED_PUBLIC_KEY; + uECC_make_key(pub + 1, priv, uECC_secp256r1()); + if (!uECC_shared_secret(peerkey.base + 1, priv, secbytes, uECC_secp256r1())) { + ret = PTLS_ALERT_DECRYPT_ERROR; + goto Exit; + } + *pubkey = ptls_iovec_init(pub, SECP256R1_PUBLIC_KEY_SIZE); + *secret = ptls_iovec_init(secbytes, SECP256R1_SHARED_SECRET_SIZE); + ret = 0; + +Exit: + ptls_clear_memory(priv, sizeof(priv)); + if (ret != 0) { + free(secbytes); + free(pub); + } + return ret; +} + +static int secp256r1sha256_sign(ptls_sign_certificate_t *_self, ptls_t *tls, uint16_t *selected_algorithm, ptls_buffer_t *outbuf, + ptls_iovec_t input, const uint16_t *algorithms, size_t num_algorithms) +{ + ptls_minicrypto_secp256r1sha256_sign_certificate_t *self = (ptls_minicrypto_secp256r1sha256_sign_certificate_t *)_self; + uint8_t hash[32], sig[64]; + size_t i; + int ret; + + /* check algorithm */ + for (i = 0; i != num_algorithms; ++i) + if (algorithms[i] == PTLS_SIGNATURE_ECDSA_SECP256R1_SHA256) + break; + if (i == num_algorithms) + return PTLS_ALERT_HANDSHAKE_FAILURE; + + { /* calc hash */ + cf_sha256_context ctx; + cf_sha256_init(&ctx); + cf_sha256_update(&ctx, input.base, input.len); + cf_sha256_digest_final(&ctx, hash); + ptls_clear_memory(&ctx, sizeof(ctx)); + } + + /* sign */ + uECC_sign(self->key, hash, sizeof(hash), sig, uECC_secp256r1()); + + /* encode using DER */ + ptls_buffer_push_asn1_sequence(outbuf, { + if ((ret = ptls_buffer_push_asn1_ubigint(outbuf, sig, 32)) != 0) + goto Exit; + if ((ret = ptls_buffer_push_asn1_ubigint(outbuf, sig + 32, 32)) != 0) + goto Exit; + }); + + *selected_algorithm = PTLS_SIGNATURE_ECDSA_SECP256R1_SHA256; + ret = 0; + +Exit: + ptls_clear_memory(hash, sizeof(hash)); + ptls_clear_memory(sig, sizeof(sig)); + return ret; +} + +int ptls_minicrypto_init_secp256r1sha256_sign_certificate(ptls_minicrypto_secp256r1sha256_sign_certificate_t *self, + ptls_iovec_t key) +{ + if (key.len != sizeof(self->key)) + return PTLS_ERROR_INCOMPATIBLE_KEY; + + self->super.cb = secp256r1sha256_sign; + memcpy(self->key, key.base, sizeof(self->key)); + + return 0; +} + +ptls_key_exchange_algorithm_t ptls_minicrypto_secp256r1 = {PTLS_GROUP_SECP256R1, secp256r1_create_key_exchange, + secp256r1_key_exchange}; +ptls_key_exchange_algorithm_t *ptls_minicrypto_key_exchanges[] = {&ptls_minicrypto_secp256r1, NULL}; diff --git a/deps/picoquic/picotls/picotlsvs/bcrypt-test/bcrypt-test.c b/deps/picoquic/picotls/picotlsvs/bcrypt-test/bcrypt-test.c new file mode 100644 index 0000000..513a344 --- /dev/null +++ b/deps/picoquic/picotls/picotlsvs/bcrypt-test/bcrypt-test.c @@ -0,0 +1,632 @@ +// bcrypt-test.cpp : This file contains the 'main' function. Program execution begins and ends there. +// +#include +#include +#include +#include "picotls/ptlsbcrypt.h" +#include "picotls/minicrypto.h" + +int KeyInit(BCRYPT_KEY_HANDLE *hKey, wchar_t *name, wchar_t *chain_mode, size_t chain_mode_sz, const BYTE *proposedKey, + DWORD proposedKeyLength, BYTE **ko, ULONG *ko_length) +{ + DWORD cbData = 0; + HANDLE hAlgo = NULL; + + // Open an algorithm handle. + NTSTATUS ret = BCryptOpenAlgorithmProvider(&hAlgo, name, NULL, 0); + + if (BCRYPT_SUCCESS(ret)) { + // Set the properties to define the chaining mode + ret = BCryptSetProperty(hAlgo, BCRYPT_CHAINING_MODE, (PBYTE)chain_mode, (ULONG)chain_mode_sz, 0); + } + + *ko = NULL; + *ko_length = 0; + + if (BCRYPT_SUCCESS(ret)) { + DWORD ko_size = 0; + ULONG cbResult = 0; + + ret = BCryptGetProperty(hAlgo, BCRYPT_OBJECT_LENGTH, (PUCHAR)&ko_size, (ULONG)sizeof(ko_size), &cbResult, 0); + + if (BCRYPT_SUCCESS(ret)) { + *ko = (uint8_t *)malloc(ko_size); + if (*ko == NULL) { + ret = STATUS_NO_MEMORY; + } else { + *ko_length = ko_size; + memset(*ko, 0, *ko_length); + } + } + } + + if (BCRYPT_SUCCESS(ret)) { + // Generate the key from supplied input key bytes. + ret = BCryptGenerateSymmetricKey(hAlgo, hKey, *ko, *ko_length, (PBYTE)proposedKey, proposedKeyLength, 0); + } else { + if (*ko != NULL) { + free(*ko); + *ko = NULL; + *ko_length = 0; + } + } + + if (hAlgo != NULL) { + BCryptCloseAlgorithmProvider(hAlgo, 0); + } + + return BCRYPT_SUCCESS(ret) ? 0 : -1; +} + +void KeyRelease(BCRYPT_KEY_HANDLE *hKey, BYTE **ko, ULONG *ko_length) +{ + BCryptDestroyKey(*hKey); + *hKey = NULL; + if (*ko) { + free(*ko); + } + *ko = NULL; + *ko_length = 0; +} + +int EncodeOneShot(ptls_aead_algorithm_t *aead, wchar_t *name, wchar_t *chain_mode, size_t chain_mode_sz, BYTE *key, + ULONG key_length, + BYTE* iv, ULONG iv_length, + BYTE *data, ULONG dataLength, uint64_t seq, BYTE *authData, ULONG authDataLength, ULONG authTagLength, + BYTE *encrypted, ULONG encryptedLengthMax, ULONG *encryptedLength) +{ + + BCRYPT_KEY_HANDLE hKey = NULL; + BYTE *authTag = encrypted + dataLength; + BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO bacmi; + BYTE iv_nonce[PTLS_MAX_IV_SIZE]; + BYTE *ko = NULL; + ULONG ko_length = 0; + int ret = 0; + + *encryptedLength = 0; + + if (KeyInit(&hKey, name, chain_mode, chain_mode_sz, key, key_length, &ko, &ko_length) != 0) { + return -1; + } + + memset(authTag, 0, authTagLength); + // Set the auth mode info for AEAD + BCRYPT_INIT_AUTH_MODE_INFO(bacmi); + ptls_aead__build_iv(aead, iv_nonce, iv, seq); + bacmi.pbNonce = iv_nonce; + bacmi.cbNonce = iv_length; + bacmi.pbAuthData = authData; + bacmi.cbAuthData = authDataLength; + bacmi.pbTag = authTag; + bacmi.cbTag = authTagLength; + /* All other fields are set to NULL by the INIT macro. */ + + /* If called with a NULL pointer for the data block, we will merely compute the block size. */ + DWORD cbCipherText = 0; + NTSTATUS status = BCryptEncrypt(hKey, data, dataLength, &bacmi, NULL, 0, encrypted, encryptedLengthMax, &cbCipherText, 0); + + KeyRelease(&hKey, &ko, &ko_length); + + if (BCRYPT_SUCCESS(status)) { + *encryptedLength = cbCipherText + authTagLength; + } else { + ret = -1; + } + + return ret; +} + +int DecodeOneShot(ptls_aead_algorithm_t *aead, wchar_t *name, wchar_t *chain_mode, size_t chain_mode_sz, + BYTE *key, ULONG key_length, BYTE * iv, ULONG iv_length, BYTE *encrypted, + ULONG encryptedLength, uint64_t seq, BYTE *authData, ULONG authDataLength, + ULONG authTagLength, BYTE *decrypted, ULONG decryptedLengthMax, ULONG *decryptedLength) +{ + + BCRYPT_KEY_HANDLE hKey = NULL; + BYTE *authTag = encrypted + (encryptedLength - authTagLength); + BYTE iv_nonce[PTLS_MAX_IV_SIZE]; + BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO bacmi; + BYTE *ko = NULL; + ULONG ko_length = 0; + int ret = 0; + + *decryptedLength = 0; + + if (KeyInit(&hKey, name, chain_mode, chain_mode_sz, key, key_length, &ko, &ko_length) != 0) { + return -1; + } + + // Set the auth mode info for AEAD + BCRYPT_INIT_AUTH_MODE_INFO(bacmi); + ptls_aead__build_iv(aead, iv_nonce, iv, seq); + bacmi.pbNonce = iv_nonce; + bacmi.cbNonce = iv_length; + bacmi.pbAuthData = authData; + bacmi.cbAuthData = authDataLength; + bacmi.pbTag = authTag; + bacmi.cbTag = authTagLength; + /* All other fields are set to NULL by the INIT macro. */ + + /* If called with a NULL pointer for the data block, we will merely compute the block size. */ + DWORD cbCipherText = 0; + NTSTATUS status = BCryptDecrypt(hKey, encrypted, encryptedLength - authTagLength, &bacmi, NULL, 0, decrypted, + decryptedLengthMax, &cbCipherText, 0); + + KeyRelease(&hKey, &ko, &ko_length); + + if (BCRYPT_SUCCESS(status)) { + *decryptedLength = cbCipherText; + } else { + ret = -1; + } + + return ret; +} + +int test_oneshot(ptls_aead_algorithm_t *aead, wchar_t *name, wchar_t *chain_mode, size_t chain_mode_sz) +{ + BYTE key[32]; + BYTE data[123]; + BYTE iv[PTLS_MAX_IV_SIZE]; + uint64_t nonce; + BYTE authData[9]; + BYTE encrypted[256]; + ULONG encryptedLength; + BYTE decrypted[256]; + ULONG decryptedLength; + ULONG authTagLength = (ULONG)aead->tag_size; + int ret = 0; + + assert(sizeof(key) >= aead->key_size); + assert(sizeof(iv) >= aead->iv_size); + assert(sizeof(data) + authTagLength <= sizeof(encrypted)); + assert(sizeof(decrypted) >= sizeof(encrypted)); + + memset(key, 'k', sizeof(key)); + memset(data, 'd', sizeof(data)); + memset(iv, 'n', sizeof(iv)); + nonce = 0; + memset(authData, 'a', sizeof(authData)); + + ret = EncodeOneShot(aead, name, chain_mode, chain_mode_sz, key, (ULONG)aead->key_size, iv, (ULONG)aead->iv_size, data, + 123, nonce, authData, 9, authTagLength, encrypted, 256, &encryptedLength); + + printf("Encrypt one shot returns %d, l=%d\n", ret, encryptedLength); + + if (ret == 0) { + ret = DecodeOneShot(aead, name, chain_mode, chain_mode_sz, key, (ULONG)aead->key_size, + iv, (ULONG)aead->iv_size, encrypted, encryptedLength, nonce, authData, 9, + authTagLength, decrypted, 256, &decryptedLength); + + printf("Decrypt one shot returns %d, l=%d\n", ret, decryptedLength); + + if (ret == 0) { + if (decryptedLength != 123) { + printf("Wrong length, not %d\n", 123); + ret = -1; + } else if (memcmp(data, decrypted, 123) != 0) { + printf("Data and decrypted don't match\n"); + ret = -1; + } else { + printf("One shot matches.\n"); + } + } + } + + return ret; +} + +void delete_test_aead_context(ptls_aead_context_t *ctx) +{ + if (ctx != NULL) { + ctx->dispose_crypto(ctx); + free(ctx); + } +} + +ptls_aead_context_t *new_test_aead_context(ptls_aead_algorithm_t *aead, int is_enc, BYTE *key, BYTE *iv) +{ + int ret = 0; + ptls_aead_context_t *ctx = (ptls_aead_context_t *)malloc(aead->context_size); + + if (ctx != NULL) { + memset(ctx, 0, aead->context_size); + *ctx = (ptls_aead_context_t){aead}; + if (aead->setup_crypto(ctx, is_enc, key, iv) != 0) { + printf("For %s, setup returns %d\n", aead->name, ret); + delete_test_aead_context(ctx); + ctx = NULL; + } + } else { + printf("For %s, memory error during setup\n", aead->name); + } + + return (ctx); +} + +int test_decrypt(ptls_aead_algorithm_t *aead, wchar_t *name, wchar_t *chain_mode, size_t chain_mode_sz) +{ + BYTE key[32]; + BYTE iv[PTLS_MAX_IV_SIZE]; + BYTE data[123]; + uint64_t nonce; + BYTE authData[9]; + BYTE encrypted[256]; + ULONG encryptedLength; + BYTE decrypted[256]; + size_t decryptedLength; + ULONG authTagLength = (ULONG)aead->tag_size; + ptls_aead_context_t *ctx = NULL; + int ret = 0; + + assert(sizeof(key) >= aead->key_size); + assert(sizeof(iv) >= aead->iv_size); + assert(sizeof(data) + authTagLength <= sizeof(encrypted)); + assert(sizeof(decrypted) >= sizeof(encrypted)); + + memset(key, 'k', sizeof(key)); + memset(iv, 'n', sizeof(iv)); + memset(data, 'd', sizeof(data)); + nonce = 0; + memset(authData, 'a', sizeof(authData)); + + /* Create a decryption context */ + ctx = new_test_aead_context(aead, 0, key, iv); + if (ctx == NULL) { + ret = -1; + } + + /* Do a simple encrypt using one shot bcrypt */ + if (ret == 0) { + ret = EncodeOneShot(aead, name, chain_mode, chain_mode_sz, key, (ULONG)aead->key_size, + iv, (ULONG)aead->iv_size, data, 123, nonce, + authData, 9, authTagLength, encrypted, 256, &encryptedLength); + } + + /* Try decrypt with library procedure */ + if (ret == 0) { + decryptedLength = ctx->do_decrypt(ctx, decrypted, encrypted, encryptedLength, nonce, authData, 9); + if (decryptedLength >= encryptedLength) { + printf("For %s, decrypt returns %d\n", aead->name, (int)decryptedLength); + ret = -1; + } else if (decryptedLength != 123) { + printf("For %s, decrypt returns %d instead of %d\n", aead->name, (int)decryptedLength, 123); + ret = -1; + } else if (memcmp(data, decrypted, decryptedLength) != 0) { + printf("For %s, decrypted does not match clear text\n", aead->name); + ret = -1; + } else { + printf("For %s, decrypting test passes.\n", aead->name); + } + } + + delete_test_aead_context(ctx); + + return ret; +} + +int test_encrypt(ptls_aead_algorithm_t *aead, wchar_t *name, wchar_t *chain_mode, size_t chain_mode_sz) +{ + BYTE key[32]; + BYTE iv[PTLS_MAX_IV_SIZE]; + BYTE data[123]; + uint64_t nonce; + BYTE authData[9]; + BYTE encryptedRef[256]; + ULONG encryptedRefLength; + BYTE encrypted[256]; + size_t encryptedLength; + ULONG authTagLength = (ULONG)aead->tag_size; + ptls_aead_context_t *ctx = NULL; + int ret = 0; + + assert(sizeof(key) >= aead->key_size); + assert(sizeof(iv) >= aead->iv_size); + assert(sizeof(data) + authTagLength <= sizeof(encrypted)); + assert(sizeof(data) + authTagLength <= sizeof(encryptedRef)); + + memset(key, 'k', sizeof(key)); + memset(iv, 'n', sizeof(iv)); + memset(data, 'd', sizeof(data)); + nonce = 0; + memset(authData, 'a', sizeof(authData)); + + /* Create an encryption context */ + ctx = new_test_aead_context(aead, 1, key, iv); + if (ctx == NULL) { + ret = -1; + } + + /* Do a simple encrypt using one shot bcrypt */ + if (ret == 0) { + ret = EncodeOneShot(aead, name, chain_mode, chain_mode_sz, key, (ULONG)aead->key_size, + iv, (ULONG)aead->iv_size, data, 123, nonce, + authData, 9, authTagLength, encryptedRef, 256, &encryptedRefLength); + } + + /* Try encrypt with library procedure */ + if (ret == 0) { + ctx->do_encrypt_init(ctx, nonce, authData, 9); + encryptedLength = ctx->do_encrypt_update(ctx, encrypted, data, 123); + encryptedLength += ctx->do_encrypt_final(ctx, &encrypted[encryptedLength]); + + if (encryptedLength != encryptedRefLength) { + printf("For %s, encrypt returns %d instead of %d\n", aead->name, (int)encryptedLength, encryptedRefLength); + ret = -1; + } else if (memcmp(encryptedRef, encrypted, encryptedRefLength) != 0) { + printf("For %s, encrypted does not match ref\n", aead->name); + for (ULONG i = 0; i < encryptedRefLength; i++) { + if (encryptedRef[i] != encrypted[i]) { + printf("For %s, encrypted[%d] = 0x%02x vs encryptedRef[%d] = 0x%02x\n", aead->name, i, encrypted[i], i, + encryptedRef[i]); + break; + } + } + ret = -1; + } else { + printf("For %s, encrypting test passes.\n", aead->name); + } + } + + delete_test_aead_context(ctx); + + return ret; +} + +int test_for_size(ptls_aead_algorithm_t *aead, wchar_t *name, wchar_t *chain_mode, size_t chain_mode_sz) +{ + BYTE key[32]; + BYTE iv[PTLS_MAX_IV_SIZE]; + uint64_t nonce; + BYTE authData[9]; + BYTE *data = NULL; + BYTE *encrypted = NULL; + BYTE *decrypted = NULL; + size_t encryptedLength; + size_t decryptedLength; + ULONG authTagLength = (ULONG)aead->tag_size; + ptls_aead_context_t *ctx_e = NULL; + ptls_aead_context_t *ctx_d = NULL; + ULONG packet_size[] = {1500, 128, 3, 0}; + ULONG nb_packet_size = (ULONG)(sizeof(packet_size) / sizeof(ULONG)); + int ret = 0; + + assert(sizeof(key) >= aead->key_size); + assert(sizeof(iv) >= aead->iv_size); + + memset(key, 'k', sizeof(key)); + memset(key, 'n', sizeof(iv)); + nonce = 0; + memset(authData, 'a', sizeof(authData)); + + /* Create the encryption contexts */ + ctx_e = new_test_aead_context(aead, 1, key, iv); + ctx_d = new_test_aead_context(aead, 0, key, iv); + + if (ctx_e == NULL || ctx_d == NULL) { + ret = -1; + } + + /* Test a variety of packet sizes */ + for (ULONG i = 0; ret == 0 && i < nb_packet_size; i++) { + ULONG data_size = (packet_size[i] > 0) ? packet_size[i] : 128; + ULONG encrypted_size = packet_size[i] + authTagLength; + + data = (BYTE *)malloc(data_size); + encrypted = (BYTE *)malloc(encrypted_size); + decrypted = (BYTE *)malloc(data_size); + + if (data == NULL || encrypted == NULL || decrypted == NULL) { + printf("For %s: cannot allocate memory for packet size[%d] = %d\n", aead->name, i, packet_size[i]); + } else { + memset(data, 'd', data_size); + + ctx_e->do_encrypt_init(ctx_e, nonce, authData, 9); + encryptedLength = ctx_e->do_encrypt_update(ctx_e, encrypted, data, packet_size[i]); + encryptedLength += ctx_e->do_encrypt_final(ctx_e, &encrypted[encryptedLength]); + decryptedLength = ctx_d->do_decrypt(ctx_d, decrypted, encrypted, encryptedLength, nonce, authData, 9); + + if (decryptedLength >= encryptedLength) { + printf("For %s, decrypt returns %d\n", aead->name, (int)decryptedLength); + ret = -1; + } else if (decryptedLength != packet_size[i]) { + printf("For %s, decrypt returns %d instead of %d\n", aead->name, (int)decryptedLength, packet_size[i]); + ret = -1; + } else if (memcmp(data, decrypted, decryptedLength) != 0) { + printf("For %s, decrypted does not match clear text\n", aead->name); + ret = -1; + } else { + printf("For %s, test packet size[%d] = %d passes.\n", aead->name, i, packet_size[i]); + } + } + + if (data != NULL) { + free(data); + data = NULL; + } + + if (encrypted != NULL) { + free(encrypted); + encrypted = NULL; + } + + if (decrypted != NULL) { + free(decrypted); + decrypted = NULL; + } + } + + delete_test_aead_context(ctx_e); + delete_test_aead_context(ctx_d); + + return ret; +} + +int test_one_aead(ptls_aead_algorithm_t *aead, wchar_t *name, wchar_t *chain_mode, size_t chain_mode_sz) +{ + int ret = test_oneshot(aead, name, chain_mode, chain_mode_sz); + + printf("For %s, test one shot returns %d\n", aead->name, ret); + + if (ret == 0) { + ret = test_decrypt(aead, name, chain_mode, chain_mode_sz); + + printf("For %s, test decrypt returns %d\n", aead->name, ret); + } + + if (ret == 0) { + ret = test_encrypt(aead, name, chain_mode, chain_mode_sz); + + printf("For %s, test encrypt returns %d\n", aead->name, ret); + } + + if (ret == 0) { + ret = test_for_size(aead, name, chain_mode, chain_mode_sz); + + printf("For %s, test packet sizes returns %d\n", aead->name, ret); + } + + return ret; +} + +/* Test of cipher functions. + * The test verifies that a message encode with a bcrypt function can be + * decoded with a minicrypto function, and vice versa. + */ + +int test_cipher_one_way(char const *name1, char const *name2, ptls_cipher_algorithm_t *b1, ptls_cipher_algorithm_t *b2, + unsigned int nb_blocks) +{ + BYTE key[32]; + BYTE nonce[16]; + BYTE data[128]; + BYTE encrypted[128]; + BYTE decrypted[128]; + size_t data_size = b1->block_size * nb_blocks; + ptls_cipher_context_t *ctx1 = NULL; + ptls_cipher_context_t *ctx2 = NULL; + int ret = 0; + + assert(sizeof(key) >= b1->key_size); + assert(sizeof(data) >= data_size); + assert(sizeof(nonce) >= b1->iv_size); + + memset(key, 'k', sizeof(key)); + memset(data, 'd', data_size); + + ctx1 = ptls_cipher_new(b1, 1, key); + ctx2 = ptls_cipher_new(b2, 0, key); + + if (ctx1 == NULL || ctx2 == NULL) { + ret = -1; + } else { + memset(nonce, 0, sizeof(nonce)); + if (ctx1->do_init != NULL) { + ctx1->do_init(ctx1, nonce); + } + + if (ctx2->do_init != NULL) { + ctx2->do_init(ctx2, nonce); + } + + ctx1->do_transform(ctx1, encrypted, data, data_size); + ctx2->do_transform(ctx2, decrypted, encrypted, data_size); + + if (memcmp(data, decrypted, data_size) != 0) { + printf("For %s -> %s, decrypted does not match clear text\n", name1, name2); + ret = -1; + } else { + printf("For %s -> %s, test passes.\n", name1, name2); + } + } + + if (ctx1 != NULL) { + ptls_cipher_free(ctx1); + } + + if (ctx2 != NULL) { + ptls_cipher_free(ctx2); + } + + return ret; +} + +int test_cipher_pair(char const *name1, char const *name2, ptls_cipher_algorithm_t *b1, ptls_cipher_algorithm_t *b2, + unsigned int nb_blocks) +{ + int ret = test_cipher_one_way(name1, name2, b1, b2, nb_blocks); + + if (ret == 0) { + ret = test_cipher_one_way(name2, name1, b2, b1, nb_blocks); + } + + return ret; +} + +/* Test of the hash functions + */ + +int test_hash_calc(char const *name1, char const *name2, ptls_hash_algorithm_t *h1, ptls_hash_algorithm_t *h2) +{ + BYTE data[123]; + BYTE tag1[128]; + BYTE tag2[128]; + ptls_hash_context_t *ctx1 = NULL; + ptls_hash_context_t *ctx2 = NULL; + int ret = 0; + + assert(sizeof(tag1) >= h1->digest_size); + assert(sizeof(tag2) >= h2->digest_size); + assert(h1->digest_size == h2->digest_size); + + memset(data, 'd', sizeof(data)); + memset(tag1, '1', sizeof(tag1)); + memset(tag2, '2', sizeof(tag2)); + + if (h1->digest_size != h2->digest_size) { + ret = -1; + } + if (ret == 0) { + ret = ptls_calc_hash(h1, tag1, data, sizeof(data)); + } + + if (ret == 0) { + ret = ptls_calc_hash(h2, tag2, data, sizeof(data)); + } + + if (ret == 0){ + if (memcmp(tag1, tag2, h1->digest_size) != 0) { + printf("For %s -> %s, hash1 does not match hash2\n", name1, name2); + ret = -1; + } else { + printf("For %s -> %s, hash test passes.\n", name1, name2); + } + } + + return ret; +} + +/* Minimal test program for the bcrypt functions. + * Need to add tests for the SHA256 and SHA384 implementations. + */ + +int main() +{ + int ret = 0; + + ret |= test_cipher_pair("bcrypt aes128ecb", "minicrypto aes128ecb", &ptls_bcrypt_aes128ecb, &ptls_minicrypto_aes128ecb, 1); + ret |= test_cipher_pair("bcrypt aes256ecb", "minicrypto aes256ecb", &ptls_bcrypt_aes256ecb, &ptls_minicrypto_aes256ecb, 1); + + ret |= test_cipher_pair("bcrypt aes128ctr", "minicrypto aes128ctr", &ptls_bcrypt_aes128ctr, &ptls_minicrypto_aes128ctr, 4); + ret |= test_cipher_pair("bcrypt aes256ctr", "minicrypto aes256ctr", &ptls_bcrypt_aes256ctr, &ptls_minicrypto_aes256ctr, 4); + + ret |= test_one_aead(&ptls_bcrypt_aes128gcm, BCRYPT_AES_ALGORITHM, BCRYPT_CHAIN_MODE_GCM, sizeof(BCRYPT_CHAIN_MODE_GCM)); + + ret |= test_one_aead(&ptls_bcrypt_aes256gcm, BCRYPT_AES_ALGORITHM, BCRYPT_CHAIN_MODE_GCM, sizeof(BCRYPT_CHAIN_MODE_GCM)); + + ret |= test_hash_calc("bcrypt sha256", "minicrypto sha256", &ptls_bcrypt_sha256, &ptls_minicrypto_sha256); + ret |= test_hash_calc("bcrypt sha384", "minicrypto sha384", &ptls_bcrypt_sha384, &ptls_minicrypto_sha384); + + exit(ret); +} diff --git a/deps/picoquic/picotls/picotlsvs/bcrypt-test/bcrypt-test.vcxproj b/deps/picoquic/picotls/picotlsvs/bcrypt-test/bcrypt-test.vcxproj new file mode 100644 index 0000000..4c71134 --- /dev/null +++ b/deps/picoquic/picotls/picotlsvs/bcrypt-test/bcrypt-test.vcxproj @@ -0,0 +1,171 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 16.0 + {7A04A2BF-03BF-4C3A-9E44-A53B0E90036B} + Win32Proj + bcrypttest + 10.0.17763.0 + + + + Application + true + v141 + Unicode + + + Application + false + v141 + true + Unicode + + + Application + true + v141 + Unicode + + + Application + false + v141 + true + Unicode + + + + + + + + + + + + + + + + + + + + + true + + + true + + + false + + + false + + + + + + Level3 + Disabled + true + _DEBUG;_CONSOLE;_WINDOWS;%(PreprocessorDefinitions) + true + $(ProjectDir)..\picotls;$(ProjectDir);$(ProjectDir)..\..\picotls;$(ProjectDir)..\..\include;$(ProjectDir)..\..\include\picotls;%(AdditionalIncludeDirectories) + + + Console + true + picotls-core.lib;picotls-bcrypt.lib;picotls-minicrypto.lib;picotls-minicrypto-deps.lib;bcrypt.lib;ws2_32.lib;%(AdditionalDependencies) + $(OutDir);%(AdditionalLibraryDirectories) + + + + + + + Level3 + Disabled + true + WIN32;_DEBUG;_CONSOLE;_WINDOWS;%(PreprocessorDefinitions) + true + $(ProjectDir)..\picotls;$(ProjectDir);$(ProjectDir)..\..\picotls;$(ProjectDir)..\..\include;$(ProjectDir)..\..\include\picotls;%(AdditionalIncludeDirectories) + + + Console + true + picotls-core.lib;picotls-bcrypt.lib;picotls-minicrypto.lib;picotls-minicrypto-deps.lib;bcrypt.lib;ws2_32.lib;%(AdditionalDependencies) + $(OutDir);%(AdditionalLibraryDirectories) + + + + + + + Level3 + MaxSpeed + true + true + true + WIN32;NDEBUG;_CONSOLE;_WINDOWS;%(PreprocessorDefinitions) + true + $(ProjectDir)..\picotls;$(ProjectDir);$(ProjectDir)..\..\picotls;$(ProjectDir)..\..\include;$(ProjectDir)..\..\include\picotls;%(AdditionalIncludeDirectories) + + + Console + true + true + true + $(OutDir);%(AdditionalLibraryDirectories) + picotls-core.lib;picotls-bcrypt.lib;picotls-minicrypto.lib;picotls-minicrypto-deps.lib;bcrypt.lib;ws2_32.lib;%(AdditionalDependencies) + + + + + + + Level3 + MaxSpeed + true + true + true + NDEBUG;_CONSOLE;_WINDOWS;%(PreprocessorDefinitions) + true + $(ProjectDir)..\picotls;$(ProjectDir);$(ProjectDir)..\..\picotls;$(ProjectDir)..\..\include;$(ProjectDir)..\..\include\picotls;%(AdditionalIncludeDirectories) + + + Console + true + true + true + $(OutDir);%(AdditionalLibraryDirectories) + picotls-core.lib;picotls-bcrypt.lib;picotls-minicrypto.lib;picotls-minicrypto-deps.lib;bcrypt.lib;ws2_32.lib;%(AdditionalDependencies) + + + + + + + + + \ No newline at end of file diff --git a/deps/picoquic/picotls/picotlsvs/bcrypt-test/bcrypt-test.vcxproj.filters b/deps/picoquic/picotls/picotlsvs/bcrypt-test/bcrypt-test.vcxproj.filters new file mode 100644 index 0000000..6d21ff8 --- /dev/null +++ b/deps/picoquic/picotls/picotlsvs/bcrypt-test/bcrypt-test.vcxproj.filters @@ -0,0 +1,22 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Source Files + + + \ No newline at end of file diff --git a/deps/picoquic/picotls/picotlsvs/bcrypt-test/bcrypt-test.vcxproj.user b/deps/picoquic/picotls/picotlsvs/bcrypt-test/bcrypt-test.vcxproj.user new file mode 100644 index 0000000..88a5509 --- /dev/null +++ b/deps/picoquic/picotls/picotlsvs/bcrypt-test/bcrypt-test.vcxproj.user @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/deps/picoquic/picotls/picotlsvs/cifra/ReadMe.txt b/deps/picoquic/picotls/picotlsvs/cifra/ReadMe.txt new file mode 100644 index 0000000..c1a42b2 --- /dev/null +++ b/deps/picoquic/picotls/picotlsvs/cifra/ReadMe.txt @@ -0,0 +1,29 @@ +======================================================================== + STATIC LIBRARY : cifra Project Overview +======================================================================== + +AppWizard has created this cifra library project for you. + +No source files were created as part of your project. + + +cifra.vcxproj + This is the main project file for VC++ projects generated using an Application Wizard. + It contains information about the version of Visual C++ that generated the file, and + information about the platforms, configurations, and project features selected with the + Application Wizard. + +cifra.vcxproj.filters + This is the filters file for VC++ projects generated using an Application Wizard. + It contains information about the association between the files in your project + and the filters. This association is used in the IDE to show grouping of files with + similar extensions under a specific node (for e.g. ".cpp" files are associated with the + "Source Files" filter). + +///////////////////////////////////////////////////////////////////////////// +Other notes: + +AppWizard uses "TODO:" comments to indicate parts of the source code you +should add to or customize. + +///////////////////////////////////////////////////////////////////////////// diff --git a/deps/picoquic/picotls/picotlsvs/cifra/cifra.vcxproj b/deps/picoquic/picotls/picotlsvs/cifra/cifra.vcxproj new file mode 100644 index 0000000..53c73cf --- /dev/null +++ b/deps/picoquic/picotls/picotlsvs/cifra/cifra.vcxproj @@ -0,0 +1,188 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 15.0 + {5D4DA3A3-7851-4CAE-AE4F-C421A2C8C440} + Win32Proj + cifra + 10.0.14393.0 + + + + StaticLibrary + true + v141 + Unicode + + + StaticLibrary + false + v141 + true + Unicode + + + StaticLibrary + true + v141 + Unicode + + + StaticLibrary + false + v141 + true + Unicode + + + + + + + + + + + + + + + + + + + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_LIB;_WINDOWS;%(PreprocessorDefinitions) + $(ProjectDir);$(ProjectDir)..\..\picotls;$(ProjectDir)..\..\include;$(ProjectDir)..\..\deps\cifra\src;$(ProjectDir)..\..\deps\cifra\src\ext;%(AdditionalIncludeDirectories) + + + Windows + + + + + + + Level3 + Disabled + _DEBUG;_LIB;_WINDOWS;%(PreprocessorDefinitions) + $(ProjectDir);$(ProjectDir)..\..\picotls;$(ProjectDir)..\..\include;$(ProjectDir)..\..\deps\cifra\src;$(ProjectDir)..\..\deps\cifra\src\ext;%(AdditionalIncludeDirectories) + + + Windows + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_LIB;_WINDOWS;%(PreprocessorDefinitions) + $(ProjectDir);$(ProjectDir)..\..\picotls;$(ProjectDir)..\..\include;$(ProjectDir)..\..\deps\cifra\src;$(ProjectDir)..\..\deps\cifra\src\ext;$(ProjectDir)\..\..\deps\micro-ecc;$(OPENSSLDIR)\include\;%(AdditionalIncludeDirectories) + + + Windows + true + true + + + + + Level3 + + + MaxSpeed + true + true + NDEBUG;_LIB;_WINDOWS;%(PreprocessorDefinitions) + $(ProjectDir);$(ProjectDir)..\..\picotls;$(ProjectDir)..\..\include;$(ProjectDir)..\..\deps\cifra\src;$(ProjectDir)..\..\deps\cifra\src\ext;%(AdditionalIncludeDirectories) + + + Windows + true + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/deps/picoquic/picotls/picotlsvs/cifra/cifra.vcxproj.filters b/deps/picoquic/picotls/picotlsvs/cifra/cifra.vcxproj.filters new file mode 100644 index 0000000..6c0de7f --- /dev/null +++ b/deps/picoquic/picotls/picotlsvs/cifra/cifra.vcxproj.filters @@ -0,0 +1,150 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + + \ No newline at end of file diff --git a/deps/picoquic/picotls/picotlsvs/fusiontest/fusiontest.vcxproj b/deps/picoquic/picotls/picotlsvs/fusiontest/fusiontest.vcxproj new file mode 100644 index 0000000..6e55e9e --- /dev/null +++ b/deps/picoquic/picotls/picotlsvs/fusiontest/fusiontest.vcxproj @@ -0,0 +1,166 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + + + + + 16.0 + {513DC1C9-2CD4-437C-B8EC-168924EB5AAC} + Win32Proj + fusiontest + 10.0.17763.0 + + + + Application + true + v141 + Unicode + + + Application + false + v141 + true + Unicode + + + Application + true + v141 + Unicode + + + Application + false + v141 + true + Unicode + + + + + + + + + + + + + + + + + + + + + true + + + true + + + false + + + false + + + + NotUsing + Level3 + true + _DEBUG;_CONSOLE;_WINDOWS;%(PreprocessorDefinitions) + true + $(ProjectDir)..\picotls;$(ProjectDir);$(ProjectDir)..\..\picotls;$(ProjectDir)..\..\include;$(ProjectDir)..\..\include\picotls;$(ProjectDir)..\..;%(AdditionalIncludeDirectories) + + + Console + true + $(OPENSSL64DIR);$(OPENSSL64DIR)\lib;$(OutDir);%(AdditionalLibraryDirectories) + picotls-core.lib;picotls-fusion.lib;picotls-minicrypto.lib;picotls-minicrypto-deps.lib;libcrypto.lib;bcrypt.lib;ws2_32.lib + + + + + NotUsing + Level3 + true + WIN32;_DEBUG;_CONSOLE;_WINDOWS;%(PreprocessorDefinitions) + true + $(ProjectDir)..\picotls;$(ProjectDir);$(ProjectDir)..\..\picotls;$(ProjectDir)..\..\include;$(ProjectDir)..\..\include\picotls;$(ProjectDir)..\..;%(AdditionalIncludeDirectories) + + + Console + true + $(OPENSSLDIR);$(OutDir);%(AdditionalLibraryDirectories) + picotls-core.lib;picotls-fusion.lib;picotls-minicrypto.lib;picotls-minicrypto-deps.lib;libcrypto.lib;bcrypt.lib;ws2_32.lib + + + + + + + Level3 + true + true + true + WIN32;NDEBUG;_CONSOLE;_WINDOWS;%(PreprocessorDefinitions) + true + $(ProjectDir)..\picotls;$(ProjectDir);$(ProjectDir)..\..\picotls;$(ProjectDir)..\..\include;$(ProjectDir)..\..\include\picotls;$(ProjectDir)..\..;%(AdditionalIncludeDirectories) + + + Console + true + true + true + picotls-core.lib;picotls-fusion.lib;picotls-minicrypto.lib;picotls-minicrypto-deps.lib;libcrypto.lib;bcrypt.lib;ws2_32.lib + $(OPENSSLDIR);$(OutDir);%(AdditionalLibraryDirectories) + + + + + + + Level3 + true + true + true + NDEBUG;_CONSOLE;_WINDOWS;%(PreprocessorDefinitions) + true + $(ProjectDir)..\picotls;$(ProjectDir);$(ProjectDir)..\..\picotls;$(ProjectDir)..\..\include;$(ProjectDir)..\..\include\picotls;$(ProjectDir)..\..;%(AdditionalIncludeDirectories) + + + Console + true + true + true + picotls-core.lib;picotls-fusion.lib;picotls-minicrypto.lib;picotls-minicrypto-deps.lib;libcrypto.lib;bcrypt.lib;ws2_32.lib + $(OPENSSL64DIR);$(OPENSSL64DIR)\lib;$(OutDir);%(AdditionalLibraryDirectories) + + + + + + \ No newline at end of file diff --git a/deps/picoquic/picotls/picotlsvs/fusiontest/fusiontest.vcxproj.filters b/deps/picoquic/picotls/picotlsvs/fusiontest/fusiontest.vcxproj.filters new file mode 100644 index 0000000..6869114 --- /dev/null +++ b/deps/picoquic/picotls/picotlsvs/fusiontest/fusiontest.vcxproj.filters @@ -0,0 +1,25 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Source Files + + + Source Files + + + \ No newline at end of file diff --git a/deps/picoquic/picotls/picotlsvs/fusiontest/fusiontest.vcxproj.user b/deps/picoquic/picotls/picotlsvs/fusiontest/fusiontest.vcxproj.user new file mode 100644 index 0000000..88a5509 --- /dev/null +++ b/deps/picoquic/picotls/picotlsvs/fusiontest/fusiontest.vcxproj.user @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/deps/picoquic/picotls/picotlsvs/microecc/ReadMe.txt b/deps/picoquic/picotls/picotlsvs/microecc/ReadMe.txt new file mode 100644 index 0000000..ed0a528 --- /dev/null +++ b/deps/picoquic/picotls/picotlsvs/microecc/ReadMe.txt @@ -0,0 +1,29 @@ +======================================================================== + STATIC LIBRARY : microecc Project Overview +======================================================================== + +AppWizard has created this microecc library project for you. + +No source files were created as part of your project. + + +microecc.vcxproj + This is the main project file for VC++ projects generated using an Application Wizard. + It contains information about the version of Visual C++ that generated the file, and + information about the platforms, configurations, and project features selected with the + Application Wizard. + +microecc.vcxproj.filters + This is the filters file for VC++ projects generated using an Application Wizard. + It contains information about the association between the files in your project + and the filters. This association is used in the IDE to show grouping of files with + similar extensions under a specific node (for e.g. ".cpp" files are associated with the + "Source Files" filter). + +///////////////////////////////////////////////////////////////////////////// +Other notes: + +AppWizard uses "TODO:" comments to indicate parts of the source code you +should add to or customize. + +///////////////////////////////////////////////////////////////////////////// diff --git a/deps/picoquic/picotls/picotlsvs/microecc/microecc.vcxproj b/deps/picoquic/picotls/picotlsvs/microecc/microecc.vcxproj new file mode 100644 index 0000000..46ebc5b --- /dev/null +++ b/deps/picoquic/picotls/picotlsvs/microecc/microecc.vcxproj @@ -0,0 +1,140 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 15.0 + {3440FDEA-84D2-4424-BB19-B4B26A6ADC8A} + Win32Proj + microecc + 10.0.14393.0 + + + + StaticLibrary + true + v141 + Unicode + + + StaticLibrary + false + v141 + true + Unicode + + + StaticLibrary + true + v141 + Unicode + + + StaticLibrary + false + v141 + true + Unicode + + + + + + + + + + + + + + + + + + + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_LIB;_WINDOWS;%(PreprocessorDefinitions) + + + Windows + + + + + + + Level3 + Disabled + _DEBUG;_LIB;_WINDOWS;%(PreprocessorDefinitions) + + + Windows + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_LIB;_WINDOWS;%(PreprocessorDefinitions) + $(ProjectDir);$(ProjectDir)\..\..\picotls;$(ProjectDir)\..\..\include;$(ProjectDir)\..\..\deps\cifra\src;$(ProjectDir)\..\..\deps\cifra\src\ext;$(ProjectDir)\..\..\deps\micro-ecc;$(OPENSSLDIR)\include\;%(AdditionalIncludeDirectories) + + + Windows + true + true + + + + + Level3 + + + MaxSpeed + true + true + NDEBUG;_LIB;_WINDOWS;%(PreprocessorDefinitions) + + + Windows + true + true + + + + + + + + + + + + \ No newline at end of file diff --git a/deps/picoquic/picotls/picotlsvs/microecc/microecc.vcxproj.filters b/deps/picoquic/picotls/picotlsvs/microecc/microecc.vcxproj.filters new file mode 100644 index 0000000..b7ed7be --- /dev/null +++ b/deps/picoquic/picotls/picotlsvs/microecc/microecc.vcxproj.filters @@ -0,0 +1,25 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + + + + Source Files + + + \ No newline at end of file diff --git a/deps/picoquic/picotls/picotlsvs/openssl.cnf b/deps/picoquic/picotls/picotlsvs/openssl.cnf new file mode 100644 index 0000000..c96e843 --- /dev/null +++ b/deps/picoquic/picotls/picotlsvs/openssl.cnf @@ -0,0 +1,35 @@ +[ req ] +default_bits = 1024 +default_keyfile= privkey.pem +distinguished_name = req_distinguished_name + + +[ req_distinguished_name ] +countryName= US +countryName_min= 2 +countryName_max = 2 +stateOrProvinceName= Washington +localityName = Seattle +organizationName = Example +commonName = Localhost +commonName_max = 64 +emailAddress = localhost@example.com +emailAddress_max = 40 + + +[ alternate_names ] + +DNS.1 = example.com +DNS.2 = www.example.com +DNS.3 = mail.example.com +DNS.4 = ftp.example.com + +# Add these if you need them. But usually you don't want them or +# need them in production. You may need them for development. +# DNS.5 = localhost +# DNS.6 = localhost.localdomain +# DNS.7 = 127.0.0.1 + +# IPv6 localhost +# DNS.8 = ::1 + diff --git a/deps/picoquic/picotls/picotlsvs/picotls-bcrypt/picotls-bcrypt.vcxproj b/deps/picoquic/picotls/picotlsvs/picotls-bcrypt/picotls-bcrypt.vcxproj new file mode 100644 index 0000000..7b5fb25 --- /dev/null +++ b/deps/picoquic/picotls/picotlsvs/picotls-bcrypt/picotls-bcrypt.vcxproj @@ -0,0 +1,142 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 16.0 + {0A0E7AF2-05C8-488B-867C-D83B776B8BF4} + picotlsbcrypt + 10.0.17763.0 + + + + StaticLibrary + true + v141 + MultiByte + + + StaticLibrary + false + v141 + true + MultiByte + + + StaticLibrary + true + v141 + MultiByte + + + StaticLibrary + false + v141 + true + MultiByte + + + + + + + + + + + + + + + + + + + + + + + Level3 + Disabled + true + true + _DEBUG;_LIB;_WINDOWS;_WINDOWS64;%(PreprocessorDefinitions) + $(ProjectDir)..\picotls;$(ProjectDir);$(ProjectDir)..\..\picotls;$(ProjectDir)..\..\include;$(ProjectDir)..\..\include\picotls;%(AdditionalIncludeDirectories) + + + Console + + + + + Level3 + Disabled + true + true + WIN32;_DEBUG;_LIB;_WINDOWS;%(PreprocessorDefinitions) + $(ProjectDir)..\picotls;$(ProjectDir);$(ProjectDir)..\..\picotls;$(ProjectDir)..\..\include;$(ProjectDir)..\..\include\picotls;%(AdditionalIncludeDirectories) + + + Console + + + + + Level3 + MaxSpeed + true + true + true + true + WIN32;NDEBUG;_LIB;_WINDOWS;%(PreprocessorDefinitions) + $(ProjectDir)..\picotls;$(ProjectDir);$(ProjectDir)..\..\picotls;$(ProjectDir)..\..\include;$(ProjectDir)..\..\include\picotls;%(AdditionalIncludeDirectories) + + + Console + true + true + + + + + Level3 + MaxSpeed + true + true + true + true + NDEBUG;_LIB;_WINDOWS;_WINDOWS64;%(PreprocessorDefinitions) + $(ProjectDir)..\picotls;$(ProjectDir);$(ProjectDir)..\..\picotls;$(ProjectDir)..\..\include;$(ProjectDir)..\..\include\picotls;%(AdditionalIncludeDirectories) + + + Console + true + true + + + + + + + + + + + + \ No newline at end of file diff --git a/deps/picoquic/picotls/picotlsvs/picotls-bcrypt/picotls-bcrypt.vcxproj.filters b/deps/picoquic/picotls/picotlsvs/picotls-bcrypt/picotls-bcrypt.vcxproj.filters new file mode 100644 index 0000000..2193df6 --- /dev/null +++ b/deps/picoquic/picotls/picotlsvs/picotls-bcrypt/picotls-bcrypt.vcxproj.filters @@ -0,0 +1,27 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Source Files + + + + + Header Files + + + \ No newline at end of file diff --git a/deps/picoquic/picotls/picotlsvs/picotls-bcrypt/picotls-bcrypt.vcxproj.user b/deps/picoquic/picotls/picotlsvs/picotls-bcrypt/picotls-bcrypt.vcxproj.user new file mode 100644 index 0000000..88a5509 --- /dev/null +++ b/deps/picoquic/picotls/picotlsvs/picotls-bcrypt/picotls-bcrypt.vcxproj.user @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/deps/picoquic/picotls/picotlsvs/picotls-core/picotls-core.vcxproj b/deps/picoquic/picotls/picotlsvs/picotls-core/picotls-core.vcxproj new file mode 100644 index 0000000..6cca7d6 --- /dev/null +++ b/deps/picoquic/picotls/picotlsvs/picotls-core/picotls-core.vcxproj @@ -0,0 +1,164 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 15.0 + {497433FE-B252-4985-A504-54EB791F57F4} + Win32Proj + picotlscore + 10.0.17763.0 + + + + StaticLibrary + true + v141 + Unicode + + + StaticLibrary + false + v141 + true + Unicode + + + StaticLibrary + true + v141 + Unicode + + + StaticLibrary + false + v141 + true + Unicode + + + + + + + + + + + + + + + + + + + + + true + + + true + + + false + + + false + + + + NotUsing + Level3 + Disabled + true + _DEBUG;_LIB;_WINDOWS;%(PreprocessorDefinitions) + true + $(ProjectDir)..\picotls;$(ProjectDir);$(ProjectDir)..\..\picotls;$(ProjectDir)..\..\include;$(ProjectDir)..\..\include\picotls;%(AdditionalIncludeDirectories) + + + Windows + true + + + + + NotUsing + Level3 + Disabled + true + WIN32;_DEBUG;_LIB;_WINDOWS;%(PreprocessorDefinitions) + true + $(ProjectDir)..\picotls;$(ProjectDir);$(ProjectDir)..\..\picotls;$(ProjectDir)..\..\include;$(ProjectDir)..\..\include\picotls;%(AdditionalIncludeDirectories) + + + Windows + true + + + + + NotUsing + Level3 + MaxSpeed + true + true + true + WIN32;NDEBUG;_LIB;_WINDOWS;%(PreprocessorDefinitions) + true + $(ProjectDir)..\picotls;$(ProjectDir);$(ProjectDir)..\..\picotls;$(ProjectDir)..\..\include;$(ProjectDir)..\..\include\picotls;%(AdditionalIncludeDirectories) + + + Windows + true + true + true + + + + + NotUsing + Level3 + MaxSpeed + true + true + true + NDEBUG;_LIB;_WINDOWS;%(PreprocessorDefinitions) + true + $(ProjectDir)..\picotls;$(ProjectDir);$(ProjectDir)..\..\picotls;$(ProjectDir)..\..\include;$(ProjectDir)..\..\include\picotls;%(AdditionalIncludeDirectories) + + + Windows + true + true + true + + + + + + + + + + + + + + \ No newline at end of file diff --git a/deps/picoquic/picotls/picotlsvs/picotls-core/picotls-core.vcxproj.filters b/deps/picoquic/picotls/picotlsvs/picotls-core/picotls-core.vcxproj.filters new file mode 100644 index 0000000..8cb862a --- /dev/null +++ b/deps/picoquic/picotls/picotlsvs/picotls-core/picotls-core.vcxproj.filters @@ -0,0 +1,33 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Header Files + + + + + Source Files + + + Source Files + + + Source Files + + + \ No newline at end of file diff --git a/deps/picoquic/picotls/picotlsvs/picotls-core/picotls-core.vcxproj.user b/deps/picoquic/picotls/picotlsvs/picotls-core/picotls-core.vcxproj.user new file mode 100644 index 0000000..be25078 --- /dev/null +++ b/deps/picoquic/picotls/picotlsvs/picotls-core/picotls-core.vcxproj.user @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/deps/picoquic/picotls/picotlsvs/picotls-core/targetver.h b/deps/picoquic/picotls/picotlsvs/picotls-core/targetver.h new file mode 100644 index 0000000..87c0086 --- /dev/null +++ b/deps/picoquic/picotls/picotlsvs/picotls-core/targetver.h @@ -0,0 +1,8 @@ +#pragma once + +// Including SDKDDKVer.h defines the highest available Windows platform. + +// If you wish to build your application for a previous Windows platform, include WinSDKVer.h and +// set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h. + +#include diff --git a/deps/picoquic/picotls/picotlsvs/picotls-esni/getopt.c b/deps/picoquic/picotls/picotlsvs/picotls-esni/getopt.c new file mode 100644 index 0000000..21cf6ca --- /dev/null +++ b/deps/picoquic/picotls/picotlsvs/picotls-esni/getopt.c @@ -0,0 +1,103 @@ +#include "getopt.h" + +/* +* Copyright (c) 1987, 1993, 1994 +* The Regents of the University of California. All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions +* are met: +* 1. Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* 2. Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* 3. All advertising materials mentioning features or use of this software +* must display the following acknowledgement: +* This product includes software developed by the University of +* California, Berkeley and its contributors. +* 4. Neither the name of the University nor the names of its contributors +* may be used to endorse or promote products derived from this software +* without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +* SUCH DAMAGE. +*/ + +#include +#include + +int opterr = 1, /* if error message should be printed */ + optind = 1, /* index into parent argv vector */ + optopt, /* character checked for validity */ + optreset; /* reset getopt */ +const char* optarg; /* argument associated with option */ + +#define BADCH (int)'?' +#define BADARG (int)':' +#define EMSG "" + +/* + * getopt -- + * Parse argc/argv argument vector. + */ +int getopt(int nargc, char* const nargv[], const char* ostr) +{ + static const char* place = EMSG; /* option letter processing */ + const char* oli; /* option letter list index */ + + if (optreset || !*place) { /* update scanning pointer */ + optreset = 0; + if (optind >= nargc || *(place = nargv[optind]) != '-') { + place = EMSG; + return (-1); + } + if (place[1] && *++place == '-') { /* found "--" */ + ++optind; + place = EMSG; + return (-1); + } + } /* option letter okay? */ + if ((optopt = (int)*place++) == (int)':' || !(oli = strchr(ostr, optopt))) { + /* + * if the user didn't specify '-' as an option, + * assume it means -1. + */ + if (optopt == (int)'-') + return (-1); + if (!*place) + ++optind; + if (opterr && *ostr != ':') + (void)printf("illegal option -- %c\n", optopt); + return (BADCH); + } + if (*++oli != ':') { /* don't need argument */ + optarg = NULL; + if (!*place) + ++optind; + } else { /* need an argument */ + if (*place) /* no white space */ + optarg = place; + else if (nargc <= ++optind) { /* no arg */ + place = EMSG; + if (*ostr == ':') + return (BADARG); + if (opterr) + (void)printf("option requires an argument -- %c\n", optopt); + return (BADCH); + } else /* white space */ + optarg = nargv[optind]; + place = EMSG; + ++optind; + } + return (optopt); /* dump back option letter */ +} diff --git a/deps/picoquic/picotls/picotlsvs/picotls-esni/getopt.h b/deps/picoquic/picotls/picotlsvs/picotls-esni/getopt.h new file mode 100644 index 0000000..1ea538e --- /dev/null +++ b/deps/picoquic/picotls/picotlsvs/picotls-esni/getopt.h @@ -0,0 +1,19 @@ +#ifndef GETOPT_H +#ifndef __APPLE__ + +#define GETOPT_H + +#ifndef _GETOPT_H +#define _GETOPT_H +#endif + +extern int opterr; /* if error message should be printed */ +extern int optind; /* index into parent argv vector */ +extern int optopt; /* character checked for validity */ +extern int optreset; /* reset getopt */ +extern const char* optarg; /* argument associated with option */ + +int getopt(int nargc, char* const nargv[], const char* ostr); + +#endif +#endif diff --git a/deps/picoquic/picotls/picotlsvs/picotls-esni/picotls-esni.vcxproj b/deps/picoquic/picotls/picotlsvs/picotls-esni/picotls-esni.vcxproj new file mode 100644 index 0000000..0e22f56 --- /dev/null +++ b/deps/picoquic/picotls/picotlsvs/picotls-esni/picotls-esni.vcxproj @@ -0,0 +1,175 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + + + + + + + + 15.0 + {592127C5-DD8C-47ED-8EBA-026B5848C374} + Win32Proj + picotlsesni + 10.0.17763.0 + + + + Application + true + v141 + Unicode + + + Application + false + v141 + true + Unicode + + + Application + true + v141 + Unicode + + + Application + false + v141 + true + Unicode + + + + + + + + + + + + + + + + + + + + + true + + + true + + + false + + + false + + + + NotUsing + Level3 + Disabled + true + _DEBUG;_CONSOLE;_WINDOWS;%(PreprocessorDefinitions) + true + pch.h + $(ProjectDir);$(ProjectDir)..\picotls;$(ProjectDir)..\..\include;$(OPENSSL64DIR)\include + + + Console + true + $(OPENSSL64DIR);$(OPENSSL64DIR)\lib;$(OutDir);%(AdditionalLibraryDirectories) + picotls-core.lib;picotls-openssl.lib;picotls-minicrypto.lib;picotls-minicrypto-deps.lib;libcrypto.lib;bcrypt.lib;ws2_32.lib;%(AdditionalDependencies) + + + + + NotUsing + Level3 + Disabled + true + WIN32;_DEBUG;_CONSOLE;_WINDOWS;%(PreprocessorDefinitions) + true + pch.h + $(ProjectDir);$(ProjectDir)..\picotls;$(ProjectDir)..\..\include;$(OPENSSLDIR)\include;%(AdditionalIncludeDirectories) + + + Console + true + $(OPENSSLDIR);$(OPENSSLDIR)\lib;$(OutDir);%(AdditionalLibraryDirectories) + picotls-core.lib;picotls-openssl.lib;picotls-minicrypto.lib;picotls-minicrypto-deps.lib;libcrypto.lib;bcrypt.lib;ws2_32.lib;%(AdditionalDependencies) + + + + + NotUsing + Level3 + MaxSpeed + true + true + true + WIN32;NDEBUG;_CONSOLE;_WINDOWS;%(PreprocessorDefinitions) + true + pch.h + $(ProjectDir);$(ProjectDir)..\picotls;$(ProjectDir)..\..\include;$(OPENSSLDIR)\include;%(AdditionalIncludeDirectories) + + + Console + true + true + true + $(OPENSSLDIR);$(OPENSSLDIR)\lib;$(OutDir);%(AdditionalLibraryDirectories) + picotls-core.lib;picotls-openssl.lib;picotls-minicrypto.lib;picotls-minicrypto-deps.lib;libcrypto.lib;bcrypt.lib;ws2_32.lib;%(AdditionalDependencies) + + + + + NotUsing + Level3 + MaxSpeed + true + true + true + NDEBUG;_CONSOLE;_WINDOWS;%(PreprocessorDefinitions) + true + pch.h + $(ProjectDir);$(ProjectDir)..\picotls;$(ProjectDir)..\..\include;$(OPENSSL64DIR)\include + + + Console + true + true + true + $(OPENSSL64DIR);$(OPENSSL64DIR)\lib;$(OutDir);%(AdditionalLibraryDirectories) + picotls-core.lib;picotls-openssl.lib;picotls-minicrypto.lib;picotls-minicrypto-deps.lib;libcrypto.lib;bcrypt.lib;ws2_32.lib;%(AdditionalDependencies) + + + + + + \ No newline at end of file diff --git a/deps/picoquic/picotls/picotlsvs/picotls-esni/picotls-esni.vcxproj.filters b/deps/picoquic/picotls/picotlsvs/picotls-esni/picotls-esni.vcxproj.filters new file mode 100644 index 0000000..2ad14fc --- /dev/null +++ b/deps/picoquic/picotls/picotlsvs/picotls-esni/picotls-esni.vcxproj.filters @@ -0,0 +1,30 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Source Files + + + Source Files + + + + + Header Files + + + \ No newline at end of file diff --git a/deps/picoquic/picotls/picotlsvs/picotls-esni/picotls-esni.vcxproj.user b/deps/picoquic/picotls/picotlsvs/picotls-esni/picotls-esni.vcxproj.user new file mode 100644 index 0000000..1bac74a --- /dev/null +++ b/deps/picoquic/picotls/picotlsvs/picotls-esni/picotls-esni.vcxproj.user @@ -0,0 +1,8 @@ + + + + -K ..\..\..\..\picoquic\certs\esni-secp256r1.key -a rr.txt > rr.bin + $(OutDir) + WindowsLocalDebugger + + \ No newline at end of file diff --git a/deps/picoquic/picotls/picotlsvs/picotls-fusion/picotls-fusion.vcxproj b/deps/picoquic/picotls/picotlsvs/picotls-fusion/picotls-fusion.vcxproj new file mode 100644 index 0000000..06213f0 --- /dev/null +++ b/deps/picoquic/picotls/picotlsvs/picotls-fusion/picotls-fusion.vcxproj @@ -0,0 +1,150 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 16.0 + {55F22DE6-EAAE-4279-97B7-84FAAB7F29BB} + picotlsfusion + 10.0.17763.0 + + + + StaticLibrary + true + v141 + Unicode + + + StaticLibrary + false + v141 + true + Unicode + + + StaticLibrary + true + v141 + Unicode + + + StaticLibrary + false + v141 + true + Unicode + + + + + + + + + + + + + + + + + + + + + true + + + true + + + false + + + false + + + + Level3 + true + _DEBUG;_LIB;_WINDOWS;_WINDOWS64;%(PreprocessorDefinitions) + true + $(ProjectDir)..\picotls;$(ProjectDir);$(ProjectDir)..\..\picotls;$(ProjectDir)..\..\include;$(ProjectDir)..\..\include\picotls;%(AdditionalIncludeDirectories) + + + Console + true + + + + + Level3 + true + WIN32;_DEBUG;_LIB;_WINDOWS;%(PreprocessorDefinitions) + true + $(ProjectDir)..\picotls;$(ProjectDir);$(ProjectDir)..\..\picotls;$(ProjectDir)..\..\include;$(ProjectDir)..\..\include\picotls;%(AdditionalIncludeDirectories) + + + Console + true + + + + + Level3 + true + true + true + WIN32;NDEBUG;_LIB;_WINDOWS;%(PreprocessorDefinitions) + true + $(ProjectDir)..\picotls;$(ProjectDir);$(ProjectDir)..\..\picotls;$(ProjectDir)..\..\include;$(ProjectDir)..\..\include\picotls;%(AdditionalIncludeDirectories) + + + Console + true + true + true + + + + + Level3 + true + true + true + NDEBUG;_LIB;_WINDOWS;_WINDOWS64;%(PreprocessorDefinitions) + true + $(ProjectDir)..\picotls;$(ProjectDir);$(ProjectDir)..\..\picotls;$(ProjectDir)..\..\include;$(ProjectDir)..\..\include\picotls;%(AdditionalIncludeDirectories) + + + Console + true + true + true + + + + + + + + + \ No newline at end of file diff --git a/deps/picoquic/picotls/picotlsvs/picotls-fusion/picotls-fusion.vcxproj.filters b/deps/picoquic/picotls/picotlsvs/picotls-fusion/picotls-fusion.vcxproj.filters new file mode 100644 index 0000000..66181a5 --- /dev/null +++ b/deps/picoquic/picotls/picotlsvs/picotls-fusion/picotls-fusion.vcxproj.filters @@ -0,0 +1,22 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Source Files + + + \ No newline at end of file diff --git a/deps/picoquic/picotls/picotlsvs/picotls-fusion/picotls-fusion.vcxproj.user b/deps/picoquic/picotls/picotlsvs/picotls-fusion/picotls-fusion.vcxproj.user new file mode 100644 index 0000000..88a5509 --- /dev/null +++ b/deps/picoquic/picotls/picotlsvs/picotls-fusion/picotls-fusion.vcxproj.user @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/deps/picoquic/picotls/picotlsvs/picotls-minicrypto-deps/picotls-minicrypto-deps.vcxproj b/deps/picoquic/picotls/picotlsvs/picotls-minicrypto-deps/picotls-minicrypto-deps.vcxproj new file mode 100644 index 0000000..27e57f6 --- /dev/null +++ b/deps/picoquic/picotls/picotlsvs/picotls-minicrypto-deps/picotls-minicrypto-deps.vcxproj @@ -0,0 +1,176 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 15.0 + {499B82B3-F5A5-4C2E-91EF-A2F77CBC33F5} + Win32Proj + picotlsminicryptodeps + 10.0.17763.0 + + + + StaticLibrary + true + v141 + Unicode + + + StaticLibrary + false + v141 + true + Unicode + + + StaticLibrary + true + v141 + Unicode + + + StaticLibrary + false + v141 + true + Unicode + + + + + + + + + + + + + + + + + + + + + true + + + true + + + false + + + false + + + + NotUsing + Level3 + Disabled + true + _DEBUG;_LIB;_WINDOWS;%(PreprocessorDefinitions) + true + $(ProjectDir);$(ProjectDir)..\picotls;$(ProjectDir)..\..\picotls;$(ProjectDir)..\..\include;$(ProjectDir)..\..\include\picotls;$(ProjectDir)\..\..\deps\cifra\src;$(ProjectDir)\..\..\deps\cifra\src\ext;$(ProjectDir)\..\..\deps\micro-ecc;%(AdditionalIncludeDirectories) + + + Windows + true + + + + + NotUsing + Level3 + Disabled + true + WIN32;_DEBUG;_LIB;_WINDOWS;%(PreprocessorDefinitions) + true + $(ProjectDir);$(ProjectDir)..\picotls;$(ProjectDir)..\..\picotls;$(ProjectDir)..\..\include;$(ProjectDir)..\..\include\picotls;$(ProjectDir)\..\..\deps\cifra\src;$(ProjectDir)\..\..\deps\cifra\src\ext;$(ProjectDir)\..\..\deps\micro-ecc;%(AdditionalIncludeDirectories) + + + Windows + true + + + + + NotUsing + Level3 + MaxSpeed + true + true + true + WIN32;NDEBUG;_LIB;_WINDOWS;%(PreprocessorDefinitions) + true + $(ProjectDir);$(ProjectDir)..\picotls;$(ProjectDir)..\..\picotls;$(ProjectDir)..\..\include;$(ProjectDir)..\..\include\picotls;$(ProjectDir)\..\..\deps\cifra\src;$(ProjectDir)\..\..\deps\cifra\src\ext;$(ProjectDir)\..\..\deps\micro-ecc;%(AdditionalIncludeDirectories) + + + Windows + true + true + true + + + + + NotUsing + Level3 + MaxSpeed + true + true + true + NDEBUG;_LIB;_WINDOWS;%(PreprocessorDefinitions) + true + $(ProjectDir);$(ProjectDir)..\picotls;$(ProjectDir)..\..\picotls;$(ProjectDir)..\..\include;$(ProjectDir)..\..\include\picotls;$(ProjectDir)\..\..\deps\cifra\src;$(ProjectDir)\..\..\deps\cifra\src\ext;$(ProjectDir)\..\..\deps\micro-ecc;%(AdditionalIncludeDirectories) + + + Windows + true + true + true + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/deps/picoquic/picotls/picotlsvs/picotls-minicrypto-deps/picotls-minicrypto-deps.vcxproj.filters b/deps/picoquic/picotls/picotlsvs/picotls-minicrypto-deps/picotls-minicrypto-deps.vcxproj.filters new file mode 100644 index 0000000..909a8ca --- /dev/null +++ b/deps/picoquic/picotls/picotlsvs/picotls-minicrypto-deps/picotls-minicrypto-deps.vcxproj.filters @@ -0,0 +1,69 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Header Files + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + \ No newline at end of file diff --git a/deps/picoquic/picotls/picotlsvs/picotls-minicrypto-deps/picotls-minicrypto-deps.vcxproj.user b/deps/picoquic/picotls/picotlsvs/picotls-minicrypto-deps/picotls-minicrypto-deps.vcxproj.user new file mode 100644 index 0000000..be25078 --- /dev/null +++ b/deps/picoquic/picotls/picotlsvs/picotls-minicrypto-deps/picotls-minicrypto-deps.vcxproj.user @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/deps/picoquic/picotls/picotlsvs/picotls-minicrypto-deps/targetver.h b/deps/picoquic/picotls/picotlsvs/picotls-minicrypto-deps/targetver.h new file mode 100644 index 0000000..87c0086 --- /dev/null +++ b/deps/picoquic/picotls/picotlsvs/picotls-minicrypto-deps/targetver.h @@ -0,0 +1,8 @@ +#pragma once + +// Including SDKDDKVer.h defines the highest available Windows platform. + +// If you wish to build your application for a previous Windows platform, include WinSDKVer.h and +// set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h. + +#include diff --git a/deps/picoquic/picotls/picotlsvs/picotls-minicrypto/picotls-minicrypto.vcxproj b/deps/picoquic/picotls/picotlsvs/picotls-minicrypto/picotls-minicrypto.vcxproj new file mode 100644 index 0000000..6469f0e --- /dev/null +++ b/deps/picoquic/picotls/picotlsvs/picotls-minicrypto/picotls-minicrypto.vcxproj @@ -0,0 +1,171 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 15.0 + {559AC085-1BEF-450A-A62D-0D370561D596} + Win32Proj + picotlsminicrypto + 10.0.17763.0 + + + + StaticLibrary + true + v141 + Unicode + + + StaticLibrary + false + v141 + true + Unicode + + + StaticLibrary + true + v141 + Unicode + + + StaticLibrary + false + v141 + true + Unicode + + + + + + + + + + + + + + + + + + + + + true + + + true + + + false + + + false + + + + NotUsing + Level3 + Disabled + true + _DEBUG;_LIB;_WINDOWS;%(PreprocessorDefinitions) + true + $(ProjectDir);$(ProjectDir)..\picotls;$(ProjectDir)..\..\picotls;$(ProjectDir)..\..\include;$(ProjectDir)..\..\include\picotls;$(ProjectDir)\..\..\deps\cifra\src;$(ProjectDir)\..\..\deps\micro-ecc;%(AdditionalIncludeDirectories) + + + Windows + true + + + + + NotUsing + Level3 + Disabled + true + WIN32;_DEBUG;_LIB;_WINDOWS;%(PreprocessorDefinitions) + true + $(ProjectDir);$(ProjectDir)..\picotls;$(ProjectDir)..\..\picotls;$(ProjectDir)..\..\include;$(ProjectDir)..\..\include\picotls;$(ProjectDir)\..\..\deps\cifra\src;$(ProjectDir)\..\..\deps\micro-ecc;%(AdditionalIncludeDirectories) + + + Windows + true + + + + + NotUsing + Level3 + MaxSpeed + true + true + true + WIN32;NDEBUG;_LIB;_WINDOWS;%(PreprocessorDefinitions) + true + $(ProjectDir);$(ProjectDir)..\picotls;$(ProjectDir)..\..\picotls;$(ProjectDir)..\..\include;$(ProjectDir)..\..\include\picotls;$(ProjectDir)\..\..\deps\cifra\src;$(ProjectDir)\..\..\deps\micro-ecc;%(AdditionalIncludeDirectories) + + + Windows + true + true + true + + + + + NotUsing + Level3 + MaxSpeed + true + true + true + NDEBUG;_LIB;_WINDOWS;%(PreprocessorDefinitions) + true + $(ProjectDir);$(ProjectDir)..\picotls;$(ProjectDir)..\..\picotls;$(ProjectDir)..\..\include;$(ProjectDir)..\..\include\picotls;$(ProjectDir)\..\..\deps\cifra\src;$(ProjectDir)\..\..\deps\micro-ecc;%(AdditionalIncludeDirectories) + + + Windows + true + true + true + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/deps/picoquic/picotls/picotlsvs/picotls-minicrypto/picotls-minicrypto.vcxproj.filters b/deps/picoquic/picotls/picotlsvs/picotls-minicrypto/picotls-minicrypto.vcxproj.filters new file mode 100644 index 0000000..b770ce5 --- /dev/null +++ b/deps/picoquic/picotls/picotlsvs/picotls-minicrypto/picotls-minicrypto.vcxproj.filters @@ -0,0 +1,54 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Header Files + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + \ No newline at end of file diff --git a/deps/picoquic/picotls/picotlsvs/picotls-minicrypto/picotls-minicrypto.vcxproj.user b/deps/picoquic/picotls/picotlsvs/picotls-minicrypto/picotls-minicrypto.vcxproj.user new file mode 100644 index 0000000..be25078 --- /dev/null +++ b/deps/picoquic/picotls/picotlsvs/picotls-minicrypto/picotls-minicrypto.vcxproj.user @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/deps/picoquic/picotls/picotlsvs/picotls-minicrypto/targetver.h b/deps/picoquic/picotls/picotlsvs/picotls-minicrypto/targetver.h new file mode 100644 index 0000000..87c0086 --- /dev/null +++ b/deps/picoquic/picotls/picotlsvs/picotls-minicrypto/targetver.h @@ -0,0 +1,8 @@ +#pragma once + +// Including SDKDDKVer.h defines the highest available Windows platform. + +// If you wish to build your application for a previous Windows platform, include WinSDKVer.h and +// set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h. + +#include diff --git a/deps/picoquic/picotls/picotlsvs/picotls-openssl/picotls-openssl.vcxproj b/deps/picoquic/picotls/picotlsvs/picotls-openssl/picotls-openssl.vcxproj new file mode 100644 index 0000000..ffa492c --- /dev/null +++ b/deps/picoquic/picotls/picotlsvs/picotls-openssl/picotls-openssl.vcxproj @@ -0,0 +1,162 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 15.0 + {56C264BF-822B-4F29-B512-5B26157CA2EC} + Win32Proj + picotlsopenssl + 10.0.17763.0 + + + + StaticLibrary + true + v141 + Unicode + + + StaticLibrary + false + v141 + true + Unicode + + + StaticLibrary + true + v141 + Unicode + + + StaticLibrary + false + v141 + true + Unicode + + + + + + + + + + + + + + + + + + + + + true + + + true + + + false + + + false + + + + NotUsing + Level3 + Disabled + true + _DEBUG;_LIB;_WINDOWS;%(PreprocessorDefinitions) + true + $(OPENSSL64DIR)\include;$(OPENSSL64DIR);$(ProjectDir);$(ProjectDir)..\picotls;$(ProjectDir)..\..\picotls;$(ProjectDir)..\..\include;$(ProjectDir)..\..\include\picotls;%(AdditionalIncludeDirectories) + + + Windows + true + + + + + NotUsing + Level3 + Disabled + true + WIN32;_DEBUG;_LIB;_WINDOWS;%(PreprocessorDefinitions) + true + $(OPENSSLDIR)\include;$(OPENSSLDIR);$(ProjectDir);$(ProjectDir)..\picotls;$(ProjectDir)\..\..\picotls;$(ProjectDir)\..\..\include;$(ProjectDir)\..\..\deps\cifra\src;%(AdditionalIncludeDirectories) + + + Windows + true + + + + + NotUsing + Level3 + MaxSpeed + true + true + true + WIN32;NDEBUG;_LIB;_WINDOWS;%(PreprocessorDefinitions) + true + $(OPENSSLDIR)\include;$(OPENSSLDIR);$(ProjectDir);$(ProjectDir)..\picotls;$(ProjectDir)\..\..\picotls;$(ProjectDir)\..\..\include;$(ProjectDir)\..\..\deps\cifra\src;%(AdditionalIncludeDirectories) + + + Windows + true + true + true + + + + + NotUsing + Level3 + MaxSpeed + true + true + true + NDEBUG;_LIB;_WINDOWS;%(PreprocessorDefinitions) + true + $(OPENSSL64DIR)\include;$(OPENSSL64DIR);$(ProjectDir);$(ProjectDir)..\picotls;$(ProjectDir)..\..\picotls;$(ProjectDir)..\..\include;$(ProjectDir)..\..\include\picotls;%(AdditionalIncludeDirectories) + + + Windows + true + true + true + + + + + + + + + + + + \ No newline at end of file diff --git a/deps/picoquic/picotls/picotlsvs/picotls-openssl/picotls-openssl.vcxproj.filters b/deps/picoquic/picotls/picotlsvs/picotls-openssl/picotls-openssl.vcxproj.filters new file mode 100644 index 0000000..d199a14 --- /dev/null +++ b/deps/picoquic/picotls/picotlsvs/picotls-openssl/picotls-openssl.vcxproj.filters @@ -0,0 +1,27 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Header Files + + + + + Source Files + + + \ No newline at end of file diff --git a/deps/picoquic/picotls/picotlsvs/picotls-openssl/picotls-openssl.vcxproj.user b/deps/picoquic/picotls/picotlsvs/picotls-openssl/picotls-openssl.vcxproj.user new file mode 100644 index 0000000..be25078 --- /dev/null +++ b/deps/picoquic/picotls/picotlsvs/picotls-openssl/picotls-openssl.vcxproj.user @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/deps/picoquic/picotls/picotlsvs/picotls-openssl/targetver.h b/deps/picoquic/picotls/picotlsvs/picotls-openssl/targetver.h new file mode 100644 index 0000000..87c0086 --- /dev/null +++ b/deps/picoquic/picotls/picotlsvs/picotls-openssl/targetver.h @@ -0,0 +1,8 @@ +#pragma once + +// Including SDKDDKVer.h defines the highest available Windows platform. + +// If you wish to build your application for a previous Windows platform, include WinSDKVer.h and +// set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h. + +#include diff --git a/deps/picoquic/picotls/picotlsvs/picotls/ReadMe.txt b/deps/picoquic/picotls/picotlsvs/picotls/ReadMe.txt new file mode 100644 index 0000000..08baa57 --- /dev/null +++ b/deps/picoquic/picotls/picotlsvs/picotls/ReadMe.txt @@ -0,0 +1,37 @@ +======================================================================== + STATIC LIBRARY : picotls Project Overview +======================================================================== + +AppWizard has created this picotls library project for you. + +This file contains a summary of what you will find in each of the files that +make up your picotls application. + + +picotls.vcxproj + This is the main project file for VC++ projects generated using an Application Wizard. + It contains information about the version of Visual C++ that generated the file, and + information about the platforms, configurations, and project features selected with the + Application Wizard. + +picotls.vcxproj.filters + This is the filters file for VC++ projects generated using an Application Wizard. + It contains information about the association between the files in your project + and the filters. This association is used in the IDE to show grouping of files with + similar extensions under a specific node (for e.g. ".cpp" files are associated with the + "Source Files" filter). + + +///////////////////////////////////////////////////////////////////////////// + +StdAfx.h, StdAfx.cpp + These files are used to build a precompiled header (PCH) file + named picotls.pch and a precompiled types file named StdAfx.obj. + +///////////////////////////////////////////////////////////////////////////// +Other notes: + +AppWizard uses "TODO:" comments to indicate parts of the source code you +should add to or customize. + +///////////////////////////////////////////////////////////////////////////// diff --git a/deps/picoquic/picotls/picotlsvs/picotls/picotls.vcxproj b/deps/picoquic/picotls/picotlsvs/picotls/picotls.vcxproj new file mode 100644 index 0000000..f1b112c --- /dev/null +++ b/deps/picoquic/picotls/picotlsvs/picotls/picotls.vcxproj @@ -0,0 +1,177 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 15.0 + {46E6D6E9-7A30-4058-9661-DF70CC07E821} + Win32Proj + picotls + 10.0.14393.0 + + + + StaticLibrary + true + v141 + Unicode + + + StaticLibrary + false + v141 + true + Unicode + + + StaticLibrary + true + v141 + Unicode + + + StaticLibrary + false + v141 + true + Unicode + + + + + + + + + + + + + + + + + + + + + + + NotUsing + Level3 + Disabled + WIN32;_DEBUG;_LIB;_WINDOWS;%(PreprocessorDefinitions) + $(ProjectDir);$(ProjectDir)\..\..\picotls;$(ProjectDir)\..\..\include;$(ProjectDir)\..\..\deps\cifra\src;$(ProjectDir)\..\..\deps\cifra\src\ext;$(ProjectDir)\..\..\deps\micro-ecc;$(OPENSSLDIR)\include\;$(OPENSSLDIR)\;%(AdditionalIncludeDirectories) + + + Windows + + + + + NotUsing + Level3 + Disabled + _DEBUG;_LIB;_WINDOWS;%(PreprocessorDefinitions) + $(ProjectDir);$(ProjectDir)..\..\picotls;$(ProjectDir)..\..\include;$(ProjectDir)..\..\include\picotls;$(ProjectDir)..\..\deps\cifra\src;$(ProjectDir)..\..\deps\micro-ecc;$(OPENSSL64DIR)\include\;$(OPENSSL64DIR)\;%(AdditionalIncludeDirectories) + + + Windows + + + + + Level3 + NotUsing + MaxSpeed + true + true + WIN32;NDEBUG;_LIB;_WINDOWS;%(PreprocessorDefinitions) + $(ProjectDir);$(ProjectDir)\..\..\picotls;$(ProjectDir)\..\..\include;$(ProjectDir)\..\..\deps\cifra\src;$(ProjectDir)\..\..\deps\micro-ecc;$(OPENSSLDIR)\include\;$(OPENSSLDIR)\;%(AdditionalIncludeDirectories) + + + Windows + true + true + + + + + Level3 + NotUsing + MaxSpeed + true + true + NDEBUG;_LIB;_WINDOWS;%(PreprocessorDefinitions) + $(ProjectDir);$(ProjectDir)..\..\picotls;$(ProjectDir)..\..\include;$(ProjectDir)..\..\include\picotls;$(ProjectDir)..\..\deps\cifra\src;$(ProjectDir)..\..\deps\micro-ecc;$(OPENSSL64DIR)\include\;$(OPENSSL64DIR)\;%(AdditionalIncludeDirectories) + + + Windows + true + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/deps/picoquic/picotls/picotlsvs/picotls/picotls.vcxproj.filters b/deps/picoquic/picotls/picotlsvs/picotls/picotls.vcxproj.filters new file mode 100644 index 0000000..b9deca2 --- /dev/null +++ b/deps/picoquic/picotls/picotlsvs/picotls/picotls.vcxproj.filters @@ -0,0 +1,129 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + + \ No newline at end of file diff --git a/deps/picoquic/picotls/picotlsvs/picotls/picotls.vcxproj.user b/deps/picoquic/picotls/picotlsvs/picotls/picotls.vcxproj.user new file mode 100644 index 0000000..be25078 --- /dev/null +++ b/deps/picoquic/picotls/picotlsvs/picotls/picotls.vcxproj.user @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/deps/picoquic/picotls/picotlsvs/picotls/targetver.h b/deps/picoquic/picotls/picotlsvs/picotls/targetver.h new file mode 100644 index 0000000..87c0086 --- /dev/null +++ b/deps/picoquic/picotls/picotlsvs/picotls/targetver.h @@ -0,0 +1,8 @@ +#pragma once + +// Including SDKDDKVer.h defines the highest available Windows platform. + +// If you wish to build your application for a previous Windows platform, include WinSDKVer.h and +// set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h. + +#include diff --git a/deps/picoquic/picotls/picotlsvs/picotls/wincompat.h b/deps/picoquic/picotls/picotlsvs/picotls/wincompat.h new file mode 100644 index 0000000..8c9c084 --- /dev/null +++ b/deps/picoquic/picotls/picotlsvs/picotls/wincompat.h @@ -0,0 +1,37 @@ +#ifndef WINCOMPAT_H +#define WINCOMPAT_H + +#include +#define ssize_t int +#include +#include +#include + +#ifndef gettimeofday +#define gettimeofday wintimeofday + +#ifndef __attribute__ +#define __attribute__(X) +#endif + +#ifdef __cplusplus +extern "C" { +#endif +struct timezone { + int tz_minuteswest; /* minutes west of Greenwich */ + int tz_dsttime; /* type of DST correction */ +}; + +int wintimeofday(struct timeval *tv, struct timezone *tz); + +#ifndef strcasecmp +#define strcasecmp _stricmp +#endif + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif + +#endif /* WINCOMPAT_H */ \ No newline at end of file diff --git a/deps/picoquic/picotls/picotlsvs/picotls/wintimeofday.c b/deps/picoquic/picotls/picotlsvs/picotls/wintimeofday.c new file mode 100644 index 0000000..7dfaeac --- /dev/null +++ b/deps/picoquic/picotls/picotlsvs/picotls/wintimeofday.c @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2016 Christian Huitema + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifdef _WINDOWS +#include "wincompat.h" + + /* + * This is a simple replacement for the "gettimeofday" finction, + * which is not available on Windows + */ + +int wintimeofday(struct timeval* tv, struct timezone* tz) +{ + FILETIME ft; + uint64_t now = 0; + + /* + * The GetSystemTimeAsFileTime API returns the number + * of 100-nanosecond intervals since January 1, 1601 (UTC), + * in FILETIME format. + */ + GetSystemTimePreciseAsFileTime(&ft); + + /* + * Convert to plain 64 bit format, without making + * assumptions about the FILETIME structure alignment. + */ + now = ft.dwHighDateTime; + now <<= 32; + now |= ft.dwLowDateTime; + /* + * Convert units from 100ns to 1us + */ + now /= 10; + /* + * Account for microseconds elapsed between 1601 and 1970. + */ + now -= 11644473600000000ULL; + + if (tv != NULL) + { + uint64_t sec = now / 1000000; + uint64_t usec = now % 1000000; + + tv->tv_sec = (long)sec; + tv->tv_usec = (long)usec; + } + + if (tz != NULL) + { + /* + * TODO: implement a timezone retrieval function. + * Not urgent, since the GetDNS code always set this parameter to NULL. + */ + return -1; + } + + return 0; +} +#endif /* WIN32 */ \ No newline at end of file diff --git a/deps/picoquic/picotls/picotlsvs/picotlsvs.sln b/deps/picoquic/picotls/picotlsvs/picotlsvs.sln new file mode 100644 index 0000000..2b6a843 --- /dev/null +++ b/deps/picoquic/picotls/picotlsvs/picotlsvs.sln @@ -0,0 +1,174 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.29230.47 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "picotlsvs", "picotlsvs\picotlsvs.vcxproj", "{D0265367-FCCF-47A4-95FD-C33BECAB3486}" + ProjectSection(ProjectDependencies) = postProject + {559AC085-1BEF-450A-A62D-0D370561D596} = {559AC085-1BEF-450A-A62D-0D370561D596} + {499B82B3-F5A5-4C2E-91EF-A2F77CBC33F5} = {499B82B3-F5A5-4C2E-91EF-A2F77CBC33F5} + {56C264BF-822B-4F29-B512-5B26157CA2EC} = {56C264BF-822B-4F29-B512-5B26157CA2EC} + {497433FE-B252-4985-A504-54EB791F57F4} = {497433FE-B252-4985-A504-54EB791F57F4} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "testopenssl", "testopenssl\testopenssl.vcxproj", "{8750EE3B-9440-48BF-8D83-7274E94B06A7}" + ProjectSection(ProjectDependencies) = postProject + {559AC085-1BEF-450A-A62D-0D370561D596} = {559AC085-1BEF-450A-A62D-0D370561D596} + {499B82B3-F5A5-4C2E-91EF-A2F77CBC33F5} = {499B82B3-F5A5-4C2E-91EF-A2F77CBC33F5} + {56C264BF-822B-4F29-B512-5B26157CA2EC} = {56C264BF-822B-4F29-B512-5B26157CA2EC} + {497433FE-B252-4985-A504-54EB791F57F4} = {497433FE-B252-4985-A504-54EB791F57F4} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ptlsbench", "ptlsbench\ptlsbench.vcxproj", "{15D7D32F-3B62-4B10-A06A-BA1ADD591D7F}" + ProjectSection(ProjectDependencies) = postProject + {559AC085-1BEF-450A-A62D-0D370561D596} = {559AC085-1BEF-450A-A62D-0D370561D596} + {499B82B3-F5A5-4C2E-91EF-A2F77CBC33F5} = {499B82B3-F5A5-4C2E-91EF-A2F77CBC33F5} + {56C264BF-822B-4F29-B512-5B26157CA2EC} = {56C264BF-822B-4F29-B512-5B26157CA2EC} + {0A0E7AF2-05C8-488B-867C-D83B776B8BF4} = {0A0E7AF2-05C8-488B-867C-D83B776B8BF4} + {497433FE-B252-4985-A504-54EB791F57F4} = {497433FE-B252-4985-A504-54EB791F57F4} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "picotls-esni", "picotls-esni\picotls-esni.vcxproj", "{592127C5-DD8C-47ED-8EBA-026B5848C374}" + ProjectSection(ProjectDependencies) = postProject + {559AC085-1BEF-450A-A62D-0D370561D596} = {559AC085-1BEF-450A-A62D-0D370561D596} + {499B82B3-F5A5-4C2E-91EF-A2F77CBC33F5} = {499B82B3-F5A5-4C2E-91EF-A2F77CBC33F5} + {56C264BF-822B-4F29-B512-5B26157CA2EC} = {56C264BF-822B-4F29-B512-5B26157CA2EC} + {497433FE-B252-4985-A504-54EB791F57F4} = {497433FE-B252-4985-A504-54EB791F57F4} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "picotls-core", "picotls-core\picotls-core.vcxproj", "{497433FE-B252-4985-A504-54EB791F57F4}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "picotls-minicrypto", "picotls-minicrypto\picotls-minicrypto.vcxproj", "{559AC085-1BEF-450A-A62D-0D370561D596}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "picotls-openssl", "picotls-openssl\picotls-openssl.vcxproj", "{56C264BF-822B-4F29-B512-5B26157CA2EC}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "picotls-minicrypto-deps", "picotls-minicrypto-deps\picotls-minicrypto-deps.vcxproj", "{499B82B3-F5A5-4C2E-91EF-A2F77CBC33F5}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "picotls-bcrypt", "picotls-bcrypt\picotls-bcrypt.vcxproj", "{0A0E7AF2-05C8-488B-867C-D83B776B8BF4}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "bcrypt-test", "bcrypt-test\bcrypt-test.vcxproj", "{7A04A2BF-03BF-4C3A-9E44-A53B0E90036B}" + ProjectSection(ProjectDependencies) = postProject + {559AC085-1BEF-450A-A62D-0D370561D596} = {559AC085-1BEF-450A-A62D-0D370561D596} + {499B82B3-F5A5-4C2E-91EF-A2F77CBC33F5} = {499B82B3-F5A5-4C2E-91EF-A2F77CBC33F5} + {0A0E7AF2-05C8-488B-867C-D83B776B8BF4} = {0A0E7AF2-05C8-488B-867C-D83B776B8BF4} + {497433FE-B252-4985-A504-54EB791F57F4} = {497433FE-B252-4985-A504-54EB791F57F4} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "picotls-fusion", "picotls-fusion\picotls-fusion.vcxproj", "{55F22DE6-EAAE-4279-97B7-84FAAB7F29BB}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "fusiontest", "fusiontest\fusiontest.vcxproj", "{513DC1C9-2CD4-437C-B8EC-168924EB5AAC}" + ProjectSection(ProjectDependencies) = postProject + {559AC085-1BEF-450A-A62D-0D370561D596} = {559AC085-1BEF-450A-A62D-0D370561D596} + {499B82B3-F5A5-4C2E-91EF-A2F77CBC33F5} = {499B82B3-F5A5-4C2E-91EF-A2F77CBC33F5} + {55F22DE6-EAAE-4279-97B7-84FAAB7F29BB} = {55F22DE6-EAAE-4279-97B7-84FAAB7F29BB} + {497433FE-B252-4985-A504-54EB791F57F4} = {497433FE-B252-4985-A504-54EB791F57F4} + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {D0265367-FCCF-47A4-95FD-C33BECAB3486}.Debug|x64.ActiveCfg = Debug|x64 + {D0265367-FCCF-47A4-95FD-C33BECAB3486}.Debug|x64.Build.0 = Debug|x64 + {D0265367-FCCF-47A4-95FD-C33BECAB3486}.Debug|x86.ActiveCfg = Debug|Win32 + {D0265367-FCCF-47A4-95FD-C33BECAB3486}.Debug|x86.Build.0 = Debug|Win32 + {D0265367-FCCF-47A4-95FD-C33BECAB3486}.Release|x64.ActiveCfg = Release|x64 + {D0265367-FCCF-47A4-95FD-C33BECAB3486}.Release|x64.Build.0 = Release|x64 + {D0265367-FCCF-47A4-95FD-C33BECAB3486}.Release|x86.ActiveCfg = Release|Win32 + {D0265367-FCCF-47A4-95FD-C33BECAB3486}.Release|x86.Build.0 = Release|Win32 + {8750EE3B-9440-48BF-8D83-7274E94B06A7}.Debug|x64.ActiveCfg = Debug|x64 + {8750EE3B-9440-48BF-8D83-7274E94B06A7}.Debug|x64.Build.0 = Debug|x64 + {8750EE3B-9440-48BF-8D83-7274E94B06A7}.Debug|x86.ActiveCfg = Debug|Win32 + {8750EE3B-9440-48BF-8D83-7274E94B06A7}.Debug|x86.Build.0 = Debug|Win32 + {8750EE3B-9440-48BF-8D83-7274E94B06A7}.Release|x64.ActiveCfg = Release|x64 + {8750EE3B-9440-48BF-8D83-7274E94B06A7}.Release|x64.Build.0 = Release|x64 + {8750EE3B-9440-48BF-8D83-7274E94B06A7}.Release|x86.ActiveCfg = Release|Win32 + {8750EE3B-9440-48BF-8D83-7274E94B06A7}.Release|x86.Build.0 = Release|Win32 + {15D7D32F-3B62-4B10-A06A-BA1ADD591D7F}.Debug|x64.ActiveCfg = Debug|x64 + {15D7D32F-3B62-4B10-A06A-BA1ADD591D7F}.Debug|x64.Build.0 = Debug|x64 + {15D7D32F-3B62-4B10-A06A-BA1ADD591D7F}.Debug|x86.ActiveCfg = Debug|Win32 + {15D7D32F-3B62-4B10-A06A-BA1ADD591D7F}.Debug|x86.Build.0 = Debug|Win32 + {15D7D32F-3B62-4B10-A06A-BA1ADD591D7F}.Release|x64.ActiveCfg = Release|x64 + {15D7D32F-3B62-4B10-A06A-BA1ADD591D7F}.Release|x64.Build.0 = Release|x64 + {15D7D32F-3B62-4B10-A06A-BA1ADD591D7F}.Release|x86.ActiveCfg = Release|Win32 + {15D7D32F-3B62-4B10-A06A-BA1ADD591D7F}.Release|x86.Build.0 = Release|Win32 + {592127C5-DD8C-47ED-8EBA-026B5848C374}.Debug|x64.ActiveCfg = Debug|x64 + {592127C5-DD8C-47ED-8EBA-026B5848C374}.Debug|x64.Build.0 = Debug|x64 + {592127C5-DD8C-47ED-8EBA-026B5848C374}.Debug|x86.ActiveCfg = Debug|Win32 + {592127C5-DD8C-47ED-8EBA-026B5848C374}.Debug|x86.Build.0 = Debug|Win32 + {592127C5-DD8C-47ED-8EBA-026B5848C374}.Release|x64.ActiveCfg = Release|x64 + {592127C5-DD8C-47ED-8EBA-026B5848C374}.Release|x64.Build.0 = Release|x64 + {592127C5-DD8C-47ED-8EBA-026B5848C374}.Release|x86.ActiveCfg = Release|Win32 + {592127C5-DD8C-47ED-8EBA-026B5848C374}.Release|x86.Build.0 = Release|Win32 + {497433FE-B252-4985-A504-54EB791F57F4}.Debug|x64.ActiveCfg = Debug|x64 + {497433FE-B252-4985-A504-54EB791F57F4}.Debug|x64.Build.0 = Debug|x64 + {497433FE-B252-4985-A504-54EB791F57F4}.Debug|x86.ActiveCfg = Debug|Win32 + {497433FE-B252-4985-A504-54EB791F57F4}.Debug|x86.Build.0 = Debug|Win32 + {497433FE-B252-4985-A504-54EB791F57F4}.Release|x64.ActiveCfg = Release|x64 + {497433FE-B252-4985-A504-54EB791F57F4}.Release|x64.Build.0 = Release|x64 + {497433FE-B252-4985-A504-54EB791F57F4}.Release|x86.ActiveCfg = Release|Win32 + {497433FE-B252-4985-A504-54EB791F57F4}.Release|x86.Build.0 = Release|Win32 + {559AC085-1BEF-450A-A62D-0D370561D596}.Debug|x64.ActiveCfg = Debug|x64 + {559AC085-1BEF-450A-A62D-0D370561D596}.Debug|x64.Build.0 = Debug|x64 + {559AC085-1BEF-450A-A62D-0D370561D596}.Debug|x86.ActiveCfg = Debug|Win32 + {559AC085-1BEF-450A-A62D-0D370561D596}.Debug|x86.Build.0 = Debug|Win32 + {559AC085-1BEF-450A-A62D-0D370561D596}.Release|x64.ActiveCfg = Release|x64 + {559AC085-1BEF-450A-A62D-0D370561D596}.Release|x64.Build.0 = Release|x64 + {559AC085-1BEF-450A-A62D-0D370561D596}.Release|x86.ActiveCfg = Release|Win32 + {559AC085-1BEF-450A-A62D-0D370561D596}.Release|x86.Build.0 = Release|Win32 + {56C264BF-822B-4F29-B512-5B26157CA2EC}.Debug|x64.ActiveCfg = Debug|x64 + {56C264BF-822B-4F29-B512-5B26157CA2EC}.Debug|x64.Build.0 = Debug|x64 + {56C264BF-822B-4F29-B512-5B26157CA2EC}.Debug|x86.ActiveCfg = Debug|Win32 + {56C264BF-822B-4F29-B512-5B26157CA2EC}.Debug|x86.Build.0 = Debug|Win32 + {56C264BF-822B-4F29-B512-5B26157CA2EC}.Release|x64.ActiveCfg = Release|x64 + {56C264BF-822B-4F29-B512-5B26157CA2EC}.Release|x64.Build.0 = Release|x64 + {56C264BF-822B-4F29-B512-5B26157CA2EC}.Release|x86.ActiveCfg = Release|Win32 + {56C264BF-822B-4F29-B512-5B26157CA2EC}.Release|x86.Build.0 = Release|Win32 + {499B82B3-F5A5-4C2E-91EF-A2F77CBC33F5}.Debug|x64.ActiveCfg = Debug|x64 + {499B82B3-F5A5-4C2E-91EF-A2F77CBC33F5}.Debug|x64.Build.0 = Debug|x64 + {499B82B3-F5A5-4C2E-91EF-A2F77CBC33F5}.Debug|x86.ActiveCfg = Debug|Win32 + {499B82B3-F5A5-4C2E-91EF-A2F77CBC33F5}.Debug|x86.Build.0 = Debug|Win32 + {499B82B3-F5A5-4C2E-91EF-A2F77CBC33F5}.Release|x64.ActiveCfg = Release|x64 + {499B82B3-F5A5-4C2E-91EF-A2F77CBC33F5}.Release|x64.Build.0 = Release|x64 + {499B82B3-F5A5-4C2E-91EF-A2F77CBC33F5}.Release|x86.ActiveCfg = Release|Win32 + {499B82B3-F5A5-4C2E-91EF-A2F77CBC33F5}.Release|x86.Build.0 = Release|Win32 + {0A0E7AF2-05C8-488B-867C-D83B776B8BF4}.Debug|x64.ActiveCfg = Debug|x64 + {0A0E7AF2-05C8-488B-867C-D83B776B8BF4}.Debug|x64.Build.0 = Debug|x64 + {0A0E7AF2-05C8-488B-867C-D83B776B8BF4}.Debug|x86.ActiveCfg = Debug|Win32 + {0A0E7AF2-05C8-488B-867C-D83B776B8BF4}.Debug|x86.Build.0 = Debug|Win32 + {0A0E7AF2-05C8-488B-867C-D83B776B8BF4}.Release|x64.ActiveCfg = Release|x64 + {0A0E7AF2-05C8-488B-867C-D83B776B8BF4}.Release|x64.Build.0 = Release|x64 + {0A0E7AF2-05C8-488B-867C-D83B776B8BF4}.Release|x86.ActiveCfg = Release|Win32 + {0A0E7AF2-05C8-488B-867C-D83B776B8BF4}.Release|x86.Build.0 = Release|Win32 + {7A04A2BF-03BF-4C3A-9E44-A53B0E90036B}.Debug|x64.ActiveCfg = Debug|x64 + {7A04A2BF-03BF-4C3A-9E44-A53B0E90036B}.Debug|x64.Build.0 = Debug|x64 + {7A04A2BF-03BF-4C3A-9E44-A53B0E90036B}.Debug|x86.ActiveCfg = Debug|Win32 + {7A04A2BF-03BF-4C3A-9E44-A53B0E90036B}.Debug|x86.Build.0 = Debug|Win32 + {7A04A2BF-03BF-4C3A-9E44-A53B0E90036B}.Release|x64.ActiveCfg = Release|x64 + {7A04A2BF-03BF-4C3A-9E44-A53B0E90036B}.Release|x64.Build.0 = Release|x64 + {7A04A2BF-03BF-4C3A-9E44-A53B0E90036B}.Release|x86.ActiveCfg = Release|Win32 + {7A04A2BF-03BF-4C3A-9E44-A53B0E90036B}.Release|x86.Build.0 = Release|Win32 + {55F22DE6-EAAE-4279-97B7-84FAAB7F29BB}.Debug|x64.ActiveCfg = Debug|x64 + {55F22DE6-EAAE-4279-97B7-84FAAB7F29BB}.Debug|x64.Build.0 = Debug|x64 + {55F22DE6-EAAE-4279-97B7-84FAAB7F29BB}.Debug|x86.ActiveCfg = Debug|Win32 + {55F22DE6-EAAE-4279-97B7-84FAAB7F29BB}.Release|x64.ActiveCfg = Release|x64 + {55F22DE6-EAAE-4279-97B7-84FAAB7F29BB}.Release|x64.Build.0 = Release|x64 + {55F22DE6-EAAE-4279-97B7-84FAAB7F29BB}.Release|x86.ActiveCfg = Release|Win32 + {513DC1C9-2CD4-437C-B8EC-168924EB5AAC}.Debug|x64.ActiveCfg = Debug|x64 + {513DC1C9-2CD4-437C-B8EC-168924EB5AAC}.Debug|x64.Build.0 = Debug|x64 + {513DC1C9-2CD4-437C-B8EC-168924EB5AAC}.Debug|x86.ActiveCfg = Debug|Win32 + {513DC1C9-2CD4-437C-B8EC-168924EB5AAC}.Release|x64.ActiveCfg = Release|x64 + {513DC1C9-2CD4-437C-B8EC-168924EB5AAC}.Release|x64.Build.0 = Release|x64 + {513DC1C9-2CD4-437C-B8EC-168924EB5AAC}.Release|x86.ActiveCfg = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {97EE09B3-5753-4DE3-8683-3B2B378BCA03} + EndGlobalSection +EndGlobal diff --git a/deps/picoquic/picotls/picotlsvs/picotlsvs/ReadMe.txt b/deps/picoquic/picotls/picotlsvs/picotlsvs/ReadMe.txt new file mode 100644 index 0000000..b12bf06 --- /dev/null +++ b/deps/picoquic/picotls/picotlsvs/picotlsvs/ReadMe.txt @@ -0,0 +1,40 @@ +======================================================================== + CONSOLE APPLICATION : picotlsvs Project Overview +======================================================================== + +AppWizard has created this picotlsvs application for you. + +This file contains a summary of what you will find in each of the files that +make up your picotlsvs application. + + +picotlsvs.vcxproj + This is the main project file for VC++ projects generated using an Application Wizard. + It contains information about the version of Visual C++ that generated the file, and + information about the platforms, configurations, and project features selected with the + Application Wizard. + +picotlsvs.vcxproj.filters + This is the filters file for VC++ projects generated using an Application Wizard. + It contains information about the association between the files in your project + and the filters. This association is used in the IDE to show grouping of files with + similar extensions under a specific node (for e.g. ".cpp" files are associated with the + "Source Files" filter). + +picotlsvs.cpp + This is the main application source file. + +///////////////////////////////////////////////////////////////////////////// +Other standard files: + +StdAfx.h, StdAfx.cpp + These files are used to build a precompiled header (PCH) file + named picotlsvs.pch and a precompiled types file named StdAfx.obj. + +///////////////////////////////////////////////////////////////////////////// +Other notes: + +AppWizard uses "TODO:" comments to indicate parts of the source code you +should add to or customize. + +///////////////////////////////////////////////////////////////////////////// diff --git a/deps/picoquic/picotls/picotlsvs/picotlsvs/cert.pem b/deps/picoquic/picotls/picotlsvs/picotlsvs/cert.pem new file mode 100644 index 0000000..f94c873 --- /dev/null +++ b/deps/picoquic/picotls/picotlsvs/picotlsvs/cert.pem @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDKzCCAhOgAwIBAgIBADANBgkqhkiG9w0BAQsFADAaMRgwFgYDVQQDEw9waWNv +dGxzIHRlc3QgY2EwHhcNMTgwMjIzMDIzODEyWhcNMjgwMjIxMDIzODEyWjAbMRkw +FwYDVQQDExB0ZXN0LmV4YW1wbGUuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A +MIIBCgKCAQEA5soWzSG7iyawQlHM1yaX2dUAATUkhpbg2WPFOEem7E3zYzc6A/Z+ +bViFlfEgL37cbDUb4pnOAHrrsjGgkyBYh5i9iCTVfCk+H6SOHZJORO1Tq8X9C7Wc +NcshpSdm2Pa8hmv9hsHbLSeoPNeg8NkTPwMVaMZ2GpdmiyAmhzSZ2H9mzNI7ntPW +/XCchVf+ax2yt9haZ+mQE2NPYwHDjqCtdGkP5ZXXnYhJSBzSEhxfGckIiKDyOxiN +kLFLvUdT4ERSFBjauP2cSI0XoOUsiBxJNwHH310AU8jZbveSTcXGYgEuu2MIuDo7 +Vhkq5+TCqXsIFNbjy0taOoPRvUbPsbqFlQIDAQABo3sweTAJBgNVHRMEAjAAMCwG +CWCGSAGG+EIBDQQfFh1PcGVuU1NMIEdlbmVyYXRlZCBDZXJ0aWZpY2F0ZTAdBgNV +HQ4EFgQUE1vXDjBT8j2etP4brfHQ9DeKnpgwHwYDVR0jBBgwFoAUv3nKl7JgeCCW +qkZXnN+nsiP1JWMwDQYJKoZIhvcNAQELBQADggEBAKwARsxOCiGPXU1xhvs+pq9I +63mLi4rfnssOGzGnnAfuEaxggpozf3fOSgfyTaDbACdRPTZEStjQ5HMCcHvY7CH0 +8EYA+lkmFbuXXL8uHby1JBTzbTGf8pkRUsuF/Ie0SLChoDgt8oF3mY5pyU4HUaAw +Zp6HBpIRMdmbwGcwm25bl9MQYTrTX3dBfp3XPzfXbVwjJ7bsiTwAGq+dKwzwOQeM +2ZMZt4BQBoevsNopPrqG0S6kGUmJOIax0t13bKwDj21+Hp/O90HTFVCtAaDxRC56 +k0O8Q62ZxzjGJ7Zw6K3azXlH/BYE+CajxTUF+FKRRkkWL1GrFVUsYd9KLDAVry0= +-----END CERTIFICATE----- diff --git a/deps/picoquic/picotls/picotlsvs/picotlsvs/ec_cert.pem b/deps/picoquic/picotls/picotlsvs/picotlsvs/ec_cert.pem new file mode 100644 index 0000000..9e02cd3 --- /dev/null +++ b/deps/picoquic/picotls/picotlsvs/picotlsvs/ec_cert.pem @@ -0,0 +1,62 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 1 (0x1) + Signature Algorithm: sha256WithRSAEncryption + Issuer: CN=picotls test ca + Validity + Not Before: Feb 23 05:31:04 2018 GMT + Not After : Feb 21 05:31:04 2028 GMT + Subject: CN=test.example.com + Subject Public Key Info: + Public Key Algorithm: id-ecPublicKey + Public-Key: (256 bit) + pub: + 04:da:c8:a5:40:54:ba:33:da:18:a9:41:7f:49:53: + df:60:e6:a6:3d:b6:8e:53:3a:9f:dd:19:14:5e:ab: + 03:cf:bc:fb:36:98:16:24:8f:07:29:6d:15:d8:4f: + 30:e8:09:64:fb:14:fc:86:7c:d4:06:c2:fd:9d:e8: + 99:3f:48:8c:2b + ASN1 OID: prime256v1 + NIST CURVE: P-256 + X509v3 extensions: + X509v3 Basic Constraints: + CA:FALSE + Netscape Comment: + OpenSSL Generated Certificate + X509v3 Subject Key Identifier: + EE:30:86:16:A1:D2:69:AD:64:E4:D7:77:6B:B2:FD:5C:4F:01:A2:B5 + X509v3 Authority Key Identifier: + keyid:BF:79:CA:97:B2:60:78:20:96:AA:46:57:9C:DF:A7:B2:23:F5:25:63 + + Signature Algorithm: sha256WithRSAEncryption + 8f:ac:9c:01:6d:81:aa:8c:ae:5d:b5:16:74:ea:e8:eb:26:5b: + b1:66:d5:6b:d4:4d:79:0d:6d:87:a9:b6:bf:74:2d:c1:b2:2e: + 52:b6:4b:ca:0d:01:45:38:58:1a:d2:6a:6d:20:98:5a:51:b0: + 6f:2c:3f:0f:12:88:ed:7c:09:a5:74:00:21:3d:4b:d2:2d:54: + aa:53:8b:64:f9:1e:ea:a5:8a:e7:61:5e:56:92:52:36:3e:a0: + 68:59:9c:7d:b3:e8:5c:4b:77:6e:de:28:ed:18:91:a9:9c:39: + d2:96:cc:98:05:8c:74:dc:1e:12:5b:38:bd:56:cb:a3:e8:e1: + 2a:5a:2b:d2:32:45:c1:10:85:20:6c:6b:34:ea:66:91:0e:2e: + b8:64:87:9f:07:bc:23:4f:23:ad:be:89:df:0a:98:47:e9:63: + 02:d3:41:f4:2d:a4:ce:dd:e3:d8:41:08:fe:df:47:c0:e7:63: + 8e:1f:f0:4b:c5:ae:ab:c0:ba:38:3e:e3:90:9c:08:bd:75:1c: + b9:b8:54:43:1d:99:42:e0:a2:b7:75:bb:14:03:79:9a:f6:07: + d8:a5:ab:2b:3a:70:8b:77:85:70:8a:98:38:9b:35:09:f6:62: + 6b:29:4a:a7:a7:f9:3b:de:d8:c8:90:57:f2:76:2a:23:0b:01: + 68:c6:9a:f2 +-----BEGIN CERTIFICATE----- +MIICYDCCAUigAwIBAgIBATANBgkqhkiG9w0BAQsFADAaMRgwFgYDVQQDEw9waWNv +dGxzIHRlc3QgY2EwHhcNMTgwMjIzMDUzMTA0WhcNMjgwMjIxMDUzMTA0WjAbMRkw +FwYDVQQDExB0ZXN0LmV4YW1wbGUuY29tMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcD +QgAE2silQFS6M9oYqUF/SVPfYOamPbaOUzqf3RkUXqsDz7z7NpgWJI8HKW0V2E8w +6Alk+xT8hnzUBsL9neiZP0iMK6N7MHkwCQYDVR0TBAIwADAsBglghkgBhvhCAQ0E +HxYdT3BlblNTTCBHZW5lcmF0ZWQgQ2VydGlmaWNhdGUwHQYDVR0OBBYEFO4whhah +0mmtZOTXd2uy/VxPAaK1MB8GA1UdIwQYMBaAFL95ypeyYHgglqpGV5zfp7Ij9SVj +MA0GCSqGSIb3DQEBCwUAA4IBAQCPrJwBbYGqjK5dtRZ06ujrJluxZtVr1E15DW2H +qba/dC3Bsi5StkvKDQFFOFga0mptIJhaUbBvLD8PEojtfAmldAAhPUvSLVSqU4tk ++R7qpYrnYV5WklI2PqBoWZx9s+hcS3du3ijtGJGpnDnSlsyYBYx03B4SWzi9Vsuj +6OEqWivSMkXBEIUgbGs06maRDi64ZIefB7wjTyOtvonfCphH6WMC00H0LaTO3ePY +QQj+30fA52OOH/BLxa6rwLo4PuOQnAi9dRy5uFRDHZlC4KK3dbsUA3ma9gfYpasr +OnCLd4Vwipg4mzUJ9mJrKUqnp/k73tjIkFfydiojCwFoxpry +-----END CERTIFICATE----- diff --git a/deps/picoquic/picotls/picotlsvs/picotlsvs/ec_key.pem b/deps/picoquic/picotls/picotlsvs/picotlsvs/ec_key.pem new file mode 100644 index 0000000..906aaff --- /dev/null +++ b/deps/picoquic/picotls/picotlsvs/picotlsvs/ec_key.pem @@ -0,0 +1,5 @@ +-----BEGIN PRIVATE KEY----- +MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgwXS0+V7+egEOvkro +M7I2E/xl6WWRqDmemoD7q9H/ujqhRANCAATayKVAVLoz2hipQX9JU99g5qY9to5T +Op/dGRReqwPPvPs2mBYkjwcpbRXYTzDoCWT7FPyGfNQGwv2d6Jk/SIwr +-----END PRIVATE KEY----- diff --git a/deps/picoquic/picotls/picotlsvs/picotlsvs/key-test-1.pem b/deps/picoquic/picotls/picotlsvs/picotlsvs/key-test-1.pem new file mode 100644 index 0000000..1fb5f17 --- /dev/null +++ b/deps/picoquic/picotls/picotlsvs/picotlsvs/key-test-1.pem @@ -0,0 +1,5 @@ +-----BEGIN PRIVATE KEY----- +MIGEAgEAMBAGByqGSM49AgEGBSuBBAAKBG0wawIBAQQgVcB/UNPxalR9zDYAjQIf +jojUDiQuGnSJrFEEzZPT/92hRANCAASc7UJtgnF/abqWM60T3XNJEzBv5ez9TdwK +H0M6xpM2q+53wmsN/eYLdgtjgBd3DBmHtPilCkiFICXyaA8z9LkJ +-----END PRIVATE KEY----- diff --git a/deps/picoquic/picotls/picotlsvs/picotlsvs/key-test-2.pem b/deps/picoquic/picotls/picotlsvs/picotlsvs/key-test-2.pem new file mode 100644 index 0000000..f4aeb5e --- /dev/null +++ b/deps/picoquic/picotls/picotlsvs/picotlsvs/key-test-2.pem @@ -0,0 +1,9 @@ +-----BEGIN PRIVATE KEY----- +MIIBMAIBADCB0wYHKoZIzj0CATCBxwIBATAkBgcqhkjOPQEBAhkA//////////// +/////////v//////////MEsEGP////////////////////7//////////AQYIhI9 +wjlaBcqnQj2uzMlHYKfUYiVr1WkWAxUAxGloRDXes3jEtlypWR4qV2MFmi4EMQR9 +KXeBAMZaHaF4NxZYjc4ri0rujiKPGJY4qQ8iY3M3M0tJ3LZqbcj5l4rKdkipQ7AC +GQD///////////////96YtAxyD9ClPZA7BMCAQEEVTBTAgEBBBiKtwssqrxHY/gu +KDD4QgmyLDKaqBv2wEWhNAMyAAT5j6o+ojeB6jaFAfx4rtGf5hYbT1N6NnlAWiP1 ++bEWtTJiEVqnpeZN0m0SLybIGZY= +-----END PRIVATE KEY----- \ No newline at end of file diff --git a/deps/picoquic/picotls/picotlsvs/picotlsvs/key-test-3.pem b/deps/picoquic/picotls/picotlsvs/picotlsvs/key-test-3.pem new file mode 100644 index 0000000..08c9496 --- /dev/null +++ b/deps/picoquic/picotls/picotlsvs/picotlsvs/key-test-3.pem @@ -0,0 +1,5 @@ +-----BEGIN EC PRIVATE KEY----- +MHQCAQEEILNvumjK/H0ioec3gNnn07vM/yDUfwAlInWQYtTY+NpsoAcGBSuBBAAK +oUQDQgAEVw4JNyAuRNukW7+FFRLZW2Y8dNTAZ3SJp5AB+P+becKkbwtLLlO+PENe +7ZC02lGb4Sak2Wc2jBdsheCzfJOloQ== +-----END EC PRIVATE KEY----- diff --git a/deps/picoquic/picotls/picotlsvs/picotlsvs/key-test-4.pem b/deps/picoquic/picotls/picotlsvs/picotlsvs/key-test-4.pem new file mode 100644 index 0000000..4018db1 --- /dev/null +++ b/deps/picoquic/picotls/picotlsvs/picotlsvs/key-test-4.pem @@ -0,0 +1,5 @@ +-----BEGIN PRIVATE KEY----- +MIGEAgEAMBAGByqGSM49AgEGBSuBBAAKBG0wawIBAQQgocwnJPlQe7sRiRc0obuy +ddukwdOK73PgNnqZUIFm64WhRANCAAQ1FDasoi8+SmGUBtKW5brq9+gCiE/Ymz53 +BPUYN8AeHABM64AqWwX0oFCM027E4qRjGGjd7O+T+M/tXTJJR5p4 +-----END PRIVATE KEY----- diff --git a/deps/picoquic/picotls/picotlsvs/picotlsvs/key.pem b/deps/picoquic/picotls/picotlsvs/picotlsvs/key.pem new file mode 100644 index 0000000..b46db68 --- /dev/null +++ b/deps/picoquic/picotls/picotlsvs/picotlsvs/key.pem @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDmyhbNIbuLJrBC +UczXJpfZ1QABNSSGluDZY8U4R6bsTfNjNzoD9n5tWIWV8SAvftxsNRvimc4Aeuuy +MaCTIFiHmL2IJNV8KT4fpI4dkk5E7VOrxf0LtZw1yyGlJ2bY9ryGa/2GwdstJ6g8 +16Dw2RM/AxVoxnYal2aLICaHNJnYf2bM0jue09b9cJyFV/5rHbK32Fpn6ZATY09j +AcOOoK10aQ/lldediElIHNISHF8ZyQiIoPI7GI2QsUu9R1PgRFIUGNq4/ZxIjReg +5SyIHEk3AcffXQBTyNlu95JNxcZiAS67Ywi4OjtWGSrn5MKpewgU1uPLS1o6g9G9 +Rs+xuoWVAgMBAAECggEBAJZxS/XCNH/b43AH5LCnbrtH1u3yl3HIrp/nIquyQYSu +t6aIXKAysW1UFBiPCz0KxGMhJ6FKQ3gaqMQLB7KAllUl4v75i9SZCe8UlLOAKNdT +oYRK1s4oP8DtPmxrR+bMyE4T3TtX6SkBPfETWs1Fo/8iYnVfUaO559VvSs4+Ir91 +6EVBZ+xSQ6+H4nc9TvrByd7HHMmDGFXF19eo1rtQOxbNMiZSNwch+eFdzUr+hIqT +oeMiZCL+gTiWCglQIQNpRurSdc2szQSZNO85DlNU/kt2z2nxQMRrnl5mWUZdqbTM +qOEMq2RD/lR+fGwC/Zp1jly4f5c3QTF8oPzAhnmog20CgYEA9FtAZlmQ/8c5gDhh +n0dgBRDAVL0hmtDxjsL9RsvTVeWLXecpqUfFd0bpm/pi/QUIkoDc33jmUSh/2vCu +LCG5lvKsDVNwqiT+zsdHwGFyybYPNEPk6ILccr5fL7p8Fox5t9+oE+nct+P+VI82 +3TD/wN2soFPD9wDLjSDSfXMS7TsCgYEA8clXgh9AGJuUKoOHvsV9BFQ1HiFVtuXT +Qa+KKCLDnreeTuzF2s4nV51PJM5QEe0zfOABP2xjPYJRe+zIQ+rMwMA1Yxv17ZH/ +81w/poziTtZBGfCRmSnRY4gf3lTajUwcJhzmZ4AdV/ZQyX7rjFTZl9jeuMyY8uNY +y3SEpiw6a28CgYAKUhBWQlIte2yiTb9RyuHzVNHKwnI457pMHVA1PUafyiIoxSqt +S6q7bvNO8zRbG2tRRMAPcDvKEbvUs3Wnx4TfK0C5D10i0o0wjpopNfRzMI1T18pD +R8On1QKQMYAsM6KwcXHX5Xi9C5QiXiojDX6/1p0D6IXOWOo/+7LoOYQDIQKBgDJY +KB5x/1igXHOVu5gfau6R0hWZ/0z8Acb1lCDTTEQqG453gqMSteJqYOZbBxUUfNoN +knTwTqGqFulk3jY2F7gyzWr7kXOMKO01UhON1jlwJ1INY2Ou72h4GZqjtHYjWOEe +t2LprDJ6mUu7X7RynnQdthJol5hLeluywUQQhYGFAoGAbfGV4PhKD2sbY5ZDPkiu +8sWD8fLthbK6yFV7PrWRl4sOe32Iza6bvqT/EU0hbRr54vZXFFMJtev3PzqGNz/o +J5IkcpgVCXjOxIBmRognT4duuEM0EBiH1vaZF1f44x1ntnkncaW3wQ0VGp7xGURI +ykArGNH50gPRuPACNWvYoKE= +-----END PRIVATE KEY----- diff --git a/deps/picoquic/picotls/picotlsvs/picotlsvs/myec1.pem b/deps/picoquic/picotls/picotlsvs/picotlsvs/myec1.pem new file mode 100644 index 0000000..8a19bb0 --- /dev/null +++ b/deps/picoquic/picotls/picotlsvs/picotlsvs/myec1.pem @@ -0,0 +1,5 @@ +-----BEGIN EC PRIVATE KEY----- +MHQCAQEEIKHMJyT5UHu7EYkXNKG7snXbpMHTiu9z4DZ6mVCBZuuFoAcGBSuBBAAK +oUQDQgAENRQ2rKIvPkphlAbSluW66vfoAohP2Js+dwT1GDfAHhwATOuAKlsF9KBQ +jNNuxOKkYxho3ezvk/jP7V0ySUeaeA== +-----END EC PRIVATE KEY----- diff --git a/deps/picoquic/picotls/picotlsvs/picotlsvs/openssl.cnf b/deps/picoquic/picotls/picotlsvs/picotlsvs/openssl.cnf new file mode 100644 index 0000000..c96e843 --- /dev/null +++ b/deps/picoquic/picotls/picotlsvs/picotlsvs/openssl.cnf @@ -0,0 +1,35 @@ +[ req ] +default_bits = 1024 +default_keyfile= privkey.pem +distinguished_name = req_distinguished_name + + +[ req_distinguished_name ] +countryName= US +countryName_min= 2 +countryName_max = 2 +stateOrProvinceName= Washington +localityName = Seattle +organizationName = Example +commonName = Localhost +commonName_max = 64 +emailAddress = localhost@example.com +emailAddress_max = 40 + + +[ alternate_names ] + +DNS.1 = example.com +DNS.2 = www.example.com +DNS.3 = mail.example.com +DNS.4 = ftp.example.com + +# Add these if you need them. But usually you don't want them or +# need them in production. You may need them for development. +# DNS.5 = localhost +# DNS.6 = localhost.localdomain +# DNS.7 = 127.0.0.1 + +# IPv6 localhost +# DNS.8 = ::1 + diff --git a/deps/picoquic/picotls/picotlsvs/picotlsvs/picotlsvs.c b/deps/picoquic/picotls/picotlsvs/picotlsvs/picotlsvs.c new file mode 100644 index 0000000..59de800 --- /dev/null +++ b/deps/picoquic/picotls/picotlsvs/picotlsvs/picotlsvs.c @@ -0,0 +1,699 @@ +/* picotlsvs: test program for the TLS 1.3 library. */ +#include +#include +#include +#include "../picotls/wincompat.h" +#include "../../include/picotls.h" +#include "../../include/picotls/openssl.h" +#include "../../include/picotls/minicrypto.h" +#include "../../include/picotls/asn1.h" +#include "../../include/picotls/pembase64.h" + +#ifdef _WINDOWS +#define PICOTLS_CERT_STORE "..\\..\\t\\assets\\test-ca.crt" +#else +#define PICOTLS_CERT_STORE "../../t/assets/test-ca.crt" +#endif + +void log_printf(void * ctx, const char * format, ...) +{ + va_list argptr; + va_start(argptr, format); + vfprintf(stderr, format, argptr); +} + +ptls_minicrypto_log_ctx_t log_ctx = { NULL, log_printf }; + +int ptls_export_secret(ptls_t *tls, void *output, size_t outlen, const char *label, ptls_iovec_t context_value, int is_early); + +/* + * Testing the Base64 and ASN1 verifiers. + * Start by loading the private key object, then do a mini fuzz test. + * The goal is to verify that the decoding returns something correct, + * even in presence of errors. + */ + +size_t ptls_minicrypto_asn1_decode_private_key( + ptls_asn1_pkcs8_private_key_t * pkey, + int * decode_error, ptls_minicrypto_log_ctx_t * log_ctx); + +int openPemTest(char const * filename) +{ + ptls_iovec_t buf = { 0 }; + size_t count = 1; + size_t fuzz_index = 0; + uint8_t original_byte = 0; + uint8_t fuzz_byte = 0xAA; + size_t byte_index = 0; + int decode_error; + + int ret = ptls_load_pem_objects(filename, "PRIVATE KEY", &buf, 1, &count); + + + if (ret == 0) + { + for (fuzz_index = 0; ret == 0 && fuzz_index < buf.len; fuzz_index++) + { + ptls_asn1_pkcs8_private_key_t pkey = { {0} }; + original_byte = buf.base[fuzz_index]; + decode_error = 0; + buf.base[fuzz_index] ^= fuzz_byte; + + pkey.vec.base = buf.base; + pkey.vec.len = buf.len; + + byte_index = ptls_minicrypto_asn1_decode_private_key( + &pkey, &decode_error, NULL); + + if (decode_error != 0) + { + if (decode_error == 1) + { + ret = -1; + } + } + + buf.base[fuzz_index] = original_byte; + } + } + + if (buf.base != NULL) + { + free(buf.base); + } + + return ret; +} + +/* + * Using the open ssl library to load the test certificate + */ + +X509* openPemFile(char* filename) +{ + + X509* cert = X509_new(); + BIO* bio_cert = BIO_new_file(filename, "rb"); + PEM_read_bio_X509(bio_cert, &cert, NULL, NULL); + return cert; +} + +int get_certificates(char * pem_fname, ptls_iovec_t ** list, int * nb_certs) +{ + int ret = 0; + size_t count = 0; + X509 *cert; + static ptls_iovec_t certs[16]; + + *nb_certs = 0; + *list = NULL; + + cert = openPemFile(pem_fname); + + if (cert == NULL) + { + fprintf(stderr, "Could not read cert in %s\n", pem_fname); + ret = -1; + } + else + { + ptls_iovec_t *dst = certs + count++; + dst->len = i2d_X509(cert, &dst->base); + } + + *nb_certs = (int) count; + *list = certs; + + return ret; +} + +void SetSignCertificate(char const * keypem, ptls_context_t * ctx) +{ + static ptls_openssl_sign_certificate_t signer; + + EVP_PKEY *pkey = EVP_PKEY_new(); + BIO* bio_key = BIO_new_file(keypem, "rb"); + PEM_read_bio_PrivateKey(bio_key, &pkey, NULL, NULL); + ptls_openssl_init_sign_certificate(&signer, pkey); + EVP_PKEY_free(pkey); + ctx->sign_certificate = &signer.super; +} + +int handshake_init(ptls_t * tls, ptls_buffer_t * sendbuf, ptls_handshake_properties_t * ph_prop) +{ + size_t inlen = 0, roff = 0; + + ptls_buffer_init(sendbuf, "", 0); + int ret = ptls_handshake(tls, sendbuf, NULL, NULL, ph_prop); + + return ret; +} + + +int handshake_progress(ptls_t * tls, ptls_buffer_t * sendbuf, ptls_buffer_t * recvbuf, ptls_handshake_properties_t * ph_prop) +{ + size_t inlen = 0, roff = 0; + int ret = 0; + + ptls_buffer_init(sendbuf, "", 0); + + /* Provide the data */ + while (roff < recvbuf->off && (ret == 0 || ret == PTLS_ERROR_IN_PROGRESS)) + { + inlen = recvbuf->off - roff; + ret = ptls_handshake(tls, sendbuf, recvbuf->base + roff, &inlen, ph_prop); + roff += inlen; + } + + if (roff < recvbuf->off) + { + // Could not consume all the data. This is bad. + fprintf(stderr, "Could only process %d bytes out of %d\n", (int) roff, (int) recvbuf->off); + } + ptls_buffer_dispose(recvbuf); + + return ret; +} + +/* + Verify the secret extraction functionality + at the end of the handshake. + */ + +int extract_1rtt_secret( + ptls_t *tls, const char *label, + ptls_cipher_suite_t ** cipher, + uint8_t * secret, size_t secret_max) +{ + int ret = 0; + *cipher = ptls_get_cipher(tls); + + if (*cipher == NULL) + { + ret = -1; + } + else if ((*cipher)->hash->digest_size > secret_max) + { + ret = -1; + } + else + { + ret = ptls_export_secret(tls, secret, (*cipher)->hash->digest_size, + label, ptls_iovec_init(NULL, 0), 1); + } + + return 0; +} + +int verify_1rtt_secret_extraction(ptls_t *tls_client, ptls_t *tls_server) +{ + int ret = 0; + ptls_cipher_suite_t * cipher_client; + ptls_cipher_suite_t * cipher_server; + uint8_t secret_client[64]; + uint8_t secret_server[64]; + char const * label = "This is just a test"; + + ret = extract_1rtt_secret(tls_client, label, &cipher_client, + secret_client, sizeof(secret_client)); + + if (ret != 0) + { + fprintf(stderr, "Cannot extract client 1RTT secret, ret=%d\n", ret); + } + else + { + ret = extract_1rtt_secret(tls_server, label, &cipher_server, + secret_server, sizeof(secret_server)); + if (ret != 0) + { + fprintf(stderr, "Cannot extract client 1RTT secret, ret=%d\n", ret); + } + } + + if (ret == 0) + { + if (strcmp(cipher_client->aead->name, cipher_server->aead->name) != 0) + { + fprintf(stderr, "AEAD differ, client:%s, server:%s\n", + cipher_client->aead->name, cipher_server->aead->name); + ret = -1; + } + else if (cipher_client->hash->digest_size != cipher_server->hash->digest_size) + { + fprintf(stderr, "Key length differ, client:%d, server:%d\n", + (int) cipher_client->hash->digest_size, (int) cipher_server->hash->digest_size); + ret = -1; + } + else if (memcmp(secret_client, secret_server, cipher_client->hash->digest_size) != 0) + { + fprintf(stderr, "Key of client and server differ!\n"); + ret = -1; + } + } + + return ret; +} + +X509_STORE * openssl_init_cert_store(char const * crt_file) +{ + int ret = 0; + X509_STORE *store = X509_STORE_new(); + + if (store != NULL) { + X509_LOOKUP *lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file()); + ret = X509_LOOKUP_load_file(lookup, crt_file, X509_FILETYPE_PEM); + if (ret != 1) { + fprintf(stderr, "Cannot load store (%s), ret = %d\n", + crt_file, ret); + } + } + + return store; +} + + +int openssl_init_test_client(ptls_context_t *ctx_client, char const * crt_file) +{ + int ret = 0; + static ptls_openssl_verify_certificate_t verifier; + X509_STORE *store = NULL; + + /* Initialize the client context */ + memset(ctx_client, 0, sizeof(ptls_context_t)); + ctx_client->random_bytes = ptls_openssl_random_bytes; + ctx_client->get_time = &ptls_get_time; + ctx_client->key_exchanges = ptls_openssl_key_exchanges; + ctx_client->cipher_suites = ptls_openssl_cipher_suites; + ptls_openssl_init_verify_certificate(&verifier, openssl_init_cert_store(crt_file)); + ctx_client->verify_certificate = &verifier.super; + + return ret; +} + +int openssl_init_test_server(ptls_context_t *ctx_server, + char const * key_file, char const * cert_file) +{ + int ret = 0; + /* Initialize the server context */ + memset(ctx_server, 0, sizeof(ptls_context_t)); + ctx_server->random_bytes = ptls_openssl_random_bytes; + ctx_server->get_time = &ptls_get_time; + ctx_server->key_exchanges = ptls_openssl_key_exchanges; + ctx_server->cipher_suites = ptls_openssl_cipher_suites; + + ret = ptls_load_certificates(ctx_server, cert_file); + if (ret != 0) + { + fprintf(stderr, "Could not read the server certificates\n"); + } + else + { + SetSignCertificate(key_file, ctx_server); + } + + return ret; +} + +int minicrypto_init_test_client(ptls_context_t *ctx_client) +{ + int ret = 0; + /* Initialize the client context */ + memset(ctx_client, 0, sizeof(ptls_context_t)); + ctx_client->random_bytes = ptls_minicrypto_random_bytes; + ctx_client->get_time = &ptls_get_time; + ctx_client->key_exchanges = ptls_minicrypto_key_exchanges; + ctx_client->cipher_suites = ptls_minicrypto_cipher_suites; + ctx_client->verify_certificate = NULL; // &verifier.super; + + return ret; +} + +int minicrypto_init_test_server(ptls_context_t *ctx_server, char const * key_file, char const * cert_file) +{ + int ret = 0; + + /* Initialize the server context */ + memset(ctx_server, 0, sizeof(ptls_context_t)); + ctx_server->random_bytes = ptls_minicrypto_random_bytes; + ctx_server->get_time = &ptls_get_time; + ctx_server->key_exchanges = ptls_minicrypto_key_exchanges; + ctx_server->cipher_suites = ptls_minicrypto_cipher_suites; + + ret = ptls_load_certificates(ctx_server, cert_file); + + if (ret != 0) + { + fprintf(stderr, "Could not read the server certificates\n"); + } + else + { + ret = ptls_minicrypto_load_private_key(ctx_server, key_file); + } + + return ret; +} + +#define PICOTLS_VS_TEST_EXTENSION 1234 +static uint8_t testExtensionClient[] = { 1, 2, 3 }; +static uint8_t testExtensionServer[] = { 1, 2, 3, 4, 5, 6, 7, 8 }; +char const test_sni[] = "test.example.com"; +char const test_alpn[] = "picotls"; +static const ptls_iovec_t proposed_alpn[] = { + { (uint8_t *) "grease", 6}, + { (uint8_t *)test_alpn, sizeof(test_alpn) -1 } +}; + + +struct st_picotls_vs_test_context_t +{ + int client_mode; + size_t received_extension_length; + uint8_t received_extension[16]; + ptls_raw_extension_t ext[2]; + + ptls_handshake_properties_t handshake_properties; + +}; + +int collect_test_extension(ptls_t *tls, struct st_ptls_handshake_properties_t *properties, uint16_t type) +{ + return type == PICOTLS_VS_TEST_EXTENSION; +} + +void set_test_extensions(ptls_raw_extension_t ext[2], uint8_t * data, size_t len) +{ + ext[0].type = PICOTLS_VS_TEST_EXTENSION; + ext[0].data.base = data; + ext[0].data.len = len; + ext[1].type = 0xFFFF; + ext[1].data.base = NULL; + ext[1].data.len = 0; +} + +int collected_test_extensions(ptls_t *tls, ptls_handshake_properties_t *properties, + ptls_raw_extension_t *slots) +{ + struct st_picotls_vs_test_context_t * ctx = (struct st_picotls_vs_test_context_t *) + ((char *)properties - offsetof(struct st_picotls_vs_test_context_t, handshake_properties)); + + if (slots[0].type == PICOTLS_VS_TEST_EXTENSION && slots[1].type == 0xFFFF) + { + ctx->received_extension_length = slots[0].data.len; + memcpy(ctx->received_extension, slots[0].data.base, + (slots[0].data.len < sizeof(ctx->received_extension)) ? + slots[0].data.len : sizeof(ctx->received_extension)); + + if (ctx->client_mode == 0) + { + properties->additional_extensions = ctx->ext; + set_test_extensions(ctx->ext, testExtensionServer, sizeof(testExtensionServer)); + } + } + + return 0; +} + +int client_hello_call_back(ptls_on_client_hello_t *on_hello_cb_ctx, ptls_t *tls, + ptls_on_client_hello_parameters_t *params) +{ + /* Save the server name */ + ptls_set_server_name(tls, (const char *)params->server_name.base, params->server_name.len); + /* Check the ALPN */ + for (size_t i = 0; i < params->negotiated_protocols.count; i++) + { + if (params->negotiated_protocols.list[i].len == sizeof(test_alpn) - 1 && + memcmp(params->negotiated_protocols.list[i].base, test_alpn, sizeof(test_alpn) - 1) == 0) + { + ptls_set_negotiated_protocol(tls, test_alpn, sizeof(test_alpn) - 1); + break; + } + } + return 0; +} + +void set_handshake_context(struct st_picotls_vs_test_context_t * ctx, int client_mode) +{ + memset(ctx, 0, sizeof(struct st_picotls_vs_test_context_t)); + + if ((ctx->client_mode = client_mode) != 0) + { + ctx->handshake_properties.client.negotiated_protocols.list = proposed_alpn; + ctx->handshake_properties.client.negotiated_protocols.count = + sizeof(proposed_alpn) / sizeof(ptls_iovec_t); + + ctx->handshake_properties.additional_extensions = ctx->ext; + set_test_extensions(ctx->ext, testExtensionClient, sizeof(testExtensionClient)); + } + + ctx->handshake_properties.collect_extension = collect_test_extension; + ctx->handshake_properties.collected_extensions = collected_test_extensions; +} + +int verify_handshake_extension(struct st_picotls_vs_test_context_t * app_ctx_client, + struct st_picotls_vs_test_context_t *app_ctx_server) +{ + int ret = 0; + + if (app_ctx_server->received_extension_length == 0) + { + fprintf(stderr, "Server did not receive the client extension.\n"); + ret = -1; + } + else if (app_ctx_server->received_extension_length != sizeof(testExtensionClient) || + memcmp(app_ctx_server->received_extension, testExtensionClient, sizeof(testExtensionClient))) + { + fprintf(stderr, "Server did not correctly receive the client extension.\n"); + ret = -1; + } + else if (app_ctx_client->received_extension_length == 0) + { + fprintf(stderr, "Client did not receive the server extension.\n"); + ret = -1; + } + else if (app_ctx_client->received_extension_length != sizeof(testExtensionServer) || + memcmp(app_ctx_client->received_extension, testExtensionServer, sizeof(testExtensionServer))) + { + fprintf(stderr, "Client did not correctly receive the server extension.\n"); + ret = -1; + } + + return ret; +} + +int ptls_memory_loopback_test(int openssl_client, int openssl_server, char const * key_file, char const * cert_file) +{ + ptls_context_t ctx_client, ctx_server; + ptls_t *tls_client = NULL, *tls_server = NULL; + int ret = 0; + ptls_buffer_t client_buf, server_buf; + struct st_picotls_vs_test_context_t app_ctx_client, app_ctx_server; + ptls_on_client_hello_t client_hello_cb; + + + /* init the contexts */ + if (ret == 0 && openssl_client) + { + ret = openssl_init_test_client(&ctx_client, PICOTLS_CERT_STORE); + } + else + { + ret = minicrypto_init_test_client(&ctx_client); + } + + if (ret == 0 && openssl_server) + { + ret = openssl_init_test_server(&ctx_server, key_file, cert_file); + } + else + { + ret = minicrypto_init_test_server(&ctx_server, key_file, cert_file); + } + + /* Create the connections */ + if (ret == 0) + { + tls_client = ptls_new(&ctx_client, 0); + tls_server = ptls_new(&ctx_server, 1); + + if (tls_server == NULL || tls_client == NULL) + { + fprintf(stderr, "Could not create the TLS connection objects\n"); + ret = -1; + } + } + + /* Perform the handshake */ + if (ret == 0) + { + int nb_rounds = 0; + + set_handshake_context(&app_ctx_client, 1); + set_handshake_context(&app_ctx_server, 0); + + client_hello_cb.cb = client_hello_call_back; + ctx_server.on_client_hello = &client_hello_cb; + + ptls_set_server_name(tls_client, test_sni, sizeof(test_sni) - 1); + + ret = handshake_init(tls_client, &client_buf, + &app_ctx_client.handshake_properties); + printf("First message from client, ret = %d, %d bytes.\n", ret, (int) client_buf.off); + + while ((ret == 0 || ret == PTLS_ERROR_IN_PROGRESS) && client_buf.off > 0 && nb_rounds < 12) + { + nb_rounds++; + + ret = handshake_progress(tls_server, &server_buf, &client_buf, + &app_ctx_server.handshake_properties); + app_ctx_server.handshake_properties.additional_extensions = NULL; + + printf("Message from server, ret = %d, %d bytes.\n", ret, (int) server_buf.off); + + if ((ret == 0 || ret == PTLS_ERROR_IN_PROGRESS) && server_buf.off > 0) + { + app_ctx_client.handshake_properties.additional_extensions = NULL; + + ret = handshake_progress(tls_client, &client_buf, &server_buf, + &app_ctx_client.handshake_properties); + + printf("Message from client, ret = %d, %d bytes.\n", ret, (int) client_buf.off); + } + } + + printf("Exit handshake after %d rounds, ret = %d.\n", nb_rounds, ret); + + if (ret == 0) + { + ret = verify_1rtt_secret_extraction(tls_client, tls_server); + + if (ret == 0) + { + printf("Key extracted and matches!\n"); + } + } + + if (ret == 0) + { + ret = verify_handshake_extension(&app_ctx_client, &app_ctx_server); + + if (ret == 0) + { + printf("Extensions received and match!\n"); + } + } + + if (ret == 0) + { + const char * sni_received = ptls_get_server_name(tls_server); + + if (sni_received == NULL) + { + fprintf(stderr, "Server did not receive the SNI set by the client\n"); + ret = -1; + } + else if (strcmp(sni_received, test_sni) != 0) + { + fprintf(stderr, "Server receives SNI: <%s>, does not match <%s>\n", + sni_received, test_sni); + ret = -1; + } + } + + if (ret == 0) + { + const char * alpn_received = ptls_get_negotiated_protocol(tls_server); + + if (alpn_received == NULL) + { + fprintf(stderr, "Server did not negotiate ALPN\n"); + ret = -1; + } + else if (strcmp(alpn_received, test_alpn) != 0) + { + fprintf(stderr, "Server receives ALPN: <%s>, does not match <%s>\n", + alpn_received, test_alpn); + ret = -1; + } + } + + if (ret == 0) + { + printf("SNI and ALPN match.\n"); + } + } + + if (tls_client != NULL) + { + ptls_free(tls_client); + } + + if (tls_server != NULL) + { + ptls_free(tls_server); + } + + if (openssl_server == 0 && ctx_server.sign_certificate != NULL) + { + free(ctx_server.sign_certificate); + } + + return ret; +} + +static char const * test_keys[] = { + "key.pem", + "ec_key.pem", + "key-test-1.pem", + "key-test-2.pem", + "key-test-4.pem" +}; + +static const size_t nb_test_keys = sizeof(test_keys) / sizeof(char const *); + +int main() +{ + int ret = 0; + +#if 1 + /* TODO: move to ASN.1 unit test*/ + + for (size_t i = 0; ret == 0 && i < nb_test_keys; i++) + { + ret = openPemTest(test_keys[i]); + } +#endif + + if (ret == 0) + { + printf("\nStarting the RSA test with OpenSSL\n"); + ret = ptls_memory_loopback_test(1, 1, "key.pem", "cert.pem"); + } + + if (ret == 0) + { + printf("\nStarting the P256R1 test with OpenSSL\n"); + ret = ptls_memory_loopback_test(1, 1, "ec_key.pem", "ec_cert.pem"); + } + + if (ret == 0) + { + printf("\nStarting the P256R1 test with OpenSSL server and Minicrypto client\n"); + ret = ptls_memory_loopback_test(0, 1, "ec_key.pem", "ec_cert.pem"); + } + + if (ret == 0) + { + printf("\nStarting the P256R1 test with Minicrypto\n"); + ret = ptls_memory_loopback_test(0, 0, "ec_key.pem", "ec_cert.pem"); + } + + if (ret == 0) + { + printf("\nStarting the P256R1 test with Minicrypto server and OpenSSL client\n"); + ret = ptls_memory_loopback_test(1, 0, "ec_key.pem", "ec_cert.pem"); + } + + return ret; +} + diff --git a/deps/picoquic/picotls/picotlsvs/picotlsvs/picotlsvs.vcxproj b/deps/picoquic/picotls/picotlsvs/picotlsvs/picotlsvs.vcxproj new file mode 100644 index 0000000..35810aa --- /dev/null +++ b/deps/picoquic/picotls/picotlsvs/picotlsvs/picotlsvs.vcxproj @@ -0,0 +1,161 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 15.0 + {D0265367-FCCF-47A4-95FD-C33BECAB3486} + Win32Proj + picotlsvs + 10.0.17763.0 + + + + Application + true + v141 + Unicode + + + Application + false + v141 + true + Unicode + + + Application + true + v141 + Unicode + + + Application + false + v141 + true + Unicode + + + + + + + + + + + + + + + + + + + + + true + + + true + + + false + + + false + + + + NotUsing + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;_WINDOWS;%(PreprocessorDefinitions) + $(OPENSSLDIR)\include;$(ProjectDir);$(ProjectDir)..\picotls;$(ProjectDir)..\..\include;%(AdditionalIncludeDirectories) + + + Console + $(OPENSSLDIR);$(OutDir) + picotls-core.lib;picotls-openssl.lib;picotls-minicrypto.lib;picotls-minicrypto-deps.lib;libcrypto.lib;bcrypt.lib;ws2_32.lib;%(AdditionalDependencies) + + + + + NotUsing + Level3 + Disabled + _DEBUG;_CONSOLE;_WINDOWS;%(PreprocessorDefinitions) + $(ProjectDir);$(ProjectDir)..\picotls;$(ProjectDir)..\..\include;$(OPENSSL64DIR)\include;%(AdditionalIncludeDirectories) + + + Console + $(OPENSSL64DIR);$(OPENSSL64DIR)\lib;$(OutDir);%(AdditionalLibraryDirectories) + picotls-core.lib;picotls-openssl.lib;picotls-minicrypto.lib;picotls-minicrypto-deps.lib;libcrypto.lib;bcrypt.lib;ws2_32.lib;%(AdditionalDependencies) + + + + + Level3 + NotUsing + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;_WINDOWS;%(PreprocessorDefinitions) + $(OPENSSLDIR)\include;$(ProjectDir);$(ProjectDir)..\picotls;$(ProjectDir)..\..\include;%(AdditionalIncludeDirectories) + + + Console + true + true + $(OPENSSLDIR);$(OutDir) + picotls-core.lib;picotls-openssl.lib;picotls-minicrypto.lib;picotls-minicrypto-deps.lib;libcrypto.lib;bcrypt.lib;ws2_32.lib;%(AdditionalDependencies) + + + + + Level3 + NotUsing + MaxSpeed + true + true + NDEBUG;_CONSOLE;_WINDOWS;%(PreprocessorDefinitions) + $(ProjectDir);$(ProjectDir)..\picotls;$(ProjectDir)..\..\include;$(OPENSSL64DIR)\include;%(AdditionalIncludeDirectories) + + + Console + true + true + $(OPENSSL64DIR);$(OPENSSL64DIR)\lib;$(OutDir);%(AdditionalLibraryDirectories) + picotls-core.lib;picotls-openssl.lib;picotls-minicrypto.lib;picotls-minicrypto-deps.lib;libcrypto.lib;bcrypt.lib;ws2_32.lib;%(AdditionalDependencies) + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/deps/picoquic/picotls/picotlsvs/picotlsvs/picotlsvs.vcxproj.filters b/deps/picoquic/picotls/picotlsvs/picotlsvs/picotlsvs.vcxproj.filters new file mode 100644 index 0000000..a52d6d4 --- /dev/null +++ b/deps/picoquic/picotls/picotlsvs/picotlsvs/picotlsvs.vcxproj.filters @@ -0,0 +1,30 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + + + + Header Files + + + + + Source Files + + + \ No newline at end of file diff --git a/deps/picoquic/picotls/picotlsvs/picotlsvs/picotlsvs.vcxproj.user b/deps/picoquic/picotls/picotlsvs/picotlsvs/picotlsvs.vcxproj.user new file mode 100644 index 0000000..f853d7f --- /dev/null +++ b/deps/picoquic/picotls/picotlsvs/picotlsvs/picotlsvs.vcxproj.user @@ -0,0 +1,7 @@ + + + + $(ProjectDir) + WindowsLocalDebugger + + \ No newline at end of file diff --git a/deps/picoquic/picotls/picotlsvs/picotlsvs/targetver.h b/deps/picoquic/picotls/picotlsvs/picotlsvs/targetver.h new file mode 100644 index 0000000..87c0086 --- /dev/null +++ b/deps/picoquic/picotls/picotlsvs/picotlsvs/targetver.h @@ -0,0 +1,8 @@ +#pragma once + +// Including SDKDDKVer.h defines the highest available Windows platform. + +// If you wish to build your application for a previous Windows platform, include WinSDKVer.h and +// set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h. + +#include diff --git a/deps/picoquic/picotls/picotlsvs/pioctls-core/pioctls-core.vcxproj b/deps/picoquic/picotls/picotlsvs/pioctls-core/pioctls-core.vcxproj new file mode 100644 index 0000000..c4c7e53 --- /dev/null +++ b/deps/picoquic/picotls/picotlsvs/pioctls-core/pioctls-core.vcxproj @@ -0,0 +1,164 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 15.0 + {F53F19EE-149A-4C36-9772-25B01AB4BD7A} + Win32Proj + pioctlscore + 10.0.17763.0 + + + + StaticLibrary + true + v141 + Unicode + + + StaticLibrary + false + v141 + true + Unicode + + + StaticLibrary + true + v141 + Unicode + + + StaticLibrary + false + v141 + true + Unicode + + + + + + + + + + + + + + + + + + + + + true + + + true + + + false + + + false + + + + Use + Level3 + Disabled + true + _DEBUG;_LIB;%(PreprocessorDefinitions) + true + + + Windows + true + + + + + Use + Level3 + Disabled + true + WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) + true + + + Windows + true + + + + + Use + Level3 + MaxSpeed + true + true + true + WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions) + true + + + Windows + true + true + true + + + + + Use + Level3 + MaxSpeed + true + true + true + NDEBUG;_LIB;%(PreprocessorDefinitions) + true + + + Windows + true + true + true + + + + + + + + + Create + Create + Create + Create + + + + + + \ No newline at end of file diff --git a/deps/picoquic/picotls/picotlsvs/pioctls-core/pioctls-core.vcxproj.filters b/deps/picoquic/picotls/picotlsvs/pioctls-core/pioctls-core.vcxproj.filters new file mode 100644 index 0000000..782ac4b --- /dev/null +++ b/deps/picoquic/picotls/picotlsvs/pioctls-core/pioctls-core.vcxproj.filters @@ -0,0 +1,30 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Header Files + + + Header Files + + + + + Source Files + + + \ No newline at end of file diff --git a/deps/picoquic/picotls/picotlsvs/pioctls-core/pioctls-core.vcxproj.user b/deps/picoquic/picotls/picotlsvs/pioctls-core/pioctls-core.vcxproj.user new file mode 100644 index 0000000..be25078 --- /dev/null +++ b/deps/picoquic/picotls/picotlsvs/pioctls-core/pioctls-core.vcxproj.user @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/deps/picoquic/picotls/picotlsvs/pioctls-core/stdafx.cpp b/deps/picoquic/picotls/picotlsvs/pioctls-core/stdafx.cpp new file mode 100644 index 0000000..fd4f341 --- /dev/null +++ b/deps/picoquic/picotls/picotlsvs/pioctls-core/stdafx.cpp @@ -0,0 +1 @@ +#include "stdafx.h" diff --git a/deps/picoquic/picotls/picotlsvs/pioctls-core/stdafx.h b/deps/picoquic/picotls/picotlsvs/pioctls-core/stdafx.h new file mode 100644 index 0000000..69f8872 --- /dev/null +++ b/deps/picoquic/picotls/picotlsvs/pioctls-core/stdafx.h @@ -0,0 +1,14 @@ +// stdafx.h : include file for standard system include files, +// or project specific include files that are used frequently, but +// are changed infrequently +// + +#pragma once + +#include "targetver.h" + +#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers + + + +// reference additional headers your program requires here diff --git a/deps/picoquic/picotls/picotlsvs/pioctls-core/targetver.h b/deps/picoquic/picotls/picotlsvs/pioctls-core/targetver.h new file mode 100644 index 0000000..87c0086 --- /dev/null +++ b/deps/picoquic/picotls/picotlsvs/pioctls-core/targetver.h @@ -0,0 +1,8 @@ +#pragma once + +// Including SDKDDKVer.h defines the highest available Windows platform. + +// If you wish to build your application for a previous Windows platform, include WinSDKVer.h and +// set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h. + +#include diff --git a/deps/picoquic/picotls/picotlsvs/ptlsbench/ptlsbench.vcxproj b/deps/picoquic/picotls/picotlsvs/ptlsbench/ptlsbench.vcxproj new file mode 100644 index 0000000..4e06766 --- /dev/null +++ b/deps/picoquic/picotls/picotlsvs/ptlsbench/ptlsbench.vcxproj @@ -0,0 +1,171 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + + + + 15.0 + {15D7D32F-3B62-4B10-A06A-BA1ADD591D7F} + Win32Proj + ptlsbench + 10.0.17763.0 + + + + Application + true + v141 + Unicode + + + Application + false + v141 + true + Unicode + + + Application + true + v141 + Unicode + + + Application + false + v141 + true + Unicode + + + + + + + + + + + + + + + + + + + + + true + + + true + + + false + + + false + + + + NotUsing + Level3 + Disabled + true + _DEBUG;_CONSOLE;_WINDOWS;%(PreprocessorDefinitions) + true + pch.h + $(ProjectDir)..\picotls;$(ProjectDir)..\..\include;$(ProjectDir)\..\..\deps\cifra\src;$(ProjectDir)\..\..\deps\cifra\src\ext;$(ProjectDir)\..\..\deps\micro-ecc;$(OPENSSL64DIR)\include;$(OPENSSL64DIR)\;%(AdditionalIncludeDirectories) + + + Console + true + $(OPENSSL64DIR);$(OPENSSL64DIR)\lib;$(OutDir) + bcrypt.lib;picotls-core.lib;picotls-openssl.lib;picotls-bcrypt.lib;picotls-minicrypto.lib;picotls-minicrypto-deps.lib;libcrypto.lib;ws2_32.lib;%(AdditionalDependencies) + + + + + NotUsing + Level3 + Disabled + true + WIN32;_DEBUG;_CONSOLE;_WINDOWS;%(PreprocessorDefinitions) + true + pch.h + $(ProjectDir)..\picotls;$(ProjectDir)..\..\include;$(ProjectDir)\..\..\deps\cifra\src;$(ProjectDir)\..\..\deps\cifra\src\ext;$(ProjectDir)\..\..\deps\micro-ecc;$(OPENSSLDIR)\include;$(OPENSSLDIR)\;%(AdditionalIncludeDirectories) + + + Console + true + $(OPENSSLDIR);$(OutDir) + bcrypt.lib;picotls-core.lib;picotls-openssl.lib;picotls-bcrypt.lib;picotls-minicrypto.lib;picotls-minicrypto-deps.lib;libcrypto.lib;ws2_32.lib;%(AdditionalDependencies) + + + + + NotUsing + Level3 + MaxSpeed + true + true + true + WIN32;NDEBUG;_CONSOLE;_WINDOWS;%(PreprocessorDefinitions) + true + pch.h + $(ProjectDir)..\picotls;$(ProjectDir)..\..\include;$(ProjectDir)\..\..\deps\cifra\src;$(ProjectDir)\..\..\deps\cifra\src\ext;$(ProjectDir)\..\..\deps\micro-ecc;$(OPENSSLDIR)\include;$(OPENSSLDIR)\;%(AdditionalIncludeDirectories) + + + Console + true + true + true + $(OPENSSLDIR);$(OutDir) + picotls-core.lib;picotls-openssl.lib;picotls-minicrypto.lib;picotls-minicrypto-deps.lib;libcrypto.lib;bcrypt.lib;ws2_32.lib;%(AdditionalDependencies) + + + + + NotUsing + Level3 + MaxSpeed + true + true + true + NDEBUG;_CONSOLE;_WINDOWS;%(PreprocessorDefinitions) + true + pch.h + $(ProjectDir)..\picotls;$(ProjectDir)..\..\include;$(ProjectDir)\..\..\deps\cifra\src;$(ProjectDir)\..\..\deps\cifra\src\ext;$(ProjectDir)\..\..\deps\micro-ecc;$(OPENSSL64DIR)\include;$(OPENSSL64DIR)\;%(AdditionalIncludeDirectories) + + + Console + true + true + true + $(OPENSSL64DIR);$(OPENSSL64DIR)\lib;$(OutDir) + picotls-core.lib;picotls-openssl.lib;picotls-minicrypto.lib;picotls-minicrypto-deps.lib;libcrypto.lib;bcrypt.lib;ws2_32.lib;%(AdditionalDependencies) + + + + + + \ No newline at end of file diff --git a/deps/picoquic/picotls/picotlsvs/ptlsbench/ptlsbench.vcxproj.filters b/deps/picoquic/picotls/picotlsvs/ptlsbench/ptlsbench.vcxproj.filters new file mode 100644 index 0000000..a598c85 --- /dev/null +++ b/deps/picoquic/picotls/picotlsvs/ptlsbench/ptlsbench.vcxproj.filters @@ -0,0 +1,22 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Source Files + + + \ No newline at end of file diff --git a/deps/picoquic/picotls/picotlsvs/ptlsbench/ptlsbench.vcxproj.user b/deps/picoquic/picotls/picotlsvs/ptlsbench/ptlsbench.vcxproj.user new file mode 100644 index 0000000..a7c76ba --- /dev/null +++ b/deps/picoquic/picotls/picotlsvs/ptlsbench/ptlsbench.vcxproj.user @@ -0,0 +1,11 @@ + + + + $(OutDir) + WindowsLocalDebugger + + + $(OutDir) + WindowsLocalDebugger + + \ No newline at end of file diff --git a/deps/picoquic/picotls/picotlsvs/testopenssl/testopenssl.vcxproj b/deps/picoquic/picotls/picotlsvs/testopenssl/testopenssl.vcxproj new file mode 100644 index 0000000..6c7b156 --- /dev/null +++ b/deps/picoquic/picotls/picotlsvs/testopenssl/testopenssl.vcxproj @@ -0,0 +1,168 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 15.0 + {8750EE3B-9440-48BF-8D83-7274E94B06A7} + Win32Proj + testopenssl + 10.0.17763.0 + + + + Application + true + v141 + Unicode + + + Application + false + v141 + true + Unicode + + + Application + true + v141 + Unicode + + + Application + false + v141 + true + Unicode + + + + + + + + + + + + + + + + + + + + + true + + + true + + + false + + + false + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;_WINDOWS;%(PreprocessorDefinitions) + $(ProjectDir)..\picotls;$(ProjectDir)..\..\include;$(ProjectDir)\..\..\deps\cifra\src;$(ProjectDir)\..\..\deps\cifra\src\ext;$(ProjectDir)\..\..\deps\micro-ecc;$(OPENSSLDIR)\include;$(OPENSSLDIR)\;%(AdditionalIncludeDirectories) + + + Console + $(OPENSSLDIR);$(OutDir) + picotls-core.lib;picotls-openssl.lib;picotls-minicrypto.lib;picotls-minicrypto-deps.lib;libcrypto.lib;bcrypt.lib;ws2_32.lib;%(AdditionalDependencies) + + + + + + + Level3 + Disabled + _DEBUG;_CONSOLE;_WINDOWS;%(PreprocessorDefinitions) + $(ProjectDir);$(ProjectDir)..\picotls;$(ProjectDir)..\..\include;$(OPENSSL64DIR)\include;$(OPENSSL64DIR);%(AdditionalIncludeDirectories) + + + Console + $(OPENSSL64DIR);$(OPENSSL64DIR)\lib;$(OutDir);%(AdditionalLibraryDirectories) + picotls-core.lib;picotls-openssl.lib;picotls-minicrypto.lib;picotls-minicrypto-deps.lib;libcrypto.lib;bcrypt.lib;ws2_32.lib;%(AdditionalDependencies) + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;_WINDOWS;%(PreprocessorDefinitions) + $(ProjectDir)..\picotls;$(ProjectDir)..\..\include;$(ProjectDir)\..\..\deps\cifra\src;$(ProjectDir)\..\..\deps\cifra\src\ext;$(ProjectDir)\..\..\deps\micro-ecc;$(OPENSSLDIR)\include;$(OPENSSLDIR)\;%(AdditionalIncludeDirectories) + + + Console + true + true + $(OPENSSLDIR);$(OutDir) + picotls-core.lib;picotls-openssl.lib;picotls-minicrypto.lib;picotls-minicrypto-deps.lib;libcrypto.lib;bcrypt.lib;ws2_32.lib;%(AdditionalDependencies) + + + + + Level3 + + + MaxSpeed + true + true + NDEBUG;_CONSOLE;_WINDOWS;%(PreprocessorDefinitions) + $(ProjectDir);$(ProjectDir)..\picotls;$(ProjectDir)..\..\include;$(OPENSSL64DIR)\include;$(OPENSSL64DIR);%(AdditionalIncludeDirectories) + + + Console + true + true + $(OPENSSL64DIR);$(OPENSSL64DIR)\lib;$(OutDir);%(AdditionalLibraryDirectories) + picotls-core.lib;picotls-openssl.lib;picotls-minicrypto.lib;picotls-minicrypto-deps.lib;libcrypto.lib;bcrypt.lib;ws2_32.lib;%(AdditionalDependencies) + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/deps/picoquic/picotls/picotlsvs/testopenssl/testopenssl.vcxproj.filters b/deps/picoquic/picotls/picotlsvs/testopenssl/testopenssl.vcxproj.filters new file mode 100644 index 0000000..b6ccd2f --- /dev/null +++ b/deps/picoquic/picotls/picotlsvs/testopenssl/testopenssl.vcxproj.filters @@ -0,0 +1,41 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Source Files + + + Source Files + + + Header Files + + + + + Header Files + + + + + Header Files + + + Header Files + + + \ No newline at end of file diff --git a/deps/picoquic/picotls/src/esni.c b/deps/picoquic/picotls/src/esni.c new file mode 100644 index 0000000..5c5c047 --- /dev/null +++ b/deps/picoquic/picotls/src/esni.c @@ -0,0 +1,240 @@ +/* + * Copyright (c) 2018 Fastly, Kazuho Oku + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +#include +#include +#include +#include +#include +#ifdef _WINDOWS +#include "..\picotls\wincompat.h" +#ifndef _CRT_SECURE_NO_WARNINGS +#define _CRT_SECURE_NO_WARNINGS +#endif +#pragma warning(disable : 4996) +#else +#include +#endif +#include +#include +#include +#include +#include "picotls.h" +#include "picotls/pembase64.h" +#include "picotls/openssl.h" + +static int emit_esni(ptls_key_exchange_context_t **key_exchanges, ptls_cipher_suite_t **cipher_suites, uint16_t padded_length, + uint64_t not_before, uint64_t lifetime, char const *published_sni, char const *file_output) +{ + ptls_buffer_t buf; + ptls_key_exchange_context_t *ctx[256] = {NULL}; + int ret; + + ptls_buffer_init(&buf, "", 0); + + ptls_buffer_push16(&buf, PTLS_ESNI_VERSION_DRAFT03); + ptls_buffer_push(&buf, 0, 0, 0, 0); /* checksum, filled later */ + if (published_sni != NULL) { + ptls_buffer_push_block(&buf, 2, { ptls_buffer_pushv(&buf, published_sni, strlen(published_sni)); }); + } else { + ptls_buffer_push16(&buf, 0); + } + ptls_buffer_push_block(&buf, 2, { + size_t i; + for (i = 0; key_exchanges[i] != NULL; ++i) { + ptls_buffer_push16(&buf, key_exchanges[i]->algo->id); + ptls_buffer_push_block(&buf, 2, + { ptls_buffer_pushv(&buf, key_exchanges[i]->pubkey.base, key_exchanges[i]->pubkey.len); }); + } + }); + ptls_buffer_push_block(&buf, 2, { + size_t i; + for (i = 0; cipher_suites[i] != NULL; ++i) + ptls_buffer_push16(&buf, cipher_suites[i]->id); + }); + ptls_buffer_push16(&buf, padded_length); + ptls_buffer_push64(&buf, not_before); + ptls_buffer_push64(&buf, not_before + lifetime - 1); + ptls_buffer_push_block(&buf, 2, {}); + { /* fill checksum */ + uint8_t d[PTLS_SHA256_DIGEST_SIZE]; + ptls_calc_hash(&ptls_openssl_sha256, d, buf.base, buf.off); + memcpy(buf.base + 2, d, 4); + } + + if (file_output != NULL) { + FILE *fo = fopen(file_output, "wb"); + if (fo == NULL) { + fprintf(stderr, "failed to open file:%s:%s\n", optarg, strerror(errno)); + goto Exit; + } else { + fwrite(buf.base, 1, buf.off, fo); + fclose(fo); + } + } else { + /* emit the structure to stdout */ + fwrite(buf.base, 1, buf.off, stdout); + fflush(stdout); + } + + ret = 0; +Exit : { + size_t i; + for (i = 0; ctx[i] != NULL; ++i) + ctx[i]->on_exchange(ctx + i, 1, NULL, ptls_iovec_init(NULL, 0)); +} + ptls_buffer_dispose(&buf); + return ret; +} + +static void usage(const char *cmd, int status) +{ + printf("picotls-esni - generates an ESNI Resource Record\n" + "\n" + "Usage: %s [options]\n" + "Options:\n" + " -n published sni value\n" + " -K private key files (repeat the option to include multiple\n" + " keys)\n" + " -c aes128-gcm, chacha20-poly1305, ...\n" + " -d number of days until expiration (default: 90)\n" + " -p padded length (default: 260)\n" + " -o write output to specified file instead of stdout\n" + " (use on Windows as stdout is not binary there)\n" + " -h prints this help\n" + "\n" + "-c and -x can be used multiple times.\n" + "\n", + cmd); + exit(status); +} + +int main(int argc, char **argv) +{ + char const *published_sni = NULL; + char const *file_output = NULL; + ERR_load_crypto_strings(); + OpenSSL_add_all_algorithms(); +#if !defined(OPENSSL_NO_ENGINE) + /* Load all compiled-in ENGINEs */ + ENGINE_load_builtin_engines(); + ENGINE_register_all_ciphers(); + ENGINE_register_all_digests(); +#endif + + struct { + ptls_key_exchange_context_t *elements[256]; + size_t count; + } key_exchanges = {{NULL}, 0}; + struct { + ptls_cipher_suite_t *elements[256]; + size_t count; + } cipher_suites = {{NULL}, 0}; + uint16_t padded_length = 260; + uint64_t lifetime = 90 * 86400; + + int ch; + + while ((ch = getopt(argc, argv, "n:K:c:d:p:o:h")) != -1) { + switch (ch) { + case 'n': + published_sni = optarg; + break; + case 'K': { + FILE *fp; + EVP_PKEY *pkey; + + if ((fp = fopen(optarg, "rt")) == NULL) { + fprintf(stderr, "failed to open file:%s:%s\n", optarg, strerror(errno)); + exit(1); + } + + if ((pkey = PEM_read_PrivateKey(fp, NULL, NULL, NULL)) == NULL) { + fprintf(stderr, "failed to read private key from file:%s:%s\n", optarg, strerror(errno)); + exit(1); + } + fclose(fp); + if (ptls_openssl_create_key_exchange(key_exchanges.elements + key_exchanges.count++, pkey) != 0) { + fprintf(stderr, "unknown type of private key found in file:%s\n", optarg); + exit(1); + } + EVP_PKEY_free(pkey); + } break; + case 'c': { + size_t i; + for (i = 0; ptls_openssl_cipher_suites[i] != NULL; ++i) + if (strcasecmp(ptls_openssl_cipher_suites[i]->aead->name, optarg) == 0) + break; + if (ptls_openssl_cipher_suites[i] == NULL) { + fprintf(stderr, "unknown cipher-suite: %s\n", optarg); + exit(1); + } + cipher_suites.elements[cipher_suites.count++] = ptls_openssl_cipher_suites[i]; + } break; + case 'd': + if (sscanf(optarg, "%" SCNu64, &lifetime) != 1 || lifetime == 0) { + fprintf(stderr, "lifetime must be a positive integer\n"); + exit(1); + } + lifetime *= 86400; /* convert to seconds */ + break; + case 'p': +#ifdef _WINDOWS + if (sscanf_s(optarg, "%" SCNu16, &padded_length) != 1 || padded_length == 0) { + fprintf(stderr, "padded length must be a positive integer\n"); + exit(1); + } +#else + if (sscanf(optarg, "%" SCNu16, &padded_length) != 1 || padded_length == 0) { + fprintf(stderr, "padded length must be a positive integer\n"); + exit(1); + } +#endif + break; + case 'o': + file_output = optarg; + break; + case 'h': + usage(argv[0], 0); + break; + default: + usage(argv[0], 1); + break; + } + } + if (cipher_suites.count == 0) + cipher_suites.elements[cipher_suites.count++] = &ptls_openssl_aes128gcmsha256; + if (key_exchanges.count == 0) { + fprintf(stderr, "no private key specified\n"); + exit(1); + } + + argc -= optind; + argv += optind; + + if (emit_esni(key_exchanges.elements, cipher_suites.elements, padded_length, time(NULL), lifetime, published_sni, + file_output) != 0) { + fprintf(stderr, "failed to generate ESNI private structure.\n"); + exit(1); + } + + return 0; +} diff --git a/deps/picoquic/quicctx.c b/deps/picoquic/quicctx.c new file mode 100644 index 0000000..45a4976 --- /dev/null +++ b/deps/picoquic/quicctx.c @@ -0,0 +1,3805 @@ + +/* +* Author: Christian Huitema +* Copyright (c) 2017, Private Octopus, Inc. +* All rights reserved. +* +* Permission to use, copy, modify, and distribute this software for any +* purpose with or without fee is hereby granted, provided that the above +* copyright notice and this permission notice appear in all copies. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL Private Octopus, Inc. BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "picoquic.h" +#include "picoquic_internal.h" +#include "picoquic_unified_log.h" +#include "tls_api.h" +#include +#include +#ifndef _WINDOWS +#include +#endif + + +/* + * Supported versions. Specific versions may mandate different processing of different + * formats. + * The first version in the list is the preferred version. + * The protection of clear text packets will be a function of the version negotiation. + */ + +static uint8_t picoquic_cleartext_internal_test_1_salt[] = { + 0x30, 0x67, 0x16, 0xd7, 0x63, 0x75, 0xd5, 0x55, + 0x4b, 0x2f, 0x60, 0x5e, 0xef, 0x78, 0xd8, 0x33, + 0x3d, 0xc1, 0xca, 0x36 +}; + +static uint8_t picoquic_cleartext_draft_23_salt[] = { + 0xc3, 0xee, 0xf7, 0x12, 0xc7, 0x2e, 0xbb, 0x5a, + 0x11, 0xa7, 0xd2, 0x43, 0x2b, 0xb4, 0x63, 0x65, + 0xbe, 0xf9, 0xf5, 0x02 +}; + +uint8_t picoquic_retry_protection_key_25[32] = { + 0x65, 0x6e, 0x61, 0xe3, 0x36, 0xae, 0x94, 0x17, 0xf7, 0xf0, 0xed, 0xd8, 0xd7, 0x8d, 0x46, 0x1e, + 0x2a, 0xa7, 0x08, 0x4a, 0xba, 0x7a, 0x14, 0xc1, 0xe9, 0xf7, 0x26, 0xd5, 0x57, 0x09, 0x16, 0x9a }; + +static uint8_t picoquic_cleartext_draft_29_salt[] = { + 0xaf, 0xbf, 0xec, 0x28, 0x99, 0x93, 0xd2, 0x4c, + 0x9e, 0x97, 0x86, 0xf1, 0x9c, 0x61, 0x11, 0xe0, + 0x43, 0x90, 0xa8, 0x99 +}; + +uint8_t picoquic_retry_protection_key_29[32] = { + 0x8b, 0x0d, 0x37, 0xeb, 0x85, 0x35, 0x02, 0x2e, 0xbc, 0x8d, 0x76, 0xa2, 0x07, 0xd8, 0x0d, 0xf2, + 0x26, 0x46, 0xec, 0x06, 0xdc, 0x80, 0x96, 0x42, 0xc3, 0x0a, 0x8b, 0xaa, 0x2b, 0xaa, 0xff, 0x4c }; + +const picoquic_version_parameters_t picoquic_supported_versions[] = { + { PICOQUIC_TWENTIETH_INTEROP_VERSION, + sizeof(picoquic_cleartext_draft_29_salt), + picoquic_cleartext_draft_29_salt, + sizeof(picoquic_retry_protection_key_29), + picoquic_retry_protection_key_29 }, + { PICOQUIC_TWENTIETH_PRE_INTEROP_VERSION, + sizeof(picoquic_cleartext_draft_29_salt), + picoquic_cleartext_draft_29_salt, + sizeof(picoquic_retry_protection_key_29), + picoquic_retry_protection_key_29 }, + { PICOQUIC_NINETEENTH_INTEROP_VERSION, + sizeof(picoquic_cleartext_draft_29_salt), + picoquic_cleartext_draft_29_salt, + sizeof(picoquic_retry_protection_key_29), + picoquic_retry_protection_key_29 }, + { PICOQUIC_NINETEENTH_BIS_INTEROP_VERSION, + sizeof(picoquic_cleartext_draft_29_salt), + picoquic_cleartext_draft_29_salt, + sizeof(picoquic_retry_protection_key_29), + picoquic_retry_protection_key_29 }, + { PICOQUIC_EIGHTEENTH_INTEROP_VERSION, + sizeof(picoquic_cleartext_draft_23_salt), + picoquic_cleartext_draft_23_salt, + sizeof(picoquic_retry_protection_key_25), + picoquic_retry_protection_key_25 }, + { PICOQUIC_SEVENTEENTH_INTEROP_VERSION, + sizeof(picoquic_cleartext_draft_23_salt), + picoquic_cleartext_draft_23_salt, + sizeof(picoquic_retry_protection_key_25), + picoquic_retry_protection_key_25 }, + { PICOQUIC_INTERNAL_TEST_VERSION_2, + sizeof(picoquic_cleartext_internal_test_1_salt), + picoquic_cleartext_internal_test_1_salt, + sizeof(picoquic_retry_protection_key_25), + picoquic_retry_protection_key_25}, + { PICOQUIC_INTERNAL_TEST_VERSION_1, + sizeof(picoquic_cleartext_internal_test_1_salt), + picoquic_cleartext_internal_test_1_salt, + sizeof(picoquic_retry_protection_key_25), + picoquic_retry_protection_key_25 } +}; + +const size_t picoquic_nb_supported_versions = sizeof(picoquic_supported_versions) / sizeof(picoquic_version_parameters_t); + +/* +* Structures used in the hash table of connections +*/ +typedef struct st_picoquic_cnx_id_key_t { + picoquic_connection_id_t cnx_id; + picoquic_cnx_t* cnx; + picoquic_local_cnxid_t* l_cid; + struct st_picoquic_cnx_id_key_t* next_cnx_id; +} picoquic_cnx_id_key_t; + +typedef struct st_picoquic_net_id_key_t { + struct sockaddr_storage saddr; + picoquic_cnx_t* cnx; + picoquic_path_t* path; + struct st_picoquic_net_id_key_t* next_net_id; +} picoquic_net_id_key_t; + +typedef struct st_picoquic_net_icid_key_t { + struct sockaddr_storage saddr; + picoquic_connection_id_t icid; + picoquic_cnx_t* cnx; +} picoquic_net_icid_key_t; + +typedef struct st_picoquic_net_secret_key_t { + struct sockaddr_storage saddr; + uint8_t reset_secret[PICOQUIC_RESET_SECRET_SIZE]; + picoquic_cnx_t* cnx; +} picoquic_net_secret_key_t; + +/* Hash and compare for CNX hash tables */ +static uint64_t picoquic_cnx_id_hash(const void* key) +{ + const picoquic_cnx_id_key_t* cid = (const picoquic_cnx_id_key_t*)key; + return picoquic_connection_id_hash(&cid->cnx_id); +} + +static int picoquic_cnx_id_compare(const void* key1, const void* key2) +{ + const picoquic_cnx_id_key_t* cid1 = (const picoquic_cnx_id_key_t*)key1; + const picoquic_cnx_id_key_t* cid2 = (const picoquic_cnx_id_key_t*)key2; + + return picoquic_compare_connection_id(&cid1->cnx_id, &cid2->cnx_id); +} + +static uint64_t picoquic_net_id_hash(const void* key) +{ + const picoquic_net_id_key_t* net = (const picoquic_net_id_key_t*)key; + + // return picohash_bytes((uint8_t*)&net->saddr, sizeof(net->saddr)); + return picoquic_hash_addr((struct sockaddr*) & net->saddr); +} + +static int picoquic_net_id_compare(const void* key1, const void* key2) +{ + const picoquic_net_id_key_t* net1 = (const picoquic_net_id_key_t*)key1; + const picoquic_net_id_key_t* net2 = (const picoquic_net_id_key_t*)key2; + + return picoquic_compare_addr((struct sockaddr*) & net1->saddr, (struct sockaddr*) & net2->saddr); +} + +static uint64_t picoquic_net_icid_hash(const void* key) +{ + const picoquic_net_icid_key_t* net_icid = (const picoquic_net_icid_key_t*)key; + + return picohash_hash_mix(picoquic_hash_addr((struct sockaddr*) & net_icid->saddr), + picoquic_connection_id_hash(&net_icid->icid)); + +} + +static int picoquic_net_icid_compare(const void* key1, const void* key2) +{ + const picoquic_net_icid_key_t* net_icid1 = (const picoquic_net_icid_key_t*)key1; + const picoquic_net_icid_key_t* net_icid2 = (const picoquic_net_icid_key_t*)key2; + int ret = picoquic_compare_addr((struct sockaddr*) & net_icid1->saddr, (struct sockaddr*) & net_icid2->saddr); + if (ret == 0) { + ret = picoquic_compare_connection_id(&net_icid1->icid, &net_icid2->icid); + } + + return ret; +} + +static uint64_t picoquic_net_secret_hash(const void* key) +{ + const picoquic_net_secret_key_t* net_secret = (const picoquic_net_secret_key_t*)key; + + return picohash_hash_mix(picoquic_hash_addr((struct sockaddr*) & net_secret->saddr), + picohash_bytes(net_secret->reset_secret, PICOQUIC_RESET_SECRET_SIZE)); + +} + +static int picoquic_net_secret_compare(const void* key1, const void* key2) +{ + const picoquic_net_secret_key_t* net_secret1 = (const picoquic_net_secret_key_t*)key1; + const picoquic_net_secret_key_t* net_secret2 = (const picoquic_net_secret_key_t*)key2; + int ret = picoquic_compare_addr((struct sockaddr*) & net_secret1->saddr, (struct sockaddr*) & net_secret2->saddr); + + if (ret == 0) { +#ifdef PICOQUIC_USE_CONSTANT_TIME_MEMCMP + ret = picoquic_constant_time_memcmp(net_secret1->reset_secret, net_secret2->reset_secret, PICOQUIC_RESET_SECRET_SIZE); +#else + ret = memcmp(net_secret1->reset_secret, net_secret2->reset_secret, PICOQUIC_RESET_SECRET_SIZE); +#endif + } + return ret; +} + +picoquic_packet_context_enum picoquic_context_from_epoch(int epoch) +{ + static picoquic_packet_context_enum const pc[4] = { + picoquic_packet_context_initial, + picoquic_packet_context_application, + picoquic_packet_context_handshake, + picoquic_packet_context_application + }; + + return (epoch >= 0 && epoch < 4) ? pc[epoch] : 0; +} + +/* Token reuse management */ + +static int64_t picoquic_registered_token_compare(void* l, void* r) +{ + /* STream values are from 0 to 2^62-1, which means we are not worried with rollover */ + picoquic_registered_token_t* rt_l = (picoquic_registered_token_t*)l; + picoquic_registered_token_t* rt_r = (picoquic_registered_token_t*)r; + int64_t ret = rt_l->token_time - rt_r->token_time; + if (ret == 0) { + ret = rt_l->token_hash - rt_r->token_hash; + } + return ret; +} + +static picosplay_node_t* picoquic_registered_token_create(void* value) +{ + return &((picoquic_registered_token_t*)value)->registered_token_node; +} + + +static void* picoquic_registered_token_value(picosplay_node_t* node) +{ + return (void*)((char*)node - offsetof(struct st_picoquic_registered_token_t, registered_token_node)); +} + +static void picoquic_registered_token_delete(void* tree, picosplay_node_t* node) +{ + picoquic_registered_token_t* rt = (picoquic_registered_token_t*)picoquic_registered_token_value(node); + free(rt); +} + +int picoquic_registered_token_check_reuse(picoquic_quic_t * quic, + const uint8_t * token, size_t token_length, uint64_t expiry_time) +{ + int ret = -1; + if (token_length >= 8) { + picoquic_registered_token_t* rt = (picoquic_registered_token_t*)malloc(sizeof(picoquic_registered_token_t)); + if (rt != NULL) { + picosplay_node_t* rt_n = NULL; + memset(rt, 0, sizeof(picoquic_registered_token_t)); + rt->token_time = expiry_time; + rt->token_hash = PICOPARSE_64(token + token_length - 8); + rt->count = 1; + rt_n = picosplay_find(&quic->token_reuse_tree, rt); + if (rt_n != NULL) { + free(rt); + rt = (picoquic_registered_token_t*)picoquic_registered_token_value(rt_n); + rt->count++; + DBG_PRINTF("Token reuse detected, count=%d", rt->count); + } + else { + (void)picosplay_insert(&quic->token_reuse_tree, rt); + ret = 0; + } + } + } + + return ret; +} + +void picoquic_registered_token_clear(picoquic_quic_t* quic, uint64_t expiry_time_max) +{ + int end_reached = 0; + do { + picoquic_registered_token_t* rt_first = (picoquic_registered_token_t*) + picoquic_registered_token_value(picosplay_first(&quic->token_reuse_tree)); + if (rt_first == NULL || rt_first->token_time >= expiry_time_max) { + end_reached = 1; + } + else { + picosplay_delete_hint(&quic->token_reuse_tree, &rt_first->registered_token_node); + } + } while (!end_reached); +} + +/* Forward reference */ +static void picoquic_wake_list_init(picoquic_quic_t* quic); + +/* QUIC context create and dispose */ +picoquic_quic_t* picoquic_create(uint32_t nb_connections, + char const* cert_file_name, + char const* key_file_name, + char const * cert_root_file_name, + char const* default_alpn, + picoquic_stream_data_cb_fn default_callback_fn, + void* default_callback_ctx, + picoquic_connection_id_cb_fn cnx_id_callback, + void* cnx_id_callback_ctx, + uint8_t reset_seed[PICOQUIC_RESET_SECRET_SIZE], + uint64_t current_time, + uint64_t* p_simulated_time, + char const* ticket_file_name, + const uint8_t* ticket_encryption_key, + size_t ticket_encryption_key_length) +{ + picoquic_quic_t* quic = (picoquic_quic_t*)malloc(sizeof(picoquic_quic_t)); + int ret = 0; + + if (quic != NULL) { + /* TODO: winsock init */ + /* TODO: open UDP sockets - maybe */ + memset(quic, 0, sizeof(picoquic_quic_t)); + + quic->default_callback_fn = default_callback_fn; + quic->default_callback_ctx = default_callback_ctx; + quic->default_congestion_alg = PICOQUIC_DEFAULT_CONGESTION_ALGORITHM; + quic->default_alpn = picoquic_string_duplicate(default_alpn); + quic->cnx_id_callback_fn = cnx_id_callback; + quic->cnx_id_callback_ctx = cnx_id_callback_ctx; + quic->p_simulated_time = p_simulated_time; + quic->local_cnxid_length = 8; /* TODO: should be lower on clients-only implementation */ + quic->padding_multiple_default = 0; /* TODO: consider default = 128 */ + quic->padding_minsize_default = PICOQUIC_RESET_PACKET_MIN_SIZE; + quic->crypto_epoch_length_max = 0; + quic->max_simultaneous_logs = PICOQUIC_DEFAULT_SIMULTANEOUS_LOGS; + quic->max_half_open_before_retry = PICOQUIC_DEFAULT_HALF_OPEN_RETRY_THRESHOLD; + picoquic_wake_list_init(quic); + + if (cnx_id_callback != NULL) { + quic->unconditional_cnx_id = 1; + } + + if (ticket_file_name != NULL) { + quic->ticket_file_name = ticket_file_name; + ret = picoquic_load_tickets(&quic->p_first_ticket, current_time, ticket_file_name); + + if (ret == PICOQUIC_ERROR_NO_SUCH_FILE) { + DBG_PRINTF("Ticket file <%s> not created yet.\n", ticket_file_name); + ret = 0; + } + else if (ret != 0) { + DBG_PRINTF("Cannot load tickets from <%s>\n", ticket_file_name); + ret = 0; + } + } + + if (ret == 0) { + quic->table_cnx_by_id = picohash_create((size_t)nb_connections * 4, + picoquic_cnx_id_hash, picoquic_cnx_id_compare); + + quic->table_cnx_by_net = picohash_create((size_t)nb_connections * 4, + picoquic_net_id_hash, picoquic_net_id_compare); + + quic->table_cnx_by_icid = picohash_create((size_t)nb_connections, + picoquic_net_icid_hash, picoquic_net_icid_compare); + + quic->table_cnx_by_secret = picohash_create((size_t)nb_connections * 4, + picoquic_net_secret_hash, picoquic_net_secret_compare); + + picosplay_init_tree(&quic->token_reuse_tree, picoquic_registered_token_compare, + picoquic_registered_token_create, picoquic_registered_token_delete, picoquic_registered_token_value); + + if (quic->table_cnx_by_id == NULL || quic->table_cnx_by_net == NULL || + quic->table_cnx_by_icid == NULL || quic->table_cnx_by_secret == NULL) { + ret = -1; + DBG_PRINTF("%s", "Cannot initialize hash tables\n"); + } + else if (picoquic_master_tlscontext(quic, cert_file_name, key_file_name, cert_root_file_name, ticket_encryption_key, ticket_encryption_key_length) != 0) { + ret = -1; + DBG_PRINTF("%s", "Cannot create TLS context \n"); + } + else { + /* the random generator was initialized as part of the TLS context. + * Use it to create the seed for generating the per context stateless + * resets and the retry tokens */ + + if (!reset_seed) + picoquic_crypto_random(quic, quic->reset_seed, sizeof(quic->reset_seed)); + else + memcpy(quic->reset_seed, reset_seed, sizeof(quic->reset_seed)); + + picoquic_crypto_random(quic, quic->retry_seed, sizeof(quic->retry_seed)); + + /* If there is no root certificate context specified, use a null certifier. */ + } + } + + if (ret != 0) { + picoquic_free(quic); + quic = NULL; + } + } + + return quic; +} + +int picoquic_load_token_file(picoquic_quic_t* quic, char const * token_file_name) +{ + uint64_t current_time = picoquic_get_quic_time(quic); + int ret = picoquic_load_tokens(&quic->p_first_token, current_time, token_file_name); + + if (ret == PICOQUIC_ERROR_NO_SUCH_FILE) { + DBG_PRINTF("Ticket file <%s> not created yet.\n", token_file_name); + ret = 0; + } + else if (ret != 0) { + DBG_PRINTF("Cannot load tickets from <%s>\n", token_file_name); + } + + if (ret == 0) { + quic->token_file_name = token_file_name; + } + + return ret; +} + +int picoquic_set_default_tp(picoquic_quic_t* quic, picoquic_tp_t * tp) +{ + int ret = 0; + + if (quic->default_tp == NULL) { + quic->default_tp = (picoquic_tp_t *)malloc(sizeof(picoquic_tp_t)); + } + + if (quic->default_tp == NULL) { + ret = PICOQUIC_ERROR_MEMORY; + } + else { + memcpy(quic->default_tp, tp, sizeof(picoquic_tp_t)); + } + + return ret; +} + +void picoquic_set_default_padding(picoquic_quic_t* quic, uint32_t padding_multiple, uint32_t padding_minsize) +{ + quic->padding_minsize_default = padding_minsize; + quic->padding_multiple_default = padding_multiple; +} + +void picoquic_set_default_spinbit_policy(picoquic_quic_t * quic, picoquic_spinbit_version_enum default_spinbit_policy) +{ + quic->default_spin_policy = default_spinbit_policy; +} + +void picoquic_set_default_crypto_epoch_length(picoquic_quic_t* quic, uint64_t crypto_epoch_length_max) +{ + quic->crypto_epoch_length_max = (crypto_epoch_length_max == 0) ? + PICOQUIC_DEFAULT_CRYPTO_EPOCH_LENGTH : crypto_epoch_length_max; +} + +uint64_t picoquic_get_default_crypto_epoch_length(picoquic_quic_t* quic) +{ + return quic->crypto_epoch_length_max; +} + +void picoquic_set_crypto_epoch_length(picoquic_cnx_t* cnx, uint64_t crypto_epoch_length_max) +{ + cnx->crypto_epoch_length_max = (crypto_epoch_length_max == 0) ? + PICOQUIC_DEFAULT_CRYPTO_EPOCH_LENGTH : crypto_epoch_length_max; +} + +uint64_t picoquic_get_crypto_epoch_length(picoquic_cnx_t* cnx) +{ + return cnx->crypto_epoch_length_max; +} + + +uint8_t picoquic_get_local_cid_length(picoquic_quic_t* quic) +{ + return quic->local_cnxid_length; +} + +int picoquic_is_local_cid(picoquic_quic_t* quic, picoquic_connection_id_t* cid) +{ + return (cid->id_len == quic->local_cnxid_length && + picoquic_cnx_by_id(quic, *cid) != NULL); +} + +void picoquic_set_max_simultaneous_logs(picoquic_quic_t* quic, uint32_t max_simultaneous_logs) +{ + quic->max_simultaneous_logs = max_simultaneous_logs; +} + +uint32_t picoquic_get_max_simultaneous_logs(picoquic_quic_t* quic) +{ + return quic->max_simultaneous_logs; +} + +void picoquic_free(picoquic_quic_t* quic) +{ + if (quic != NULL) { + + /* delete all the connection contexts -- do this before any other + * action, as deleting connections may add packets to queues or + * change connection lists */ + while (quic->cnx_list != NULL) { + picoquic_delete_cnx(quic->cnx_list); + } + + /* Delete TLS and AEAD cntexts */ + picoquic_delete_retry_protection_contexts(quic); + + if (quic->aead_encrypt_ticket_ctx != NULL) { + picoquic_aead_free(quic->aead_encrypt_ticket_ctx); + quic->aead_encrypt_ticket_ctx = NULL; + } + + if (quic->aead_decrypt_ticket_ctx != NULL) { + picoquic_aead_free(quic->aead_decrypt_ticket_ctx); + quic->aead_decrypt_ticket_ctx = NULL; + } + + if (quic->default_alpn != NULL) { + free((void*)quic->default_alpn); + quic->default_alpn = NULL; + } + + /* delete the stored tickets */ + picoquic_free_tickets(&quic->p_first_ticket); + + /* Deelete the reused tokens tree */ + picosplay_empty_tree(&quic->token_reuse_tree); + + /* delete packets in pool */ + while (quic->p_first_packet != NULL) { + picoquic_packet_t * p = quic->p_first_packet->next_packet; + free(quic->p_first_packet); + quic->p_first_packet = p; + } + + /* delete all pending stateless packets */ + while (quic->pending_stateless_packet != NULL) { + picoquic_stateless_packet_t* to_delete = quic->pending_stateless_packet; + quic->pending_stateless_packet = to_delete->next_packet; + free(to_delete); + } + + if (quic->table_cnx_by_id != NULL) { + picohash_delete(quic->table_cnx_by_id, 1); + } + + if (quic->table_cnx_by_net != NULL) { + picohash_delete(quic->table_cnx_by_net, 1); + } + + if (quic->table_cnx_by_icid != NULL) { + picohash_delete(quic->table_cnx_by_icid, 1); + } + + if (quic->table_cnx_by_secret != NULL) { + picohash_delete(quic->table_cnx_by_secret, 1); + } + + if (quic->verify_certificate_ctx != NULL && + quic->free_verify_certificate_callback_fn != NULL) { + (quic->free_verify_certificate_callback_fn)(quic->verify_certificate_ctx); + quic->verify_certificate_ctx = NULL; + } + + if (quic->verify_certificate_callback_fn != NULL) { + picoquic_dispose_verify_certificate_callback(quic, 1); + } + + if (quic->default_tp != NULL) { + free(quic->default_tp); + quic->default_tp = NULL; + } + + /* Delete the picotls context */ + if (quic->tls_master_ctx != NULL) { + picoquic_master_tlscontext_free(quic); + + free(quic->tls_master_ctx); + quic->tls_master_ctx = NULL; + } + + quic->binlog_dir = picoquic_string_free(quic->binlog_dir); + quic->qlog_dir = picoquic_string_free(quic->qlog_dir); + + free(quic); + } +} + +void picoquic_set_null_verifier(picoquic_quic_t* quic) { + picoquic_dispose_verify_certificate_callback(quic, quic->verify_certificate_callback_fn != NULL); +} + +void picoquic_set_cookie_mode(picoquic_quic_t* quic, int cookie_mode) +{ + if (cookie_mode&1) { + quic->force_check_token = 1; + } else { + quic->force_check_token = 0; + } + + if (cookie_mode & 2) { + quic->provide_token = 1; + } + else { + quic->provide_token = 0; + } + + quic->check_token = (quic->force_check_token || quic->max_half_open_before_retry <= quic->current_number_half_open); +} + +void picoquic_set_max_half_open_retry_threshold(picoquic_quic_t* quic, uint32_t max_half_open_before_retry) +{ + quic->max_half_open_before_retry = max_half_open_before_retry; +} + +uint32_t picoquic_get_max_half_open_retry_threshold(picoquic_quic_t* quic) +{ + return quic->max_half_open_before_retry; +} + +picoquic_stateless_packet_t* picoquic_create_stateless_packet(picoquic_quic_t* quic) +{ +#ifdef _WINDOWS + UNREFERENCED_PARAMETER(quic); +#endif + return (picoquic_stateless_packet_t*)malloc(sizeof(picoquic_stateless_packet_t)); +} + +void picoquic_delete_stateless_packet(picoquic_stateless_packet_t* sp) +{ + free(sp); +} + +void picoquic_queue_stateless_packet(picoquic_quic_t* quic, picoquic_stateless_packet_t* sp) +{ + picoquic_stateless_packet_t** pnext = &quic->pending_stateless_packet; + + while ((*pnext) != NULL) { + pnext = &(*pnext)->next_packet; + } + + *pnext = sp; + sp->next_packet = NULL; +} + +picoquic_stateless_packet_t* picoquic_dequeue_stateless_packet(picoquic_quic_t* quic) +{ + picoquic_stateless_packet_t* sp = quic->pending_stateless_packet; + + if (sp != NULL) { + quic->pending_stateless_packet = sp->next_packet; + sp->next_packet = NULL; + picoquic_log_quic_pdu(quic, 0, picoquic_get_quic_time(quic), sp->cnxid_log64, + (struct sockaddr*) & sp->addr_to, (struct sockaddr*) & sp->addr_local, sp->length); + } + + return sp; +} + +int picoquic_cnx_is_still_logging(picoquic_cnx_t* cnx) +{ + int ret = + (cnx->nb_packets_logged < PICOQUIC_LOG_PACKET_MAX_SEQUENCE || cnx->quic->use_long_log); + + return ret; +} + +/* Connection context creation and registration */ +int picoquic_register_cnx_id(picoquic_quic_t* quic, picoquic_cnx_t* cnx, picoquic_local_cnxid_t* l_cid) +{ + int ret = 0; + picohash_item* item; + picoquic_cnx_id_key_t* key = (picoquic_cnx_id_key_t*)malloc(sizeof(picoquic_cnx_id_key_t)); + + if (key == NULL) { + ret = -1; + } else { + key->cnx_id = l_cid->cnx_id; + key->cnx = cnx; + key->l_cid = l_cid; + key->next_cnx_id = NULL; + + item = picohash_retrieve(quic->table_cnx_by_id, key); + + if (item != NULL) { + ret = -1; + } else { + ret = picohash_insert(quic->table_cnx_by_id, key); + + if (ret == 0) { + key->next_cnx_id = l_cid->first_cnx_id; + l_cid->first_cnx_id = key; + } + } + } + + return ret; +} + +int picoquic_register_net_id(picoquic_quic_t* quic, picoquic_cnx_t* cnx, picoquic_path_t * path_x, struct sockaddr* addr) +{ + int ret = 0; + picohash_item* item; + picoquic_net_id_key_t* key = (picoquic_net_id_key_t*)malloc(sizeof(picoquic_net_id_key_t)); + + if (key == NULL) { + ret = -1; + } else { + memset(key, 0, sizeof(picoquic_net_id_key_t)); + picoquic_store_addr(&key->saddr, addr); + + key->cnx = cnx; + key->path = path_x; + + item = picohash_retrieve(quic->table_cnx_by_net, key); + + if (item != NULL) { + ret = -1; + } else { + ret = picohash_insert(quic->table_cnx_by_net, key); + + if (ret == 0) { + key->next_net_id = path_x->first_net_id; + path_x->first_net_id = key; + } + } + } + + if (key != NULL && ret != 0) { + free(key); + } + + return ret; +} + +/* The initial CID and the reset secret are tracked in specific tables: + * + * - quic->table_cnx_by_icid: keyed by client address and initial CID. Created + * when the connection for the specified initial CID and address is created + * by the server (or the peer receiving the connection in P2P cases) + * - quic->table_cnx_by_secret: keyed by peer address and reset secret for + * the default path of the connection (cnx->path[0]). + * + * In both cases, the address is that associated to the default path. The + * path can be updated after migration, either by an address change or by + * a change of CID and secret while keeping the address constant. + * + * If either the default address or the default reset secret changes, the + * old table entry is updated to track the new address and secret. The + * entry is kept up to date until the connection closes. + * + * Migration can only happen after a connection is established, but + * packets could still arrive after that, maybe due to network delays. + * In order to keep the design simple, the ICID entry is created once, and + * kept for the duration of the connection. + * + * To facilitate management, the hash table keys are remembered in the + * connection context as: + * + * - cnx->reset_secret_key + * - cnx->net_icid_key + */ + +int picoquic_register_net_icid(picoquic_cnx_t* cnx) +{ + int ret = 0; + picohash_item* item; + picoquic_net_icid_key_t* key = (picoquic_net_icid_key_t*)malloc(sizeof(picoquic_net_icid_key_t)); + + if (key == NULL) { + ret = -1; + } + else { + memset(key, 0, sizeof(picoquic_net_icid_key_t)); + picoquic_store_addr(&key->saddr, (struct sockaddr *)&cnx->path[0]->peer_addr); + key->icid = cnx->initial_cnxid; + + key->cnx = cnx; + + item = picohash_retrieve(cnx->quic->table_cnx_by_icid, key); + + if (item != NULL) { + ret = -1; + } + else { + ret = picohash_insert(cnx->quic->table_cnx_by_icid, key); + + if (ret == 0) { + cnx->net_icid_key = key; + } + } + } + + if (key != NULL && ret != 0) { + free(key); + } + + return ret; +} + +int picoquic_register_net_secret(picoquic_cnx_t* cnx) +{ + int ret = 0; + picohash_item* item; + picoquic_net_secret_key_t* key = (picoquic_net_secret_key_t*)malloc(sizeof(picoquic_net_secret_key_t)); + + if (key == NULL) { + ret = PICOQUIC_ERROR_MEMORY; + } + else { + memset(key, 0, sizeof(picoquic_net_secret_key_t)); + picoquic_store_addr(&key->saddr, (struct sockaddr *)&cnx->path[0]->peer_addr); + memcpy(key->reset_secret, cnx->path[0]->reset_secret, PICOQUIC_RESET_SECRET_SIZE); + + key->cnx = cnx; + + item = picohash_retrieve(cnx->quic->table_cnx_by_secret, key); + + if (item != NULL) { + ret = -1; + } + else { + ret = picohash_insert(cnx->quic->table_cnx_by_secret, key); + + if (ret == 0) { + if (cnx->reset_secret_key != NULL) { + picohash_delete_key(cnx->quic->table_cnx_by_secret, cnx->reset_secret_key, 1); + } + cnx->reset_secret_key = key; + } + } + } + + if (key != NULL && ret != 0) { + free(key); + } + + return ret; +} + +void picoquic_init_transport_parameters(picoquic_tp_t* tp, int client_mode) +{ + tp->initial_max_stream_data_bidi_local = 0x200000; + tp->initial_max_stream_data_bidi_remote = 65635; + tp->initial_max_stream_data_uni = 65535; + tp->initial_max_data = 0x100000; + if (client_mode) { + tp->initial_max_stream_id_bidir = 2049; + tp->initial_max_stream_id_unidir = 2051; + } else { + tp->initial_max_stream_id_bidir = 2048; + tp->initial_max_stream_id_unidir = 2050; + } + tp->idle_timeout = PICOQUIC_MICROSEC_HANDSHAKE_MAX/1000; + tp->max_packet_size = PICOQUIC_PRACTICAL_MAX_MTU; + tp->max_datagram_frame_size = 0; + tp->ack_delay_exponent = 3; + tp->active_connection_id_limit = PICOQUIC_NB_PATH_TARGET; + tp->max_ack_delay = PICOQUIC_ACK_DELAY_MAX; + tp->enable_loss_bit = 2; + tp->min_ack_delay = PICOQUIC_ACK_DELAY_MIN; + tp->enable_time_stamp = 0; +} + + +/* management of the list of connections in context */ + +picoquic_quic_t* picoquic_get_quic_ctx(picoquic_cnx_t* cnx) +{ + return (cnx == NULL)?NULL:cnx->quic; +} + +picoquic_cnx_t* picoquic_get_first_cnx(picoquic_quic_t* quic) +{ + return quic->cnx_list; +} + +picoquic_cnx_t* picoquic_get_next_cnx(picoquic_cnx_t* cnx) +{ + return cnx->next_in_table; +} + +static void picoquic_insert_cnx_in_list(picoquic_quic_t* quic, picoquic_cnx_t* cnx) +{ + if (quic->cnx_list != NULL) { + quic->cnx_list->previous_in_table = cnx; + cnx->next_in_table = quic->cnx_list; + } else { + quic->cnx_last = cnx; + cnx->next_in_table = NULL; + } + quic->cnx_list = cnx; + cnx->previous_in_table = NULL; +} + +static void picoquic_remove_cnx_from_list(picoquic_cnx_t* cnx) +{ + if (cnx->next_in_table == NULL) { + cnx->quic->cnx_last = cnx->previous_in_table; + } else { + cnx->next_in_table->previous_in_table = cnx->previous_in_table; + } + + if (cnx->previous_in_table == NULL) { + cnx->quic->cnx_list = cnx->next_in_table; + } + else { + cnx->previous_in_table->next_in_table = cnx->next_in_table; + } + + if (cnx->net_icid_key != NULL) { + picohash_delete_key(cnx->quic->table_cnx_by_icid, cnx->net_icid_key, 1); + cnx->net_icid_key = NULL; + } + + if (cnx->reset_secret_key != NULL) { + picohash_delete_key(cnx->quic->table_cnx_by_secret, cnx->reset_secret_key, 1); + cnx->reset_secret_key = NULL; + } +} + +/* Management of the list of connections, sorted by wake time */ + +static void* picoquic_wake_list_node_value(picosplay_node_t* cnx_wake_node) +{ + return (cnx_wake_node == NULL)?NULL:(void*)((char*)cnx_wake_node - offsetof(struct st_picoquic_cnx_t, cnx_wake_node)); +} + +static int64_t picoquic_wake_list_compare(void* l, void* r) { + return (int64_t)((picoquic_cnx_t*)l)->next_wake_time - ((picoquic_cnx_t*)r)->next_wake_time; +} + +static picosplay_node_t* picoquic_wake_list_create_node(void* v_cnx) +{ + return &((picoquic_cnx_t*)v_cnx)->cnx_wake_node; +} + +static void picoquic_wake_list_delete_node(void* tree, picosplay_node_t* node) +{ +#ifdef _WINDOWS + UNREFERENCED_PARAMETER(tree); +#endif + memset(node, 0, sizeof(picosplay_node_t)); +} + +static void picoquic_wake_list_init(picoquic_quic_t * quic) +{ + picosplay_init_tree(&quic->cnx_wake_tree, picoquic_wake_list_compare, + picoquic_wake_list_create_node, picoquic_wake_list_delete_node, picoquic_wake_list_node_value); +} + +static void picoquic_remove_cnx_from_wake_list(picoquic_cnx_t* cnx) +{ + picosplay_delete_hint(&cnx->quic->cnx_wake_tree, &cnx->cnx_wake_node); +} + +static void picoquic_insert_cnx_by_wake_time(picoquic_quic_t* quic, picoquic_cnx_t* cnx) +{ + picosplay_insert(&quic->cnx_wake_tree, cnx); +} + +void picoquic_reinsert_by_wake_time(picoquic_quic_t* quic, picoquic_cnx_t* cnx, uint64_t next_time) +{ + picoquic_remove_cnx_from_wake_list(cnx); + cnx->next_wake_time = next_time; + picoquic_insert_cnx_by_wake_time(quic, cnx); +} + +picoquic_cnx_t* picoquic_get_earliest_cnx_to_wake(picoquic_quic_t* quic, uint64_t max_wake_time) +{ + picoquic_cnx_t* cnx = (picoquic_cnx_t *)picoquic_wake_list_node_value(picosplay_first(&quic->cnx_wake_tree)); + if (cnx != NULL && max_wake_time != 0 && cnx->next_wake_time > max_wake_time) + { + cnx = NULL; + } + + return cnx; +} + +uint64_t picoquic_get_next_wake_time(picoquic_quic_t* quic, uint64_t current_time) +{ + uint64_t wake_time = UINT64_MAX; + + if (quic->pending_stateless_packet != NULL) { + wake_time = current_time; + } + else{ + picoquic_cnx_t* cnx_wake_first = (picoquic_cnx_t*)picoquic_wake_list_node_value( + picosplay_first(&quic->cnx_wake_tree)); + + if (cnx_wake_first != NULL) { + wake_time = cnx_wake_first->next_wake_time; + } + } + + return wake_time; +} + +int64_t picoquic_get_next_wake_delay(picoquic_quic_t* quic, + uint64_t current_time, int64_t delay_max) +{ + uint64_t next_wake_time = picoquic_get_next_wake_time(quic, current_time); + int64_t wake_delay = next_wake_time - current_time; + + if (wake_delay > delay_max || next_wake_time == UINT64_MAX) { + wake_delay = delay_max; + } + + return wake_delay; +} + +/* Other context management functions */ + +int picoquic_get_version_index(uint32_t proposed_version) +{ + int ret = -1; + + for (size_t i = 0; i < picoquic_nb_supported_versions; i++) { + if (picoquic_supported_versions[i].version == proposed_version) { + ret = (int)i; + break; + } + } + + return ret; +} + + +static void picoquic_create_random_cnx_id(picoquic_quic_t* quic, picoquic_connection_id_t * cnx_id, uint8_t id_length) +{ + if (id_length > 0) { + picoquic_crypto_random(quic, cnx_id->id, id_length); + } + if (id_length < sizeof(cnx_id->id)) { + memset(cnx_id->id + id_length, 0, sizeof(cnx_id->id) - id_length); + } + cnx_id->id_len = id_length; +} + +/* Path management -- returns the index of the path that was created. */ + +int picoquic_create_path(picoquic_cnx_t* cnx, uint64_t start_time, const struct sockaddr* local_addr, const struct sockaddr* peer_addr) +{ + int ret = -1; + + if (cnx->nb_paths >= cnx->nb_path_alloc) + { + int new_alloc = (cnx->nb_path_alloc == 0) ? 1 : 2 * cnx->nb_path_alloc; + picoquic_path_t ** new_path = (picoquic_path_t **)malloc(new_alloc * sizeof(picoquic_path_t *)); + + if (new_path != NULL) + { + if (cnx->path != NULL) + { + if (cnx->nb_paths > 0) + { + memcpy(new_path, cnx->path, cnx->nb_paths * sizeof(picoquic_path_t *)); + } + free(cnx->path); + } + cnx->path = new_path; + cnx->nb_path_alloc = new_alloc; + } + } + + if (cnx->nb_paths < cnx->nb_path_alloc) + { + picoquic_path_t * path_x = (picoquic_path_t *)malloc(sizeof(picoquic_path_t)); + + if (path_x != NULL) + { + memset(path_x, 0, sizeof(picoquic_path_t)); + /* Register the sequence number */ + path_x->path_sequence = cnx->path_sequence_next; + cnx->path_sequence_next++; + + /* Set the addresses */ + picoquic_store_addr(&path_x->peer_addr, peer_addr); + picoquic_store_addr(&path_x->local_addr, local_addr); + + /* Set the challenge used for this path */ + for (int ichal = 0; ichal < PICOQUIC_CHALLENGE_REPEAT_MAX; ichal++) { + path_x->challenge[ichal] = picoquic_public_random_64(); + } + + /* Initialize the reset secret to a random value. This + * will prevent spurious matches to an all zero value, for example. + * The real value will be set when receiving the transport parameters. + */ + picoquic_public_random(path_x->reset_secret, PICOQUIC_RESET_SECRET_SIZE); + + /* Initialize per path time measurement */ + path_x->smoothed_rtt = PICOQUIC_INITIAL_RTT; + path_x->rtt_variant = 0; + path_x->retransmit_timer = PICOQUIC_INITIAL_RETRANSMIT_TIMER; + path_x->rtt_min = 0; + + /* Initialize per path congestion control state */ + path_x->cwin = PICOQUIC_CWIN_INITIAL; + path_x->bytes_in_transit = 0; + path_x->congestion_alg_state = NULL; + + /* Initialize per path pacing state */ + path_x->pacing_evaluation_time = start_time; + path_x->pacing_bucket_nanosec = 16; + path_x->pacing_bucket_max = 16; + path_x->pacing_packet_time_nanosec = 1; + path_x->pacing_packet_time_microsec = 1; + + /* Initialize the MTU */ + path_x->send_mtu = (peer_addr == NULL || peer_addr->sa_family == AF_INET) ? PICOQUIC_INITIAL_MTU_IPV4 : PICOQUIC_INITIAL_MTU_IPV6; + + /* Record the path */ + cnx->path[cnx->nb_paths] = path_x; + ret = cnx->nb_paths++; + } + } + + return ret; +} + +/* + * Register the path in the hash tables. + * This only registers the address associated with the path. + */ +void picoquic_register_path(picoquic_cnx_t* cnx, picoquic_path_t * path_x) +{ + + if (path_x->peer_addr.ss_family != 0) { + (void)picoquic_register_net_id(cnx->quic, cnx, cnx->path[0], (struct sockaddr *)&path_x->peer_addr); + } + + path_x->path_is_registered = 1; +} + +/* To delete a path, we need to delete the data allocated to the path: search items in + * the hash tables, and congestion algorithm context. Then delete the path data itself, + * and finally remove the path reference from the table of paths in the connection + * context. + */ + +static void picoquic_clear_path_data(picoquic_cnx_t* cnx, picoquic_path_t * path_x) +{ + while (path_x->first_net_id != NULL) { + picohash_item* item; + picoquic_net_id_key_t* net_id_key = path_x->first_net_id; + path_x->first_net_id = net_id_key->next_net_id; + net_id_key->next_net_id = NULL; + + item = picohash_retrieve(cnx->quic->table_cnx_by_net, net_id_key); + if (item != NULL) { + picohash_delete_item(cnx->quic->table_cnx_by_net, item, 1); + } + } + /* Remove the congestion data */ + if (cnx->congestion_alg != NULL) { + cnx->congestion_alg->alg_delete(path_x); + } + + /* Free the record */ + free(path_x); +} + +void picoquic_delete_path(picoquic_cnx_t* cnx, int path_index) +{ + picoquic_path_t * path_x = cnx->path[path_index]; + picoquic_packet_t* p = NULL; + + if (cnx->quic->F_log != NULL) { + fflush(cnx->quic->F_log); + } + + /* Remove old path data from retransmit queue */ + for (picoquic_packet_context_enum pc = 0; pc < picoquic_nb_packet_context; pc++) + { + p = cnx->pkt_ctx[pc].retransmit_newest; + + while (p != NULL) { + if (p->send_path == path_x) { + DBG_PRINTF("Erase path for packet pc: %d, seq:%" PRIu64 "\n", pc, p->sequence_number); + p->send_path = NULL; + } + p = p->next_packet; + } + + p = cnx->pkt_ctx[pc].retransmitted_newest; + while (p != NULL) { + if (p->send_path == path_x) { + DBG_PRINTF("Erase path for old packet pc: %d, seq:%" PRIu64 "\n", pc, p->sequence_number); + p->send_path = NULL; + } + p = p->next_packet; + } + } + /* Free the data */ + picoquic_clear_path_data(cnx, path_x); + + + /* Compact the path table */ + for (int i = path_index + 1; i < cnx->nb_paths; i++) { + cnx->path[i-1] = cnx->path[i]; + } + + cnx->nb_paths--; + cnx->path[cnx->nb_paths] = NULL; +} + +/* + * Path challenges may be abandoned if they are tried too many times without success. + */ + +void picoquic_delete_abandoned_paths(picoquic_cnx_t* cnx, uint64_t current_time, uint64_t * next_wake_time) +{ + int path_index_good = 1; + int path_index_current = 1; + unsigned int is_demotion_in_progress = 0; + + while (path_index_current < cnx->nb_paths) { + if (cnx->path[path_index_current]->challenge_failed || + (cnx->path[path_index_current]->path_is_demoted && + current_time >= cnx->path[path_index_current]->demotion_time) || + (path_index_current > 0 && cnx->path[path_index_current]->challenge_verified && + current_time - cnx->path[path_index_current]->latest_sent_time >= cnx->idle_timeout)) { + /* Demote any failed path */ + if (!cnx->path[path_index_current]->path_is_demoted) { + picoquic_demote_path(cnx, path_index_current, current_time); + } + /* Only increment the current index */ + is_demotion_in_progress |= cnx->path[path_index_current]->path_is_demoted; + path_index_current++; + } else { + if (cnx->path[path_index_current]->path_is_demoted && + current_time < cnx->path[path_index_current]->demotion_time){ + is_demotion_in_progress |= 1; + if (*next_wake_time > cnx->path[path_index_current]->demotion_time) { + *next_wake_time = cnx->path[path_index_current]->demotion_time; + SET_LAST_WAKE(cnx->quic, PICOQUIC_QUICCTX); + } + } + + if (path_index_current > path_index_good) { + /* swap the path indexed good with current */ + picoquic_path_t * path_x = cnx->path[path_index_current]; + cnx->path[path_index_current] = cnx->path[path_index_good]; + cnx->path[path_index_good] = path_x; + } + /* increment both indices */ + path_index_current++; + path_index_good++; + } + } + + while (cnx->nb_paths > path_index_good) { + int d_path = cnx->nb_paths - 1; + if (!picoquic_is_connection_id_null(&cnx->path[d_path]->remote_cnxid)) { + (void)picoquic_queue_retire_connection_id_frame(cnx, cnx->path[d_path]->remote_cnxid_sequence); + } + picoquic_delete_path(cnx, d_path); + } + + /* TODO: what if there are no paths left? */ + cnx->path_demotion_needed = is_demotion_in_progress; +} + +/* + * Demote path, compute the effective time for demotion. + */ +void picoquic_demote_path(picoquic_cnx_t* cnx, int path_index, uint64_t current_time) +{ + if (!cnx->path[path_index]->path_is_demoted) { + uint64_t demote_timer = cnx->path[path_index]->retransmit_timer; + + if (demote_timer < PICOQUIC_INITIAL_MAX_RETRANSMIT_TIMER) { + demote_timer = PICOQUIC_INITIAL_MAX_RETRANSMIT_TIMER; + } + + cnx->path[path_index]->path_is_demoted = 1; + cnx->path[path_index]->demotion_time = current_time + 3* demote_timer; + cnx->path_demotion_needed = 1; + } +} + +/* Promote path to default. This happens when a new path is verified, at the end + * of a migration, and becomes the new default path. + */ + +void picoquic_promote_path_to_default(picoquic_cnx_t* cnx, int path_index, uint64_t current_time) +{ + if (path_index > 0 && path_index < cnx->nb_paths) { + picoquic_path_t * path_x = cnx->path[path_index]; + + if (cnx->path[path_index]->path_is_preferred_path) { + /* this is a migration to the preferred path requested by the server */ + if (cnx->client_mode) { + cnx->remote_parameters.migration_disabled = 0; + } + else { + cnx->local_parameters.migration_disabled = 0; + } + } + + if (cnx->quic->F_log != NULL || cnx->f_binlog != NULL) { + char src_ip[128]; + char dst_ip[128]; + + picoquic_log_app_message(cnx, "Path %d promoted to default at T=%fs, Local: %s, Remote: %s", + path_index, (double)(current_time - cnx->start_time) / 1000000.0, + picoquic_addr_text((struct sockaddr*) & cnx->path[path_index]->local_addr, src_ip, sizeof(src_ip)), + picoquic_addr_text((struct sockaddr*) & cnx->path[path_index]->peer_addr, dst_ip, sizeof(dst_ip))); + } + + /* Set the congestion algorithm for the new path */ + if (cnx->congestion_alg != NULL) { + cnx->congestion_alg->alg_init(path_x, current_time); + } + + /* Mark old path as demoted */ + picoquic_demote_path(cnx, 0, current_time); + + /* Swap */ + cnx->path[path_index] = cnx->path[0]; + cnx->path[0] = path_x; + + /* Update the secret */ + (void)picoquic_register_net_secret(cnx); + } +} + +/* Set or renew challenge for a path */ +void picoquic_set_path_challenge(picoquic_cnx_t* cnx, int path_id, uint64_t current_time) +{ + if (!cnx->path[path_id]->challenge_required || cnx->path[path_id]->challenge_verified) { + /* Reset the path challenge */ + cnx->path[path_id]->challenge_required = 1; + for (int ichal = 0; ichal < PICOQUIC_CHALLENGE_REPEAT_MAX; ichal++) { + cnx->path[path_id]->challenge[ichal] = picoquic_public_random_64(); + } + cnx->path[path_id]->challenge_verified = 0; + cnx->path[path_id]->challenge_time = current_time; + cnx->path[path_id]->challenge_repeat_count = 0; + } +} + +/* Find path by address pair + */ +int picoquic_find_path_by_address(picoquic_cnx_t* cnx, const struct sockaddr* addr_to, + const struct sockaddr* addr_from, int * partial_match) +{ + int path_id = -1; + int is_null_from = 0; + struct sockaddr_storage null_addr; + + *partial_match = -1; + + if (addr_from != NULL || addr_to != NULL) { + if (addr_from == NULL || addr_to == NULL) { + memset(&null_addr, 0, sizeof(struct sockaddr_storage)); + if (addr_from == NULL) { + addr_from = (struct sockaddr*) & null_addr; + } + else { + addr_to = (struct sockaddr*) & null_addr; + } + is_null_from = 1; + } + + /* Find whether an existing path matches the pair of addresses */ + for (int i = 0; i < cnx->nb_paths; i++) { + if (picoquic_compare_addr((struct sockaddr*) & cnx->path[i]->peer_addr, + addr_from) == 0) { + if (cnx->path[i]->local_addr.ss_family == 0) { + *partial_match = i; + } + else if (picoquic_compare_addr((struct sockaddr*) & cnx->path[i]->local_addr, + addr_to) == 0) { + path_id = i; + break; + } + } + + if (path_id < 0 && is_null_from) { + path_id = *partial_match; + *partial_match = -1; + } + } + } + + return path_id; +} + +/* Assign CID to path */ +int picoquic_assign_peer_cnxid_to_path(picoquic_cnx_t* cnx, int path_id) +{ + int ret = -1; + picoquic_cnxid_stash_t* available_cnxid = picoquic_dequeue_cnxid_stash(cnx); + + if (available_cnxid != NULL) { + cnx->path[path_id]->remote_cnxid = available_cnxid->cnx_id; + cnx->path[path_id]->remote_cnxid_sequence = available_cnxid->sequence; + memcpy(cnx->path[path_id]->reset_secret, available_cnxid->reset_secret, + PICOQUIC_RESET_SECRET_SIZE); + free(available_cnxid); + ret = 0; + } + + return ret; +} + +/* Create a new path in order to trigger a migration */ +int picoquic_probe_new_path_ex(picoquic_cnx_t* cnx, const struct sockaddr* addr_from, + const struct sockaddr* addr_to, uint64_t current_time, int to_preferred_address) +{ + int ret = 0; + int partial_match_path = -1; + int path_id = -1; + + if ((cnx->remote_parameters.migration_disabled && !to_preferred_address ) || + cnx->local_parameters.migration_disabled) { + /* Do not create new paths if migration is disabled */ + ret = PICOQUIC_ERROR_MIGRATION_DISABLED; + DBG_PRINTF("Tried to create probe with migration disabled = %d", cnx->remote_parameters.migration_disabled); + } + else if ((path_id = picoquic_find_path_by_address(cnx, addr_to, addr_from, &partial_match_path)) >= 0) { + /* This path already exists. Will not create it, but will restore it in working order if disabled. */ + ret = -1; + } + else if (partial_match_path >= 0 && addr_from->sa_family == 0) { + /* This path already exists. Will not create it, but will restore it in working order if disabled. */ + ret = -1; + } + else if (cnx->cnxid_stash_first == NULL) { + /* No CNXID available yet. */ + ret = -1; + } + else if (cnx->nb_paths >= PICOQUIC_NB_PATH_TARGET) { + /* Too many paths created already */ + ret = -1; + } + else if (picoquic_create_path(cnx, current_time, addr_to, addr_from) > 0) { + path_id = cnx->nb_paths - 1; + ret = picoquic_assign_peer_cnxid_to_path(cnx, path_id); + + if (ret != 0) { + /* delete the path that was just created! */ + picoquic_delete_path(cnx, path_id); + } + else { + cnx->path[path_id]->path_is_published = 1; + picoquic_register_path(cnx, cnx->path[path_id]); + picoquic_set_path_challenge(cnx, path_id, current_time); + cnx->path[path_id]->path_is_preferred_path = to_preferred_address; + } + } + + return ret; +} + +int picoquic_probe_new_path(picoquic_cnx_t* cnx, const struct sockaddr* addr_from, + const struct sockaddr* addr_to, uint64_t current_time) +{ + return picoquic_probe_new_path_ex(cnx, addr_from, addr_to, current_time, 0); +} + +/* Reset the path MTU, for example if too many packet losses are detected */ +void picoquic_reset_path_mtu(picoquic_path_t* path_x) +{ + /* Re-initialize the MTU */ + path_x->send_mtu = (path_x->peer_addr.ss_family == 0 || path_x->peer_addr.ss_family == AF_INET) ? + PICOQUIC_INITIAL_MTU_IPV4 : PICOQUIC_INITIAL_MTU_IPV6; + /* Reset the MTU discovery context */ + path_x->send_mtu_max_tried = 0; + path_x->mtu_probe_sent = 0; +} + +/* + * Manage the stash of connection IDs sent by the peer + */ + +picoquic_cnxid_stash_t * picoquic_dequeue_cnxid_stash(picoquic_cnx_t * cnx) +{ + picoquic_cnxid_stash_t * stashed = NULL; + + if (cnx != NULL && cnx->cnxid_stash_first != NULL) { + stashed = cnx->cnxid_stash_first; + cnx->cnxid_stash_first = stashed->next_in_stash; + } + + return stashed; +} + +int picoquic_enqueue_cnxid_stash(picoquic_cnx_t* cnx, + const uint64_t sequence, const uint8_t cid_length, const uint8_t* cnxid_bytes, + const uint8_t* secret_bytes, picoquic_cnxid_stash_t** pstashed) +{ + int ret = 0; + int is_duplicate = 0; + size_t nb_cid_received = 0; + picoquic_connection_id_t cnx_id; + picoquic_cnxid_stash_t* next_stash = cnx->cnxid_stash_first; + picoquic_cnxid_stash_t* last_stash = NULL; + picoquic_cnxid_stash_t* stashed = NULL; + uint64_t cnxid_mask = 0; + + /* verify the format */ + if (picoquic_parse_connection_id(cnxid_bytes, cid_length, &cnx_id) == 0) { + ret = PICOQUIC_TRANSPORT_FRAME_FORMAT_ERROR; + } + + if (ret == 0 && cnx->path[0]->remote_cnxid.id_len == 0) { + /* Protocol error. The peer is using null length cnx_id */ + ret = PICOQUIC_TRANSPORT_PROTOCOL_VIOLATION; + } + + /* Verify that the proposed CID is not already in use */ + for (int i = 0; ret == 0 && i < cnx->nb_paths; i++) { + if (cnx->path[i]->remote_cnxid.id_len > 0) { + if (sequence == cnx->path[i]->remote_cnxid_sequence) { + if (picoquic_compare_connection_id(&cnx_id, &cnx->path[i]->remote_cnxid) == 0) + { + if (memcmp(secret_bytes, cnx->path[i]->reset_secret, PICOQUIC_RESET_SECRET_SIZE) == 0) { + is_duplicate = 1; + break; + } + else { + DBG_PRINTF("Path %d, Cnx_id: %02x%02x%02x%02x..., Reset = %02x%02x%02x%02x... vs %02x%02x%02x%02x...\n", + i, + cnx->path[i]->remote_cnxid.id[0], cnx->path[i]->remote_cnxid.id[1], + cnx->path[i]->remote_cnxid.id[2], cnx->path[i]->remote_cnxid.id[3], + secret_bytes[0], secret_bytes[1], secret_bytes[2], secret_bytes[3], + cnx->path[i]->reset_secret[0], cnx->path[i]->reset_secret[1], + cnx->path[i]->reset_secret[2], cnx->path[i]->reset_secret[3]); + ret = PICOQUIC_TRANSPORT_PROTOCOL_VIOLATION; + } + break; + } + else { + DBG_PRINTF("Path %d, Sequence %d, Cnx_id: %02x%02x%02x%02x..., vs %02x%02x%02x%02x...\n", + i, (int)sequence, + cnx->path[i]->remote_cnxid.id[0], cnx->path[i]->remote_cnxid.id[1], + cnx->path[i]->remote_cnxid.id[2], cnx->path[i]->remote_cnxid.id[3], + cnx_id.id[0], cnx_id.id[1], cnx_id.id[2], cnx_id.id[3]); + ret = PICOQUIC_TRANSPORT_PROTOCOL_VIOLATION; + } + } + else if (picoquic_compare_connection_id(&cnx_id, &cnx->path[i]->remote_cnxid) == 0) { + DBG_PRINTF("Path %d, Cnx_id: %02x%02x%02x%02x..., Sequence %d vs. %d\n", + i, cnx_id.id[0], cnx_id.id[1], cnx_id.id[2], cnx_id.id[3], + (int)sequence, (int)cnx->path[i]->remote_cnxid_sequence); + ret = PICOQUIC_TRANSPORT_PROTOCOL_VIOLATION; + } + else if (memcmp(secret_bytes, &cnx->path[i]->reset_secret, PICOQUIC_RESET_SECRET_SIZE) == 0) { + DBG_PRINTF("Path %d, Cnx_id: %02x%02x%02x%02x..., Sequence %d vs. %d, same secret\n", + i, cnx_id.id[0], cnx_id.id[1], cnx_id.id[2], cnx_id.id[3], + (int)sequence, (int)cnx->path[i]->remote_cnxid_sequence); + ret = PICOQUIC_TRANSPORT_PROTOCOL_VIOLATION; + } + else { + if (!cnx->path[i]->path_is_demoted) { + uint64_t check = ((uint64_t)1) << cnx->path[i]->remote_cnxid_sequence; + if ((cnxid_mask & check) == 0) { + nb_cid_received++; + cnxid_mask |= check; + } + } + } + } + } + + while (ret == 0 && is_duplicate == 0 && next_stash != NULL) { + if (picoquic_compare_connection_id(&cnx_id, &next_stash->cnx_id) == 0) + { + if (next_stash->sequence == sequence && + memcmp(secret_bytes, next_stash->reset_secret, PICOQUIC_RESET_SECRET_SIZE) == 0) { + is_duplicate = 1; + } + else { + ret = PICOQUIC_TRANSPORT_PROTOCOL_VIOLATION; + } + break; + } + else if (next_stash->sequence == sequence) { + ret = PICOQUIC_TRANSPORT_PROTOCOL_VIOLATION; + } + else if (memcmp(secret_bytes, next_stash->reset_secret, PICOQUIC_RESET_SECRET_SIZE) == 0) { + ret = PICOQUIC_TRANSPORT_PROTOCOL_VIOLATION; + } + else { + nb_cid_received++; + } + last_stash = next_stash; + next_stash = next_stash->next_in_stash; + } + + if (ret == 0 && is_duplicate == 0) { + if (nb_cid_received >= cnx->local_parameters.active_connection_id_limit) { + ret = PICOQUIC_TRANSPORT_CONNECTION_ID_LIMIT_ERROR; + } + else { + stashed = (picoquic_cnxid_stash_t*)malloc(sizeof(picoquic_cnxid_stash_t)); + + if (stashed == NULL) { + ret = PICOQUIC_TRANSPORT_INTERNAL_ERROR; + } + else { + (void)picoquic_parse_connection_id(cnxid_bytes, cid_length, &stashed->cnx_id); + stashed->sequence = sequence; + memcpy(stashed->reset_secret, secret_bytes, PICOQUIC_RESET_SECRET_SIZE); + stashed->next_in_stash = NULL; + + if (last_stash == NULL) { + cnx->cnxid_stash_first = stashed; + } + else { + last_stash->next_in_stash = stashed; + } + } + } + } + + /* the return argument is only used in tests */ + + if (pstashed != NULL) { + *pstashed = stashed; + } + + return ret; +} + +int picoquic_remove_not_before_cid(picoquic_cnx_t* cnx, uint64_t not_before, uint64_t current_time) +{ + int ret = 0; + picoquic_cnxid_stash_t * next_stash = cnx->cnxid_stash_first; + picoquic_cnxid_stash_t * previous_stash = NULL; + + while (ret == 0 && next_stash != NULL) { + if (next_stash->sequence < not_before) { + ret = picoquic_queue_retire_connection_id_frame(cnx, next_stash->sequence); + if (ret == 0){ + next_stash = next_stash->next_in_stash; + if (previous_stash == NULL) { + cnx->cnxid_stash_first = next_stash; + } + else { + previous_stash->next_in_stash = next_stash; + } + } + } + else { + previous_stash = next_stash; + next_stash = next_stash->next_in_stash; + } + } + + /* We need to stop transmitting data to the old CID. But we cannot just delete + * the correspondng paths,because there may be some data in transit. We must + * also ensure that at least one default path migrates successfully to a + * valid CID. As long as new CID are available, we can simply replace the + * old one by a new one. If no CID is available, the old path should be marked + * as failing, and thus scheduled for deletion after a time-out */ + + for (int i = 0; ret == 0 && i < cnx->nb_paths; i++) { + if (cnx->path[i]->remote_cnxid_sequence < not_before && + cnx->path[i]->remote_cnxid.id_len > 0 && + !cnx->path[i]->path_is_demoted) { + ret = picoquic_renew_connection_id(cnx, i); + if (ret != 0) { + DBG_PRINTF("Renew CNXID returns %x\n", ret); + if (i == 0) { + ret = PICOQUIC_TRANSPORT_PROTOCOL_VIOLATION; + } + else { + ret = 0; + picoquic_demote_path(cnx, i, current_time); + } + } + } + } + + return ret; +} + +/* Start using a new connection ID for the existing path + */ +int picoquic_renew_path_connection_id(picoquic_cnx_t* cnx, picoquic_path_t* path_x) +{ + int ret = 0; + picoquic_cnxid_stash_t * stashed = NULL; + + if (cnx->remote_parameters.migration_disabled != 0 || + cnx->local_parameters.migration_disabled != 0) { + /* Do not switch cnx_id if migration is disabled */ + ret = PICOQUIC_ERROR_MIGRATION_DISABLED; + } + else { + stashed = picoquic_dequeue_cnxid_stash(cnx); + + if (stashed == NULL) { + ret = PICOQUIC_ERROR_CNXID_NOT_AVAILABLE; + } else { + /* Count the references to the remote cnxid */ + int nb_cnxid_ref = 0; + + for (int i = 0; i < cnx->nb_paths; i++) { + if (cnx->path[i]->remote_cnxid_sequence == path_x->remote_cnxid_sequence) { + nb_cnxid_ref++; + } + } + + if (nb_cnxid_ref <= 1) { + /* if this was the last reference, retire the old cnxid */ + if (picoquic_queue_retire_connection_id_frame(cnx, path_x->remote_cnxid_sequence) != 0) { + DBG_PRINTF("Could not properly retire CID[%" PRIu64 "]", path_x->remote_cnxid_sequence); + } + } + + /* Install the new value */ + path_x->remote_cnxid = stashed->cnx_id; + path_x->remote_cnxid_sequence = stashed->sequence; + memcpy(path_x->reset_secret, stashed->reset_secret, + PICOQUIC_RESET_SECRET_SIZE); + free(stashed); + + /* If default path, reset the secret pointer */ + if (path_x == cnx->path[0]) { + ret = picoquic_register_net_secret(cnx); + } + } + } + + return ret; +} + +int picoquic_renew_connection_id(picoquic_cnx_t* cnx, int path_id) +{ + int ret; + + if (path_id >= cnx->nb_paths) { + ret = -1; + } + else { + ret = picoquic_renew_path_connection_id(cnx, cnx->path[path_id]); + } + + return ret; +} + +/* stream data splay management */ +int64_t picoquic_stream_data_node_compare(void* l, void* r) +{ + /* Offset values are from 0 to 2^62-1, which means we are not worried with rollover */ + return ((picoquic_stream_data_node_t*)l)->offset - ((picoquic_stream_data_node_t*)r)->offset; +} + +picosplay_node_t* picoquic_stream_data_node_create(void* value) +{ + return &((picoquic_stream_data_node_t*)value)->stream_data_node; +} + + +void* picoquic_stream_data_node_value(picosplay_node_t* node) +{ + return (void*)((char*)node - offsetof(struct st_picoquic_stream_data_node_t, stream_data_node)); +} + + +void picoquic_stream_data_node_delete(void* tree, picosplay_node_t* node) +{ + picoquic_stream_data_node_t* stream_data = (picoquic_stream_data_node_t*)picoquic_stream_data_node_value(node); + + if (stream_data->bytes != NULL) { + free(stream_data->bytes); + stream_data->bytes = NULL; + } + + free(stream_data); +} + +/* Stream splay management */ + +static int64_t picoquic_stream_node_compare(void *l, void *r) +{ + /* STream values are from 0 to 2^62-1, which means we are not worried with rollover */ + return ((picoquic_stream_head_t*)l)->stream_id - ((picoquic_stream_head_t*)r)->stream_id; +} + +static picosplay_node_t * picoquic_stream_node_create(void * value) +{ + return &((picoquic_stream_head_t *)value)->stream_node; +} + + +static void * picoquic_stream_node_value(picosplay_node_t * node) +{ + return (void*)((char*)node - offsetof(struct st_picoquic_stream_head_t, stream_node)); +} + +void picoquic_clear_stream(picoquic_stream_head_t* stream) +{ + picoquic_stream_data_node_t* ready = stream->send_queue; + picoquic_stream_data_node_t* next; + + while ((next = ready) != NULL) { + ready = next->next_stream_data; + + if (next->bytes != NULL) { + free(next->bytes); + } + free(next); + } + + picosplay_empty_tree(&stream->stream_data_tree); + + while (stream->first_sack_item.next_sack != NULL) { + picoquic_sack_item_t * sack = stream->first_sack_item.next_sack; + stream->first_sack_item.next_sack = sack->next_sack; + free(sack); + } +} + + +static void picoquic_stream_node_delete(void * tree, picosplay_node_t * node) +{ + picoquic_stream_head_t * stream = picoquic_stream_node_value(node); + + picoquic_clear_stream(stream); + + free(stream); +} + +/* Management of streams */ + +picoquic_stream_head_t * picoquic_stream_from_node(picosplay_node_t * node) +{ +#ifdef TOO_CAUTIOUS + return(picoquic_stream_head_t *)((node == NULL)?NULL:picoquic_stream_node_value(node)); +#else + return (picoquic_stream_head_t *)node; +#endif +} + +picoquic_stream_head_t * picoquic_first_stream(picoquic_cnx_t* cnx) +{ +#ifdef TOO_CAUTIOUS + return picoquic_stream_from_node(picosplay_first(&cnx->stream_tree)); +#else + return (picoquic_stream_head_t *)picosplay_first(&cnx->stream_tree); +#endif +} + +picoquic_stream_head_t * picoquic_last_stream(picoquic_cnx_t* cnx) +{ +#ifdef TOO_CAUTIOUS + return picoquic_stream_from_node(picosplay_last(&cnx->stream_tree)); +#else + return (picoquic_stream_head_t *)picosplay_last(&cnx->stream_tree); +#endif +} + +void picoquic_insert_output_stream(picoquic_cnx_t* cnx, picoquic_stream_head_t * stream) +{ + if (stream->is_output_stream == 0) { + if (stream->stream_id == cnx->high_priority_stream_id) { + /* insert in front */ + stream->previous_output_stream = NULL; + stream->next_output_stream = cnx->first_output_stream; + if (cnx->first_output_stream != NULL) { + cnx->first_output_stream->previous_output_stream = stream; + } + cnx->first_output_stream = stream; + } else { + stream->previous_output_stream = cnx->last_output_stream; + stream->next_output_stream = NULL; + if (cnx->last_output_stream == NULL) { + cnx->first_output_stream = stream; + cnx->last_output_stream = stream; + } + else { + cnx->last_output_stream->next_output_stream = stream; + cnx->last_output_stream = stream; + } + } + stream->is_output_stream = 1; + } +} + +void picoquic_remove_output_stream(picoquic_cnx_t* cnx, picoquic_stream_head_t * stream, picoquic_stream_head_t * previous_stream) +{ + if (stream->is_output_stream) { + stream->is_output_stream = 0; + + if (stream->previous_output_stream == NULL) { + cnx->first_output_stream = stream->next_output_stream; + } + else { + stream->previous_output_stream->next_output_stream = stream->next_output_stream; + } + + if (stream->next_output_stream == NULL) { + cnx->last_output_stream = stream->previous_output_stream; + } + else { + stream->next_output_stream->previous_output_stream = stream->previous_output_stream; + } + } +} + +picoquic_stream_head_t * picoquic_next_stream(picoquic_stream_head_t * stream) +{ + return (picoquic_stream_head_t *)picosplay_next((picosplay_node_t *)stream); +} + +picoquic_stream_head_t* picoquic_find_stream(picoquic_cnx_t* cnx, uint64_t stream_id) +{ + picoquic_stream_head_t target; + target.stream_id = stream_id; + + return (picoquic_stream_head_t *)picosplay_find(&cnx->stream_tree, (void*)&target); +} + +void picoquic_add_output_streams(picoquic_cnx_t* cnx, uint64_t old_limit, uint64_t new_limit, unsigned int is_bidir) +{ + uint64_t old_rank = STREAM_RANK_FROM_ID(old_limit); + uint64_t first_new_id = STREAM_ID_FROM_RANK(old_rank + 1ull, !cnx->client_mode, !is_bidir); + picoquic_stream_head_t* stream = picoquic_find_stream(cnx, first_new_id ); + + while (stream) { + if (stream->stream_id > old_limit) { + if (stream->stream_id > new_limit) { + break; + } + if (IS_LOCAL_STREAM_ID(stream->stream_id, cnx->client_mode) && IS_BIDIR_STREAM_ID(stream->stream_id) == is_bidir) { + picoquic_insert_output_stream(cnx, stream); + } + } + stream = picoquic_next_stream(stream); + } +} + +picoquic_stream_head_t* picoquic_create_stream(picoquic_cnx_t* cnx, uint64_t stream_id) +{ + picoquic_stream_head_t* stream = (picoquic_stream_head_t*)malloc(sizeof(picoquic_stream_head_t)); + if (stream != NULL) { + int is_output_stream = 0; + memset(stream, 0, sizeof(picoquic_stream_head_t)); + stream->stream_id = stream_id; + + if (IS_LOCAL_STREAM_ID(stream_id, cnx->client_mode)) { + if (IS_BIDIR_STREAM_ID(stream_id)) { + stream->maxdata_local = cnx->local_parameters.initial_max_stream_data_bidi_local; + stream->maxdata_remote = cnx->remote_parameters.initial_max_stream_data_bidi_remote; + is_output_stream = stream->stream_id <= cnx->max_stream_id_bidir_remote; + + } + else { + stream->maxdata_local = 0; + stream->maxdata_remote = cnx->remote_parameters.initial_max_stream_data_uni; + is_output_stream = stream->stream_id <= cnx->max_stream_id_unidir_remote; + } + } + else { + if (IS_BIDIR_STREAM_ID(stream_id)) { + stream->maxdata_local = cnx->local_parameters.initial_max_stream_data_bidi_remote; + stream->maxdata_remote = cnx->remote_parameters.initial_max_stream_data_bidi_local; + is_output_stream = 1; + } + else { + stream->maxdata_local = cnx->local_parameters.initial_max_stream_data_uni; + stream->maxdata_remote = 0; + is_output_stream = 0; + } + } + + picosplay_init_tree(&stream->stream_data_tree, picoquic_stream_data_node_compare, picoquic_stream_data_node_create, picoquic_stream_data_node_delete, picoquic_stream_data_node_value); + + picosplay_insert(&cnx->stream_tree, stream); + if (is_output_stream) { + picoquic_insert_output_stream(cnx, stream); + } + else { + picoquic_remove_output_stream(cnx, stream, NULL); + picoquic_delete_stream_if_closed(cnx, stream); + } + + if (stream_id >= cnx->next_stream_id[STREAM_TYPE_FROM_ID(stream_id)]) { + cnx->next_stream_id[STREAM_TYPE_FROM_ID(stream_id)] = NEXT_STREAM_ID_FOR_TYPE(stream_id); + } + } + + return stream; +} + +void picoquic_delete_stream(picoquic_cnx_t * cnx, picoquic_stream_head_t* stream) +{ + picosplay_delete(&cnx->stream_tree, stream); +} + +int picoquic_mark_direct_receive_stream(picoquic_cnx_t* cnx, uint64_t stream_id, picoquic_stream_direct_receive_fn direct_receive_fn, void* direct_receive_ctx) +{ + int ret = 0; + picoquic_stream_head_t* stream = picoquic_find_stream(cnx, stream_id); + picoquic_stream_data_node_t* data; + + if (stream == NULL) { + ret = PICOQUIC_ERROR_INVALID_STREAM_ID; + } + else if (!IS_BIDIR_STREAM_ID(stream_id) && IS_LOCAL_STREAM_ID(stream_id, cnx->client_mode)) { + ret = PICOQUIC_ERROR_INVALID_STREAM_ID; + } + else if (direct_receive_fn == NULL) { + /* This is illegal! */ + ret = PICOQUIC_ERROR_NO_CALLBACK_PROVIDED; + } + else { + stream->direct_receive_fn = direct_receive_fn; + stream->direct_receive_ctx = direct_receive_ctx; + /* If there is pending data, pass it. */ + while ((data = (picoquic_stream_data_node_t*)picosplay_first(&stream->stream_data_tree)) != NULL) { + size_t length = data->length; + uint64_t offset = data->offset; + uint8_t* bytes = data->bytes; + + if (offset < stream->consumed_offset) { + if (offset + length < stream->consumed_offset) { + length = 0; + } + else { + size_t delta_offset = (size_t)(stream->consumed_offset - offset); + length -= delta_offset; + offset += delta_offset; + } + } + + if (length > 0) { + ret = direct_receive_fn(cnx, stream_id, 0, bytes, offset, length, direct_receive_ctx); + } + + if (ret == 0) { + picosplay_delete_hint(&stream->stream_data_tree, &data->stream_data_node); + } + else { + break; + } + } + + /* If there is a fin offset, pass it. */ + if (ret == 0 && stream->fin_received && !stream->fin_signalled) { + uint8_t fin_bytes[8]; + ret = direct_receive_fn(cnx, stream_id, 1, fin_bytes, stream->fin_offset, 0, direct_receive_ctx); + } + } + + return ret; +} + + +/* Management of local CID. + * Local CID are created and registered on demand. + */ + +picoquic_local_cnxid_t* picoquic_create_local_cnxid(picoquic_cnx_t* cnx, picoquic_connection_id_t* suggested_value) +{ + picoquic_local_cnxid_t* l_cid = NULL; + int is_unique = 0; + + l_cid = (picoquic_local_cnxid_t*)malloc(sizeof(picoquic_local_cnxid_t)); + + if (l_cid != NULL) { + memset(l_cid, 0, sizeof(picoquic_local_cnxid_t)); + if (cnx->quic->local_cnxid_length == 0) { + is_unique = 1; + } + else { + for (int i = 0; i < 32; i++) { + if (i == 0 && suggested_value != NULL) { + l_cid->cnx_id = *suggested_value; + } + else { + picoquic_create_random_cnx_id(cnx->quic, &l_cid->cnx_id, cnx->quic->local_cnxid_length); + + if (cnx->quic->cnx_id_callback_fn) { + cnx->quic->cnx_id_callback_fn(cnx->quic, l_cid->cnx_id, cnx->initial_cnxid, + cnx->quic->cnx_id_callback_ctx, &l_cid->cnx_id); + } + } + + if (picoquic_cnx_by_id(cnx->quic, l_cid->cnx_id) == NULL) { + is_unique = 1; + break; + } + } + } + + if (is_unique) { + picoquic_local_cnxid_t* previous = NULL; + picoquic_local_cnxid_t* next = cnx->local_cnxid_first; + + while (next != NULL) { + previous = next; + next = next->next; + } + + if (previous == NULL) { + cnx->local_cnxid_first = l_cid; + } + else { + previous->next = l_cid; + } + + l_cid->sequence = cnx->local_cnxid_sequence_next++; + cnx->nb_local_cnxid++; + + if (cnx->quic->local_cnxid_length > 0) { + picoquic_register_cnx_id(cnx->quic, cnx, l_cid); + } + } + else { + free(l_cid); + l_cid = NULL; + } + } + + return l_cid; +} + +void picoquic_delete_local_cnxid(picoquic_cnx_t* cnx, picoquic_local_cnxid_t* l_cid) +{ + picoquic_local_cnxid_t* previous = NULL; + picoquic_local_cnxid_t* next = cnx->local_cnxid_first; + + + /* Set l_cid references to NULL in path contexts */ + for (int i = 0; i < cnx->nb_paths; i++) { + if (cnx->path[i]->p_local_cnxid == l_cid) { + cnx->path[i]->p_local_cnxid = NULL; + } + } + + /* Remove from list */ + while (next != NULL) { + if (next == l_cid) { + if (previous == NULL) { + cnx->local_cnxid_first = next->next; + } + else { + previous->next = next->next; + } + cnx->nb_local_cnxid--; + break; + } + else { + previous = next; + next = next->next; + } + } + + if (l_cid->cnx_id.id_len > 0) { + /* Remove the registration in hash tables */ + if (l_cid->first_cnx_id != NULL) { + picohash_item* item; + picoquic_cnx_id_key_t* cnx_id_key = l_cid->first_cnx_id; + + item = picohash_retrieve(cnx->quic->table_cnx_by_id, cnx_id_key); + if (item != NULL) { + picohash_delete_item(cnx->quic->table_cnx_by_id, item, 1); + } + + l_cid->first_cnx_id = NULL; + } + } + + /* Delete and done */ + free(l_cid); +} + +void picoquic_retire_local_cnxid(picoquic_cnx_t* cnx, uint64_t sequence) +{ + picoquic_local_cnxid_t* local_cnxid = cnx->local_cnxid_first; + + while (local_cnxid != NULL) { + if (local_cnxid->sequence == sequence) { + break; + } + else { + local_cnxid = local_cnxid->next; + } + } + + if (local_cnxid != NULL) { + picoquic_delete_local_cnxid(cnx, local_cnxid); + } +} + +picoquic_local_cnxid_t* picoquic_find_local_cnxid(picoquic_cnx_t* cnx, picoquic_connection_id_t* cnxid) +{ + picoquic_local_cnxid_t* local_cnxid = cnx->local_cnxid_first; + + while (local_cnxid != NULL) { + if (picoquic_compare_connection_id(&local_cnxid->cnx_id, cnxid) == 0) { + break; + } + else { + local_cnxid = local_cnxid->next; + } + } + + return local_cnxid; +} + +/* Connection management + */ + +picoquic_cnx_t* picoquic_create_cnx(picoquic_quic_t* quic, + picoquic_connection_id_t initial_cnx_id, picoquic_connection_id_t remote_cnx_id, + const struct sockaddr* addr_to, uint64_t start_time, uint32_t preferred_version, + char const* sni, char const* alpn, char client_mode) +{ + picoquic_cnx_t* cnx = (picoquic_cnx_t*)malloc(sizeof(picoquic_cnx_t)); + + if (cnx != NULL) { + int ret; + picoquic_local_cnxid_t* cnxid0; + + memset(cnx, 0, sizeof(picoquic_cnx_t)); + cnx->start_time = start_time; + cnx->client_mode = client_mode; + if (client_mode) { + if (picoquic_is_connection_id_null(&initial_cnx_id)) { + picoquic_create_random_cnx_id(quic, &initial_cnx_id, 8); + } + } + cnx->initial_cnxid = initial_cnx_id; + cnx->quic = quic; + /* Create the connection ID number 0 */ + cnxid0 = picoquic_create_local_cnxid(cnx, NULL); + + /* Should return 0, since this is the first path */ + ret = picoquic_create_path(cnx, start_time, NULL, addr_to); + + if (ret != 0 || cnxid0 == NULL) { + free(cnx); + cnx = NULL; + } else { + cnx->next_wake_time = start_time; + SET_LAST_WAKE(quic, PICOQUIC_QUICCTX); + picoquic_insert_cnx_in_list(quic, cnx); + picoquic_insert_cnx_by_wake_time(quic, cnx); + /* Do not require verification for default path */ + cnx->path[0]->p_local_cnxid = cnxid0; + cnx->path[0]->challenge_verified = 1; + + cnx->high_priority_stream_id = (uint64_t)((int64_t)-1); + for (int i = 0; i < 4; i++) { + cnx->next_stream_id[i] = i; + } + picoquic_register_path(cnx, cnx->path[0]); + } + } + + if (cnx != NULL) { + if (quic->default_tp == NULL) { + picoquic_init_transport_parameters(&cnx->local_parameters, cnx->client_mode); + } else { + memcpy(&cnx->local_parameters, quic->default_tp, sizeof(picoquic_tp_t)); + /* If the default parameters include preferred address, document it */ + if (cnx->local_parameters.prefered_address.is_defined) { + /* Create an additional CID */ + picoquic_local_cnxid_t* cnxid1 = picoquic_create_local_cnxid(cnx, NULL); + if (cnxid1 != NULL){ + /* copy the connection ID into the local parameter */ + cnx->local_parameters.prefered_address.connection_id = cnxid1->cnx_id; + /* Create the reset secret */ + (void)picoquic_create_cnxid_reset_secret(cnx->quic, &cnxid1->cnx_id, + cnx->local_parameters.prefered_address.statelessResetToken); + } + } + } + /* If local connection ID size is null, don't allow migration */ + if (quic->local_cnxid_length == 0) { + cnx->local_parameters.migration_disabled = 1; + } + + if (cnx->quic->mtu_max > 0) + { + cnx->local_parameters.max_packet_size = cnx->quic->mtu_max; + } + + /* Initialize local flow control variables to advertised values */ + cnx->maxdata_local = ((uint64_t)cnx->local_parameters.initial_max_data); + cnx->max_stream_id_bidir_local = cnx->local_parameters.initial_max_stream_id_bidir; + cnx->max_stream_id_bidir_local_computed = STREAM_TYPE_FROM_ID(cnx->local_parameters.initial_max_stream_id_bidir); + cnx->max_stream_id_unidir_local = cnx->local_parameters.initial_max_stream_id_unidir; + cnx->max_stream_id_unidir_local_computed = STREAM_TYPE_FROM_ID(cnx->local_parameters.initial_max_stream_id_unidir); + + /* Initialize padding policy to default for context */ + cnx->padding_multiple = quic->padding_multiple_default; + cnx->padding_minsize = quic->padding_minsize_default; + + /* Initialize spin policy, ensure that at least 1/8th of connections do not spin */ + cnx->spin_policy = quic->default_spin_policy; + if (cnx->spin_policy == picoquic_spinbit_basic) { + uint8_t rand256 = (uint8_t)picoquic_public_random_64(); + if (rand256 < PICOQUIC_SPIN_RESERVE_MOD_256) { + cnx->spin_policy = picoquic_spinbit_null; + } + } + else if (cnx->spin_policy == picoquic_spinbit_on) { + /* Option used in test to avoid randomizing spin bit on/off */ + cnx->spin_policy = picoquic_spinbit_basic; + } + + if (sni != NULL) { + cnx->sni = picoquic_string_duplicate(sni); + } + + if (alpn != NULL) { + cnx->alpn = picoquic_string_duplicate(alpn); + } + + cnx->callback_fn = quic->default_callback_fn; + cnx->callback_ctx = quic->default_callback_ctx; + cnx->congestion_alg = quic->default_congestion_alg; + + /* Initialize key rotation interval to default value */ + cnx->crypto_epoch_length_max = quic->crypto_epoch_length_max; + + /* Perform different initializations for clients and servers */ + if (cnx->client_mode) { + if (preferred_version == 0) { + cnx->proposed_version = picoquic_supported_versions[0].version; + cnx->version_index = 0; + } else { + cnx->version_index = picoquic_get_version_index(preferred_version); + if (cnx->version_index < 0) { + cnx->version_index = PICOQUIC_INTEROP_VERSION_INDEX; + if ((preferred_version & 0x0A0A0A0A) == 0x0A0A0A0A) { + /* This is a hack, to allow greasing the cnx ID */ + cnx->proposed_version = preferred_version; + + } else { + cnx->proposed_version = picoquic_supported_versions[PICOQUIC_INTEROP_VERSION_INDEX].version; + } + } else { + cnx->proposed_version = preferred_version; + } + } + + cnx->cnx_state = picoquic_state_client_init; + + if (!quic->is_cert_store_not_empty || sni == NULL) { + /* This is a hack. The open SSL certifier crashes if no name is specified, + * and always fails if no certificate is stored, so we just use a NULL verifier */ + picoquic_log_app_message(cnx, "%s -- certificate will not be verified.\n", + (sni == NULL) ? "No server name specified" : "No root crt list specified"); + + picoquic_set_null_verifier(quic); + } + } else { + cnx->is_half_open = 1; + cnx->quic->current_number_half_open += 1; + if (cnx->quic->current_number_half_open > cnx->quic->max_half_open_before_retry) { + cnx->quic->check_token = 1; + } + for (int epoch = 0; epoch < PICOQUIC_NUMBER_OF_EPOCHS; epoch++) { + cnx->tls_stream[epoch].send_queue = NULL; + } + cnx->cnx_state = picoquic_state_server_init; + cnx->initial_cnxid = initial_cnx_id; + cnx->path[0]->remote_cnxid = remote_cnx_id; + + cnx->version_index = picoquic_get_version_index(preferred_version); + if (cnx->version_index < 0) { + /* TODO: this is an internal error condition, should not happen */ + cnx->version_index = 0; + cnx->proposed_version = picoquic_supported_versions[0].version; + } else { + cnx->proposed_version = preferred_version; + } + } + + for (picoquic_packet_context_enum pc = 0; + pc < picoquic_nb_packet_context; pc++) { + cnx->pkt_ctx[pc].first_sack_item.start_of_sack_range = (uint64_t)((int64_t)-1); + cnx->pkt_ctx[pc].first_sack_item.end_of_sack_range = 0; + cnx->pkt_ctx[pc].first_sack_item.next_sack = NULL; + cnx->pkt_ctx[pc].highest_ack_sent = 0; + cnx->pkt_ctx[pc].highest_ack_sent_time = start_time; + cnx->pkt_ctx[pc].time_stamp_largest_received = (uint64_t)((int64_t)-1); + if (quic->random_initial) { + cnx->pkt_ctx[pc].send_sequence = picoquic_crypto_uniform_random(quic, PICOQUIC_PN_RANDOM_RANGE) + + PICOQUIC_PN_RANDOM_MIN; + } + else { + cnx->pkt_ctx[pc].send_sequence = 0; + } + cnx->pkt_ctx[pc].nb_retransmit = 0; + cnx->pkt_ctx[pc].latest_retransmit_time = 0; + cnx->pkt_ctx[pc].retransmit_newest = NULL; + cnx->pkt_ctx[pc].retransmit_oldest = NULL; + cnx->pkt_ctx[pc].highest_acknowledged = cnx->pkt_ctx[pc].send_sequence - 1; + cnx->pkt_ctx[pc].latest_time_acknowledged = start_time; + cnx->pkt_ctx[pc].highest_acknowledged_time = start_time; + cnx->pkt_ctx[pc].ack_needed = 0; + } + + cnx->latest_progress_time = start_time; + + for (int epoch = 0; epoch < PICOQUIC_NUMBER_OF_EPOCHS; epoch++) { + cnx->tls_stream[epoch].stream_id = 0; + cnx->tls_stream[epoch].consumed_offset = 0; + cnx->tls_stream[epoch].fin_offset = 0; + cnx->tls_stream[epoch].stream_node.left = NULL; + cnx->tls_stream[epoch].stream_node.parent = NULL; + cnx->tls_stream[epoch].stream_node.right = NULL; + cnx->tls_stream[epoch].sent_offset = 0; + cnx->tls_stream[epoch].local_error = 0; + cnx->tls_stream[epoch].remote_error = 0; + cnx->tls_stream[epoch].maxdata_local = (uint64_t)((int64_t)-1); + cnx->tls_stream[epoch].maxdata_remote = (uint64_t)((int64_t)-1); + + picosplay_init_tree(&cnx->tls_stream[epoch].stream_data_tree, picoquic_stream_data_node_compare, picoquic_stream_data_node_create, picoquic_stream_data_node_delete, picoquic_stream_data_node_value); + + /* No need to reset the state flags, as they are not used for the crypto stream */ + } + + cnx->ack_frequency_sequence_local = (uint64_t)((int64_t)-1); + cnx->ack_gap_local = 2; + cnx->ack_frequency_delay_local = PICOQUIC_ACK_DELAY_MAX_DEFAULT; + cnx->ack_frequency_sequence_remote = (uint64_t)((int64_t)-1); + cnx->ack_gap_remote = 2; + cnx->ack_delay_remote = PICOQUIC_ACK_DELAY_MAX_DEFAULT; + + picosplay_init_tree(&cnx->stream_tree, picoquic_stream_node_compare, picoquic_stream_node_create, picoquic_stream_node_delete, picoquic_stream_node_value); + + cnx->congestion_alg = cnx->quic->default_congestion_alg; + if (cnx->congestion_alg != NULL) { + cnx->congestion_alg->alg_init(cnx->path[0], start_time); + } + } + + /* Only initialize TLS after all parameters have been set */ + if (cnx != NULL && picoquic_tlscontext_create(quic, cnx, start_time) != 0) { + /* Cannot just do partial creation! */ + picoquic_delete_cnx(cnx); + cnx = NULL; + } + + if (cnx != NULL) { + if (picoquic_setup_initial_traffic_keys(cnx)) { + /* Cannot initialize aead for initial packets */ + picoquic_delete_cnx(cnx); + cnx = NULL; + } + } + + if (cnx != NULL && !client_mode && quic->local_cnxid_length > 0) { + if (picoquic_register_net_icid(cnx) != 0) { + DBG_PRINTF("%s", "Could not register the ICID in table.\n"); + picoquic_delete_cnx(cnx); + cnx = NULL; + } + } + + if (cnx != NULL) { + picoquic_log_new_connection(cnx); + } + + return cnx; +} + +picoquic_cnx_t* picoquic_create_client_cnx(picoquic_quic_t* quic, + struct sockaddr* addr, uint64_t start_time, uint32_t preferred_version, + char const* sni, char const* alpn, picoquic_stream_data_cb_fn callback_fn, void* callback_ctx) +{ + picoquic_cnx_t* cnx = picoquic_create_cnx(quic, picoquic_null_connection_id, picoquic_null_connection_id, addr, start_time, preferred_version, sni, alpn, 1); + + if (cnx != NULL) { + int ret; + + if (callback_fn != NULL) + cnx->callback_fn = callback_fn; + if (callback_ctx != NULL) + cnx->callback_ctx = callback_ctx; + ret = picoquic_start_client_cnx(cnx); + if (ret != 0) { + /* Cannot just do partial initialization! */ + picoquic_delete_cnx(cnx); + cnx = NULL; + } + } + + return cnx; +} + +int picoquic_start_client_cnx(picoquic_cnx_t * cnx) +{ + int ret = picoquic_initialize_tls_stream(cnx, picoquic_get_quic_time(cnx->quic)); + /* A remote session ticket may have been loaded as part of initializing TLS, + * and remote parameters may have been initialized to the initial value + * of the previous session. Apply these new parameters. */ + cnx->maxdata_remote = cnx->remote_parameters.initial_max_data; + cnx->max_stream_id_bidir_remote = cnx->remote_parameters.initial_max_stream_id_bidir; + cnx->max_stream_id_unidir_remote = cnx->remote_parameters.initial_max_stream_id_unidir; + + picoquic_reinsert_by_wake_time(cnx->quic, cnx, picoquic_get_quic_time(cnx->quic)); + + return ret; +} + +void picoquic_set_transport_parameters(picoquic_cnx_t * cnx, picoquic_tp_t const * tp) +{ + cnx->local_parameters = *tp; + + if (cnx->quic->mtu_max > 0) + { + cnx->local_parameters.max_packet_size = cnx->quic->mtu_max; + } + + /* Initialize local flow control variables to advertised values */ + + cnx->maxdata_local = ((uint64_t)cnx->local_parameters.initial_max_data); + cnx->max_stream_id_bidir_local = cnx->local_parameters.initial_max_stream_id_bidir; + cnx->max_stream_id_unidir_local = cnx->local_parameters.initial_max_stream_id_unidir; +} + +picoquic_tp_t const* picoquic_get_transport_parameters(picoquic_cnx_t* cnx, int get_local) +{ + return(get_local) ? &cnx->local_parameters : &cnx->remote_parameters; +} + +void picoquic_get_peer_addr(picoquic_cnx_t* cnx, struct sockaddr** addr) +{ + *addr = (struct sockaddr*)&cnx->path[0]->peer_addr; +} + +void picoquic_get_local_addr(picoquic_cnx_t* cnx, struct sockaddr** addr) +{ + *addr = (struct sockaddr*)&cnx->path[0]->local_addr; +} + +unsigned long picoquic_get_local_if_index(picoquic_cnx_t* cnx) +{ + return cnx->path[0]->if_index_dest; +} + +picoquic_connection_id_t picoquic_get_local_cnxid(picoquic_cnx_t* cnx) +{ + return cnx->path[0]->p_local_cnxid->cnx_id; +} + +picoquic_connection_id_t picoquic_get_remote_cnxid(picoquic_cnx_t* cnx) +{ + return cnx->path[0]->remote_cnxid; +} + +picoquic_connection_id_t picoquic_get_initial_cnxid(picoquic_cnx_t* cnx) +{ + return cnx->initial_cnxid; +} + +picoquic_connection_id_t picoquic_get_client_cnxid(picoquic_cnx_t* cnx) +{ + return (cnx->client_mode)?cnx->path[0]->p_local_cnxid->cnx_id : cnx->path[0]->remote_cnxid; +} + +picoquic_connection_id_t picoquic_get_server_cnxid(picoquic_cnx_t* cnx) +{ + return (cnx->client_mode) ? cnx->path[0]->remote_cnxid : cnx->path[0]->p_local_cnxid->cnx_id; +} + +picoquic_connection_id_t picoquic_get_logging_cnxid(picoquic_cnx_t* cnx) +{ + return cnx->initial_cnxid; +} + +uint64_t picoquic_get_cnx_start_time(picoquic_cnx_t* cnx) +{ + return cnx->start_time; +} + +picoquic_state_enum picoquic_get_cnx_state(picoquic_cnx_t* cnx) +{ + return cnx->cnx_state; +} + +uint64_t picoquic_is_0rtt_available(picoquic_cnx_t* cnx) +{ + return (cnx->crypto_context[picoquic_epoch_0rtt].aead_encrypt == NULL) ? 0 : 1; +} + +void picoquic_cnx_set_padding_policy(picoquic_cnx_t * cnx, uint32_t padding_multiple, uint32_t padding_minsize) +{ + cnx->padding_multiple = padding_multiple; + cnx->padding_minsize = padding_minsize; +} + +void picoquic_cnx_get_padding_policy(picoquic_cnx_t * cnx, uint32_t * padding_multiple, uint32_t * padding_minsize) +{ + *padding_multiple = cnx->padding_multiple; + *padding_minsize = cnx->padding_minsize; +} + +void picoquic_cnx_set_spinbit_policy(picoquic_cnx_t * cnx, picoquic_spinbit_version_enum spinbit_policy) +{ + cnx->spin_policy = spinbit_policy; +} + +void picoquic_cnx_set_pmtud_required(picoquic_cnx_t* cnx, int is_pmtud_required) +{ + cnx->is_pmtud_required = is_pmtud_required; +} + +/* + * Provide clock time + */ +uint64_t picoquic_current_time() +{ + uint64_t now; +#ifdef _WINDOWS + FILETIME ft; + /* + * The GetSystemTimeAsFileTime API returns the number + * of 100-nanosecond intervals since January 1, 1601 (UTC), + * in FILETIME format. + */ + GetSystemTimePreciseAsFileTime(&ft); + + /* + * Convert to plain 64 bit format, without making + * assumptions about the FILETIME structure alignment. + */ + now = ft.dwHighDateTime; + now <<= 32; + now |= ft.dwLowDateTime; + /* + * Convert units from 100ns to 1us + */ + now /= 10; + /* + * Account for microseconds elapsed between 1601 and 1970. + */ + now -= 11644473600000000ULL; +#else + struct timeval tv; + (void)gettimeofday(&tv, NULL); + now = (tv.tv_sec * 1000000ull) + tv.tv_usec; +#endif + return now; +} + +/* +* Get the same time simulation as used for TLS +*/ + +uint64_t picoquic_get_quic_time(picoquic_quic_t* quic) +{ + uint64_t now; + if (quic->p_simulated_time == NULL) { + now = picoquic_current_time(); + } + else { + now = *quic->p_simulated_time; + } + + return now; +} + +void picoquic_connection_id_callback(picoquic_quic_t * quic, picoquic_connection_id_t cnx_id_local, picoquic_connection_id_t cnx_id_remote, void * cnx_id_cb_data, picoquic_connection_id_t * cnx_id_returned) +{ + picoquic_connection_id_callback_ctx_t* ctx = (picoquic_connection_id_callback_ctx_t*)cnx_id_cb_data; + + quic->local_cnxid_length = ctx->cnx_id_val.id_len; + + /* Initialize with either random value or */ + memset(cnx_id_returned, 0, sizeof(picoquic_connection_id_t)); + if (ctx->cnx_id_select == picoquic_connection_id_remote) { + /* Keeping this for compatibility with old buggy version */ + cnx_id_local = cnx_id_remote; + } else { + /* setting value to random data */ + picoquic_public_random(cnx_id_local.id, quic->local_cnxid_length); + } + cnx_id_local.id_len = quic->local_cnxid_length; + + /* Apply substitution under mask */ + for (uint8_t i = 0; i < cnx_id_local.id_len; i++) { + cnx_id_returned->id[i] = (cnx_id_local.id[i] & ctx->cnx_id_mask.id[i]) | ctx->cnx_id_val.id[i]; + } + cnx_id_returned->id_len = quic->local_cnxid_length; + + /* Apply encryption if required */ + switch (ctx->cnx_id_select) { + case picoquic_connection_id_encrypt_basic: + /* encryption under mask */ + if (ctx->cid_enc == NULL) { + int ret = picoquic_cid_get_under_mask_ctx(&ctx->cid_enc, quic->reset_seed); + if (ret != 0) { + DBG_PRINTF("Cannot create CID encryption context, ret=%d\n", ret); + } + } + if (ctx->cid_enc != NULL) { + picoquic_cid_encrypt_under_mask(ctx->cid_enc, cnx_id_returned, &ctx->cnx_id_mask, cnx_id_returned); + } + break; + default: + /* Leave it unencrypted */ + break; + } +} + +picoquic_connection_id_callback_ctx_t * picoquic_connection_id_callback_create_ctx( + char const * select_type, char const * default_value_hex, char const * mask_hex) +{ + picoquic_connection_id_callback_ctx_t* ctx = (picoquic_connection_id_callback_ctx_t*) + malloc(sizeof(picoquic_connection_id_callback_ctx_t)); + + if (ctx != NULL) { + size_t lv, lm; + memset(ctx, 0, sizeof(picoquic_connection_id_callback_ctx_t)); + ctx->cnx_id_select = atoi(select_type); + /* TODO: find an alternative to parsing a 64 bit integer */ + lv = picoquic_parse_connection_id_hexa(default_value_hex, strlen(default_value_hex), &ctx->cnx_id_val); + lm = picoquic_parse_connection_id_hexa(mask_hex, strlen(mask_hex), &ctx->cnx_id_val); + + if (lm == 0 || lv == 0 || lm != lv) { + free(ctx); + ctx = NULL; + } + } + + return ctx; +} + +void picoquic_connection_id_callback_free_ctx(void * cnx_id_cb_data) +{ + picoquic_connection_id_callback_ctx_t* ctx = (picoquic_connection_id_callback_ctx_t*)cnx_id_cb_data; + + if (ctx != NULL && ctx->cid_enc != NULL) { + switch (ctx->cnx_id_select) { + case picoquic_connection_id_encrypt_basic: + /* encryption under mask */ + picoquic_cid_free_under_mask_ctx(ctx->cid_enc); + break; + default: + /* Guessing for the most common, assuming free will work... */ + picoquic_cid_free_under_mask_ctx(ctx->cid_enc); + break; + } + ctx->cid_enc = NULL; + } + free(cnx_id_cb_data); +} + +void picoquic_set_fuzz(picoquic_quic_t * quic, picoquic_fuzz_fn fuzz_fn, void * fuzz_ctx) +{ + quic->fuzz_fn = fuzz_fn; + quic->fuzz_ctx = fuzz_ctx; +} + +void picoquic_set_log_level(picoquic_quic_t* quic, int log_level) +{ + /* Only two level for now: log first 100 packets, or log everything. */ + quic->use_long_log = (log_level > 0) ? 1 : 0; +} + +void picoquic_set_random_initial(picoquic_quic_t* quic, int random_initial) +{ + /* If set, triggers randomization of initial PN numbers. */ + quic->random_initial = (random_initial > 0) ? 1 : 0; +} + +void picoquic_set_packet_train_mode(picoquic_quic_t* quic, int train_mode) +{ + /* TODO: consider setting high water mark for pacing. */ + /* If set, wait until pacing bucket is full enough to allow further transmissions. */ + quic->packet_train_mode = (train_mode > 0) ? 1 : 0; +} + +void picoquic_set_padding_policy(picoquic_quic_t* quic, uint32_t padding_min_size, uint32_t padding_multiple) +{ + quic->padding_minsize_default = padding_min_size; + quic->padding_multiple_default = padding_multiple; +} + +int picoquic_set_default_connection_id_length(picoquic_quic_t* quic, uint8_t cid_length) +{ + int ret = 0; + + if (cid_length != quic->local_cnxid_length) { + if (cid_length > PICOQUIC_CONNECTION_ID_MAX_SIZE) { + ret = PICOQUIC_ERROR_CNXID_CHECK; + } + else if (quic->cnx_list != NULL) { + ret = PICOQUIC_ERROR_CANNOT_CHANGE_ACTIVE_CONTEXT; + } + else { + quic->local_cnxid_length = cid_length; + } + } + + return ret; +} + +void picoquic_set_mtu_max(picoquic_quic_t* quic, uint32_t mtu_max) +{ + quic->mtu_max = mtu_max; +} + +void picoquic_set_alpn_select_fn(picoquic_quic_t* quic, picoquic_alpn_select_fn alpn_select_fn) +{ + if (quic->default_alpn != NULL) { + free((void *)quic->default_alpn); + quic->default_alpn = NULL; + } + quic->alpn_select_fn = alpn_select_fn; +} + +void picoquic_set_default_callback(picoquic_quic_t* quic, + picoquic_stream_data_cb_fn callback_fn, void* callback_ctx) +{ + quic->default_callback_fn = callback_fn; + quic->default_callback_ctx = callback_ctx; +} + +void picoquic_set_callback(picoquic_cnx_t* cnx, + picoquic_stream_data_cb_fn callback_fn, void* callback_ctx) +{ + cnx->callback_fn = callback_fn; + cnx->callback_ctx = callback_ctx; +} + +picoquic_stream_data_cb_fn picoquic_get_default_callback_function(picoquic_quic_t* quic) +{ + return quic->default_callback_fn; +} + +void * picoquic_get_default_callback_context(picoquic_quic_t* quic) +{ + return quic->default_callback_ctx; +} + +picoquic_stream_data_cb_fn picoquic_get_callback_function(picoquic_cnx_t * cnx) +{ + return cnx->callback_fn; +} + +void * picoquic_get_callback_context(picoquic_cnx_t * cnx) +{ + return cnx->callback_ctx; +} + +picoquic_misc_frame_header_t* picoquic_create_misc_frame(const uint8_t* bytes, size_t length, int is_pure_ack) +{ + size_t l_alloc = sizeof(picoquic_misc_frame_header_t) + length; + + if (l_alloc < sizeof(picoquic_misc_frame_header_t)) { + return NULL; + } + else { + picoquic_misc_frame_header_t* head = (picoquic_misc_frame_header_t*)malloc(l_alloc); + if (head != NULL) { + memset(head, 0, sizeof(picoquic_misc_frame_header_t)); + head->length = length; + head->is_pure_ack = is_pure_ack; + memcpy(((uint8_t *)head) + sizeof(picoquic_misc_frame_header_t), bytes, length); + } + return head; + } +} + +int picoquic_queue_misc_or_dg_frame(picoquic_cnx_t * cnx, picoquic_misc_frame_header_t** first, + picoquic_misc_frame_header_t** last, const uint8_t* bytes, size_t length, int is_pure_ack) +{ + int ret = 0; + picoquic_misc_frame_header_t* misc_frame = picoquic_create_misc_frame(bytes, length, is_pure_ack); + + if (misc_frame == NULL) { + ret = PICOQUIC_ERROR_MEMORY; + } else { + if (*last == NULL) { + *first = misc_frame; + *last = misc_frame; + } + else { + (*last)->next_misc_frame = misc_frame; + misc_frame->previous_misc_frame = *last; + *last = misc_frame; + } + } + + picoquic_reinsert_by_wake_time(cnx->quic, cnx, picoquic_get_quic_time(cnx->quic)); + + return ret; +} + +int picoquic_queue_misc_frame(picoquic_cnx_t* cnx, const uint8_t* bytes, size_t length, int is_pure_ack) +{ + return picoquic_queue_misc_or_dg_frame(cnx, &cnx->first_misc_frame, &cnx->last_misc_frame, bytes, length, is_pure_ack); +} + +void picoquic_delete_misc_or_dg(picoquic_misc_frame_header_t** first, picoquic_misc_frame_header_t** last, picoquic_misc_frame_header_t* frame) +{ + if (frame->next_misc_frame) { + frame->next_misc_frame->previous_misc_frame = frame->previous_misc_frame; + } + else { + *last = frame->previous_misc_frame; + } + + if (frame->previous_misc_frame) { + frame->previous_misc_frame->next_misc_frame = frame->next_misc_frame; + } + else { + *first = frame->next_misc_frame; + } + + free(frame); +} + +void picoquic_reset_packet_context(picoquic_cnx_t* cnx, + picoquic_packet_context_enum pc) +{ + /* TODO: special case for 0-RTT packets! */ + picoquic_packet_context_t * pkt_ctx = &cnx->pkt_ctx[pc]; + + while (pkt_ctx->retransmit_newest != NULL) { + (void)picoquic_dequeue_retransmit_packet(cnx, pkt_ctx->retransmit_newest, 1); + } + + while (pkt_ctx->retransmitted_newest != NULL) { + picoquic_dequeue_retransmitted_packet(cnx, pkt_ctx->retransmitted_newest); + } + + pkt_ctx->retransmitted_oldest = NULL; + + while (pkt_ctx->first_sack_item.next_sack != NULL) { + picoquic_sack_item_t * next = pkt_ctx->first_sack_item.next_sack; + pkt_ctx->first_sack_item.next_sack = next->next_sack; + free(next); + } + + pkt_ctx->first_sack_item.start_of_sack_range = (uint64_t)((int64_t)-1); + pkt_ctx->first_sack_item.end_of_sack_range = 0; + /* Reset the ECN data */ + pkt_ctx->ecn_ect0_total_local = 0; + pkt_ctx->ecn_ect1_total_local = 0; + pkt_ctx->ecn_ce_total_local = 0; + pkt_ctx->ecn_ect0_total_remote = 0; + pkt_ctx->ecn_ect1_total_remote = 0; + pkt_ctx->ecn_ce_total_remote = 0; +} + +/* +* Reset the connection after an incoming retry packet. +* +* Can only happen after sending the client init packet. +* Result of reset: +* +* - connection ID is not changed. +* - sequence number is not changed. +* - all queued 0-RTT retransmission will be considered lost (to do with 0-RTT) +* - Client Initial packet is considered lost, free. A new one will have to be formatted. +* - TLS stream is reset, all TLS data is freed. +* - TLS API is called again. +* - State changes. +*/ + +int picoquic_reset_cnx(picoquic_cnx_t* cnx, uint64_t current_time) +{ + int ret = 0; + + /* Delete the packets queued for retransmission */ + for (picoquic_packet_context_enum pc = 0; + pc < picoquic_nb_packet_context; pc++) { + /* Do not reset the application context, in order to keep the 0-RTT + * packets, and to keep using the same sequence number space in + * the new connection */ + if (pc != picoquic_packet_context_application) { + picoquic_reset_packet_context(cnx, pc); + } + } + + /* Reset the crypto stream */ + for (int epoch = 0; epoch < PICOQUIC_NUMBER_OF_EPOCHS; epoch++) { + picoquic_clear_stream(&cnx->tls_stream[epoch]); + cnx->tls_stream[epoch].consumed_offset = 0; + cnx->tls_stream[epoch].fin_offset = 0; + cnx->tls_stream[epoch].sent_offset = 0; + /* No need to reset the state flags, are they are not used for the crypto stream */ + } + + for (int k = 0; k < 4; k++) { + picoquic_crypto_context_free(&cnx->crypto_context[k]); + } + + picoquic_crypto_context_free(&cnx->crypto_context_new); + + ret = picoquic_setup_initial_traffic_keys(cnx); + + /* Reset the TLS context, Re-initialize the tls connection */ + if (cnx->tls_ctx != NULL) { + picoquic_tlscontext_free(cnx->tls_ctx); + cnx->tls_ctx = NULL; + } + + picoquic_log_new_connection(cnx); + + if (ret == 0) { + ret = picoquic_tlscontext_create(cnx->quic, cnx, current_time); + } + if (ret == 0) { + ret = picoquic_initialize_tls_stream(cnx, current_time); + } + + return ret; +} + +int picoquic_connection_error(picoquic_cnx_t* cnx, uint16_t local_error, uint64_t frame_type) +{ + if (cnx->cnx_state == picoquic_state_ready || + cnx->cnx_state == picoquic_state_client_ready_start || cnx->cnx_state == picoquic_state_server_false_start) { + if (local_error > PICOQUIC_ERROR_CLASS) { + cnx->local_error = PICOQUIC_TRANSPORT_INTERNAL_ERROR; + } + else { + cnx->local_error = local_error; + } + cnx->cnx_state = picoquic_state_disconnecting; + + picoquic_log_app_message(cnx, "Protocol error 0x%x", local_error); + DBG_PRINTF("Protocol error (%x)", local_error); + } else if (cnx->cnx_state < picoquic_state_server_false_start) { + if (cnx->cnx_state != picoquic_state_handshake_failure && + cnx->cnx_state != picoquic_state_handshake_failure_resend) { + cnx->local_error = local_error; + cnx->cnx_state = picoquic_state_handshake_failure; + + DBG_PRINTF("Protocol error %x", local_error); + } + } + + cnx->offending_frame_type = frame_type; + + return PICOQUIC_ERROR_DETECTED; +} + +int picoquic_start_key_rotation(picoquic_cnx_t* cnx) +{ + int ret = 0; + + /* Verify that a packet of the previous rotation was acked */ + if (cnx->cnx_state != picoquic_state_ready || + cnx->crypto_epoch_sequence > + cnx->pkt_ctx[picoquic_packet_context_application].first_sack_item.end_of_sack_range) { + ret = PICOQUIC_ERROR_KEY_ROTATION_NOT_READY; + } + else { + ret = picoquic_compute_new_rotated_keys(cnx); + } + + if (ret == 0) { + picoquic_apply_rotated_keys(cnx, 1); + picoquic_crypto_context_free(&cnx->crypto_context_old); + cnx->crypto_epoch_sequence = cnx->pkt_ctx[picoquic_packet_context_application].send_sequence; + } + + return ret; +} + +void picoquic_delete_sooner_packets(picoquic_cnx_t* cnx) +{ + picoquic_stateless_packet_t* packet = cnx->first_sooner; + + while (packet != NULL) { + picoquic_stateless_packet_t* next_packet = packet->next_packet; + picoquic_delete_stateless_packet(packet); + packet = next_packet; + } + cnx->first_sooner = NULL; +} + +void picoquic_delete_cnx(picoquic_cnx_t* cnx) +{ + picoquic_cnxid_stash_t* stashed_cnxid; + + if (cnx != NULL) { + picoquic_log_close_connection(cnx); + + if (cnx->is_half_open && cnx->quic->current_number_half_open > 0) { + cnx->quic->current_number_half_open--; + cnx->is_half_open = 0; + } + + if (cnx->cnx_state < picoquic_state_disconnected) { + /* Give the application a chance to clean up its state */ + cnx->cnx_state = picoquic_state_disconnected; + if (cnx->callback_fn) { + (void)(cnx->callback_fn)(cnx, 0, NULL, 0, picoquic_callback_close, cnx->callback_ctx, NULL); + } + } + + if (cnx->alpn != NULL) { + free((void*)cnx->alpn); + cnx->alpn = NULL; + } + + if (cnx->sni != NULL) { + free((void*)cnx->sni); + cnx->sni = NULL; + } + + if (cnx->retry_token != NULL) { + free(cnx->retry_token); + cnx->retry_token = NULL; + } + + picoquic_delete_sooner_packets(cnx); + + picoquic_remove_cnx_from_list(cnx); + picoquic_remove_cnx_from_wake_list(cnx); + + for (int i = 0; i < 4; i++) { + picoquic_crypto_context_free(&cnx->crypto_context[i]); + } + + picoquic_crypto_context_free(&cnx->crypto_context_new); + + for (picoquic_packet_context_enum pc = 0; + pc < picoquic_nb_packet_context; pc++) { + picoquic_reset_packet_context(cnx, pc); + } + + while (cnx->first_misc_frame != NULL) { + picoquic_delete_misc_or_dg(&cnx->first_misc_frame, &cnx->last_misc_frame, cnx->first_misc_frame); + } + + while (cnx->first_datagram != NULL) { + picoquic_delete_misc_or_dg(&cnx->first_datagram, &cnx->last_datagram, cnx->first_datagram); + } + + for (int epoch = 0; epoch < PICOQUIC_NUMBER_OF_EPOCHS; epoch++) { + picoquic_clear_stream(&cnx->tls_stream[epoch]); + } + + picosplay_empty_tree(&cnx->stream_tree); + + if (cnx->tls_ctx != NULL) { + picoquic_tlscontext_free(cnx->tls_ctx); + cnx->tls_ctx = NULL; + } + + if (cnx->path != NULL) + { + while (cnx->nb_paths > 0) { + picoquic_delete_path(cnx, cnx->nb_paths - 1); + } + + free(cnx->path); + cnx->path = NULL; + } + + while (cnx->local_cnxid_first != NULL) { + picoquic_delete_local_cnxid(cnx, cnx->local_cnxid_first); + } + + while ((stashed_cnxid = picoquic_dequeue_cnxid_stash(cnx)) != NULL) { + free(stashed_cnxid); + } + + free(cnx); + } +} + +int picoquic_is_handshake_error(uint16_t error_code) +{ + return ((error_code & 0xFF00) == PICOQUIC_TRANSPORT_CRYPTO_ERROR(0) || + error_code == PICOQUIC_TLS_HANDSHAKE_FAILED); +} + +/* Context retrieval functions */ +picoquic_cnx_t* picoquic_cnx_by_id(picoquic_quic_t* quic, picoquic_connection_id_t cnx_id) +{ + picoquic_cnx_t* ret = NULL; + picohash_item* item; + picoquic_cnx_id_key_t key; + + memset(&key, 0, sizeof(key)); + key.cnx_id = cnx_id; + + item = picohash_retrieve(quic->table_cnx_by_id, &key); + + if (item != NULL) { + ret = ((picoquic_cnx_id_key_t*)item->key)->cnx; + } + return ret; +} + +picoquic_cnx_t* picoquic_cnx_by_net(picoquic_quic_t* quic, const struct sockaddr* addr) +{ + picoquic_cnx_t* ret = NULL; + picohash_item* item; + picoquic_net_id_key_t key; + + memset(&key, 0, sizeof(key)); + picoquic_store_addr(&key.saddr, addr); + + item = picohash_retrieve(quic->table_cnx_by_net, &key); + + if (item != NULL) { + ret = ((picoquic_net_id_key_t*)item->key)->cnx; + } + return ret; +} + +picoquic_cnx_t* picoquic_cnx_by_icid(picoquic_quic_t* quic, picoquic_connection_id_t* icid, + const struct sockaddr* addr) +{ + picoquic_cnx_t* ret = NULL; + picohash_item* item; + picoquic_net_icid_key_t key; + + memset(&key, 0, sizeof(key)); + picoquic_store_addr(&key.saddr, addr); + key.icid = *icid; + + item = picohash_retrieve(quic->table_cnx_by_icid, &key); + + if (item != NULL) { + ret = ((picoquic_net_icid_key_t*)item->key)->cnx; + } + return ret; +} + +picoquic_cnx_t* picoquic_cnx_by_secret(picoquic_quic_t* quic, const uint8_t* reset_secret, const struct sockaddr* addr) +{ + picoquic_cnx_t* ret = NULL; + picohash_item* item; + picoquic_net_secret_key_t key; + + memset(&key, 0, sizeof(key)); + picoquic_store_addr(&key.saddr, addr); + memcpy(key.reset_secret, reset_secret, PICOQUIC_RESET_SECRET_SIZE); + + item = picohash_retrieve(quic->table_cnx_by_secret, &key); + + if (item != NULL) { + ret = ((picoquic_net_secret_key_t*)item->key)->cnx; + } + return ret; +} + +/* Get congestion control algorithm by name */ +picoquic_congestion_algorithm_t const* picoquic_get_congestion_algorithm(char const* alg_name) +{ + picoquic_congestion_algorithm_t const* alg = NULL; + if (alg_name != NULL) { + if (strcmp(alg_name, "reno") == 0) { + alg = picoquic_newreno_algorithm; + } + else if (strcmp(alg_name, "cubic") == 0) { + alg = picoquic_cubic_algorithm; + } + else if (strcmp(alg_name, "dcubic") == 0) { + alg = picoquic_dcubic_algorithm; + } + else if (strcmp(alg_name, "fast") == 0) { + alg = picoquic_fastcc_algorithm; + } + else if (strcmp(alg_name, "bbr") == 0) { + alg = picoquic_bbr_algorithm; + } + else { + alg = NULL; + } + } + return alg; +} +/* + * Set or reset the congestion control algorithm + */ + +void picoquic_set_default_congestion_algorithm(picoquic_quic_t* quic, picoquic_congestion_algorithm_t const* alg) +{ + quic->default_congestion_alg = alg; +} + +void picoquic_set_default_congestion_algorithm_by_name(picoquic_quic_t* quic, char const * alg_name) +{ + quic->default_congestion_alg = picoquic_get_congestion_algorithm(alg_name); +} + +/* + * Set the optimistic ack policy + */ + +void picoquic_set_optimistic_ack_policy(picoquic_quic_t* quic, uint32_t sequence_hole_pseudo_period) +{ + quic->sequence_hole_pseudo_period = sequence_hole_pseudo_period; +} + +void picoquic_set_congestion_algorithm(picoquic_cnx_t* cnx, picoquic_congestion_algorithm_t const* alg) +{ + if (cnx->congestion_alg != NULL) { + if (cnx->path != NULL) { + for (int i = 0; i < cnx->nb_paths; i++) { + cnx->congestion_alg->alg_delete(cnx->path[i]); + } + } + } + + cnx->congestion_alg = alg; + + if (cnx->congestion_alg != NULL) { + if (cnx->path != NULL) { + for (int i = 0; i < cnx->nb_paths; i++) { + cnx->congestion_alg->alg_init(cnx->path[i], picoquic_get_quic_time(cnx->quic)); + } + } + } +} + +void picoquic_subscribe_pacing_rate_updates(picoquic_cnx_t* cnx, uint64_t decrease_threshold, uint64_t increase_threshold) +{ + cnx->pacing_decrease_threshold = decrease_threshold; + cnx->pacing_increase_threshold = increase_threshold; + cnx->is_pacing_update_requested = (decrease_threshold != UINT64_MAX || increase_threshold != UINT64_MAX); +} + +uint64_t picoquic_get_pacing_rate(picoquic_cnx_t* cnx) +{ + return cnx->path[0]->pacing_rate; +} + +uint64_t picoquic_get_cwin(picoquic_cnx_t* cnx) +{ + return cnx->path[0]->cwin; +} + +uint64_t picoquic_get_rtt(picoquic_cnx_t* cnx) +{ + return cnx->path[0]->smoothed_rtt; +} + +int picoquic_set_local_addr(picoquic_cnx_t* cnx, struct sockaddr* addr) +{ + int ret = 0; + + if (cnx != NULL && cnx->path[0] != NULL && cnx->path[0]->local_addr.ss_family == 0) { + picoquic_store_addr(&cnx->path[0]->local_addr, addr); + ret = (cnx->path[0]->local_addr.ss_family == 0) ? -1 : 0; + } + else { + ret = -1; + } + + return ret; +} + +void picoquic_enable_keep_alive(picoquic_cnx_t* cnx, uint64_t interval) +{ + if (interval == 0) { + /* Use the negotiated value */ + uint64_t idle_timeout = cnx->idle_timeout; + if (idle_timeout == 0) { + /* Idle timeout is only initialized after parameters are negotiated */ + idle_timeout = cnx->local_parameters.idle_timeout * 1000ull; + } + /* Ensure at least 3 PTO*/ + if (idle_timeout < 3 * cnx->path[0]->retransmit_timer) { + idle_timeout = 3 * cnx->path[0]->retransmit_timer; + } + /* set interval to half that value */ + cnx->keep_alive_interval = idle_timeout / 2; + } else { + cnx->keep_alive_interval = interval; + } +} + +void picoquic_disable_keep_alive(picoquic_cnx_t* cnx) +{ + cnx->keep_alive_interval = 0; +} + +int picoquic_set_verify_certificate_callback(picoquic_quic_t* quic, picoquic_verify_certificate_cb_fn cb, void* ctx, + picoquic_free_verify_certificate_ctx free_fn) { + picoquic_dispose_verify_certificate_callback(quic, quic->verify_certificate_callback_fn != NULL); + + quic->verify_certificate_callback_fn = cb; + quic->free_verify_certificate_callback_fn = free_fn; + quic->verify_certificate_ctx = ctx; + + return picoquic_enable_custom_verify_certificate_callback(quic); +} + +int picoquic_is_client(picoquic_cnx_t* cnx) +{ + return cnx->client_mode; +} + +int picoquic_get_local_error(picoquic_cnx_t* cnx) +{ + return cnx->local_error; +} + +int picoquic_get_remote_error(picoquic_cnx_t* cnx) +{ + return cnx->remote_error; +} + +uint64_t picoquic_get_remote_stream_error(picoquic_cnx_t* cnx, uint64_t stream_id) +{ + uint64_t remote_error = 0; + picoquic_stream_head_t* stream = picoquic_find_stream(cnx, stream_id); + if (stream != NULL) { + remote_error = stream->remote_error; + } + return remote_error; +} + +uint64_t picoquic_get_data_sent(picoquic_cnx_t* cnx) +{ + return cnx->data_sent; +} + +uint64_t picoquic_get_data_received(picoquic_cnx_t* cnx) +{ + return cnx->data_received; +} + +void picoquic_set_client_authentication(picoquic_quic_t* quic, int client_authentication) { + picoquic_tls_set_client_authentication(quic, client_authentication); +} + +/* Load balancer support is defined in https://datatracker.ietf.org/doc/draft-ietf-quic-load-balancers/ + * The draft defines methods for encoding a server ID in a connection identifier, and optionally + * obfuscating or encrypting the CID value. The CID are generated by the individual servers, + * based on configuration options provided by the load balancer. The draft also defines + * methods for generating retry tokens either by a protection box colocated with the + * load balancer, or at the individual server, with methods for letting individual + * servers retrieve information from the tokens. + */ + +static void picoquic_lb_compat_cid_generate_clear(picoquic_quic_t* quic, + picoquic_load_balancer_cid_context_t * lb_ctx, picoquic_connection_id_t* cnx_id_returned) +{ + cnx_id_returned->id[0] = lb_ctx->first_byte; + memcpy(cnx_id_returned->id + 1, lb_ctx->server_id, lb_ctx->server_id_length); +} + +static void picoquic_lb_compat_cid_generate_obfuscated(picoquic_quic_t* quic, + picoquic_load_balancer_cid_context_t* lb_ctx, picoquic_connection_id_t* cnx_id_returned) +{ + uint64_t obfuscation_max = (UINT64_MAX - lb_ctx->server_id64) / lb_ctx->divider; + uint64_t obfuscator = 0; + uint64_t obfuscated; + + cnx_id_returned->id[0] = lb_ctx->first_byte; + for (size_t i = 0; i < lb_ctx->routing_bits_length; i++) { + obfuscator <<= 8; + obfuscator += cnx_id_returned->id[i + 1]; + obfuscator %= obfuscation_max; + } + obfuscated = obfuscator* lb_ctx->divider; + obfuscated += lb_ctx->server_id64; + + for (size_t i = 0; i < lb_ctx->routing_bits_length; i++) { + size_t j = lb_ctx->routing_bits_length - i; /* varies from lb_ctx->routing_bits_length to 1 */ + cnx_id_returned->id[j] = (uint8_t)obfuscated; + obfuscated >>= 8; + } +} + +static void picoquic_lb_compat_cid_one_pass_stream(void * enc_ctx, uint8_t * nonce, size_t nonce_length, uint8_t * target, size_t target_length) +{ + uint8_t mask[16]; + /* Set the obfuscation value */ + memset(mask, 0, sizeof(mask)); + memcpy(mask, nonce, nonce_length); + /* Encrypt with ECB */ + picoquic_aes128_ecb_encrypt(enc_ctx, mask, mask, sizeof(mask)); + /* Apply the mask */ + for (size_t i = 0; i < target_length; i++) { + target[i] ^= mask[i]; + } +} + +static void picoquic_lb_compat_cid_generate_stream_cipher(picoquic_quic_t* quic, + picoquic_load_balancer_cid_context_t* lb_ctx, picoquic_connection_id_t* cnx_id_returned) +{ + size_t id_offset = ((size_t)1) + lb_ctx->nonce_length; + /* Prepare a clear text server ID */ + cnx_id_returned->id[0] = lb_ctx->first_byte; + memcpy(cnx_id_returned->id + id_offset, lb_ctx->server_id, lb_ctx->server_id_length); + /* First pass -- obtain intermediate server ID */ + picoquic_lb_compat_cid_one_pass_stream(lb_ctx->cid_encryption_context, cnx_id_returned->id + 1, lb_ctx->nonce_length, + cnx_id_returned->id + id_offset, lb_ctx->server_id_length); + /* Second pass -- obtain encrypted nonce */ + picoquic_lb_compat_cid_one_pass_stream(lb_ctx->cid_encryption_context, + cnx_id_returned->id + id_offset, lb_ctx->server_id_length, + cnx_id_returned->id + 1, lb_ctx->nonce_length); + /* Third pass -- obtain encrypted server-id */ + picoquic_lb_compat_cid_one_pass_stream(lb_ctx->cid_encryption_context, cnx_id_returned->id + 1, lb_ctx->nonce_length, + cnx_id_returned->id + id_offset, lb_ctx->server_id_length); +} + +static void picoquic_lb_compat_cid_generate_block_cipher(picoquic_quic_t* quic, + picoquic_load_balancer_cid_context_t* lb_ctx, picoquic_connection_id_t* cnx_id_returned) +{ + cnx_id_returned->id[0] = lb_ctx->first_byte; + /* Copy the server ID */ + memcpy(cnx_id_returned->id + 1, lb_ctx->server_id, lb_ctx->server_id_length); + /* Set the zeropad value */ + memset(cnx_id_returned->id + 1 + lb_ctx->server_id_length, 0, lb_ctx->zero_pad_length); + /* encrypt 16 bytes */ + picoquic_aes128_ecb_encrypt(lb_ctx->cid_encryption_context, cnx_id_returned->id + 1, cnx_id_returned->id + 1, 16); + cnx_id_returned->id[0] = lb_ctx->first_byte; +} + +void picoquic_lb_compat_cid_generate(picoquic_quic_t* quic, picoquic_connection_id_t cnx_id_local, + picoquic_connection_id_t cnx_id_remote, void* cnx_id_cb_data, picoquic_connection_id_t* cnx_id_returned) +{ + picoquic_load_balancer_cid_context_t* lb_ctx = (picoquic_load_balancer_cid_context_t*)cnx_id_cb_data; +#ifdef _WINDOWS + UNREFERENCED_PARAMETER(cnx_id_local); + UNREFERENCED_PARAMETER(cnx_id_remote); +#endif + switch (lb_ctx->method) { + case picoquic_load_balancer_cid_clear: + picoquic_lb_compat_cid_generate_clear(quic, lb_ctx, cnx_id_returned); + break; + case picoquic_load_balancer_cid_obfuscated: + picoquic_lb_compat_cid_generate_obfuscated(quic, lb_ctx, cnx_id_returned); + break; + case picoquic_load_balancer_cid_stream_cipher: + picoquic_lb_compat_cid_generate_stream_cipher(quic, lb_ctx, cnx_id_returned); + break; + case picoquic_load_balancer_cid_block_cipher: + picoquic_lb_compat_cid_generate_block_cipher(quic, lb_ctx, cnx_id_returned); + break; + default: + /* Error, unknown method */ + break; + } +} + +static uint64_t picoquic_lb_compat_cid_verify_clear(picoquic_quic_t* quic, + picoquic_load_balancer_cid_context_t* lb_ctx, picoquic_connection_id_t const* cnx_id) +{ + uint64_t s_id64 = 0; + + for (size_t i = 0; i < lb_ctx->server_id_length; i++) { + s_id64 <<= 8; + s_id64 += cnx_id->id[i + 1]; + } + + return s_id64; +} + +static uint64_t picoquic_lb_compat_cid_verify_obfuscated(picoquic_quic_t* quic, + picoquic_load_balancer_cid_context_t* lb_ctx, picoquic_connection_id_t const* cnx_id) +{ + uint64_t s_id64 = 0; + + for (size_t i = 0; i < lb_ctx->routing_bits_length; i++) { + s_id64 <<= 8; + s_id64 += cnx_id->id[i + 1]; + s_id64 %= lb_ctx->divider; + } + + return s_id64; +} + +static uint64_t picoquic_lb_compat_cid_verify_stream_cipher(picoquic_quic_t* quic, + picoquic_load_balancer_cid_context_t* lb_ctx, picoquic_connection_id_t const* cnx_id) +{ + size_t id_offset = ((size_t)1) + lb_ctx->nonce_length; + uint64_t s_id64 = 0; + picoquic_connection_id_t target = *cnx_id; + /* First pass -- obtain intermediate server ID */ + picoquic_lb_compat_cid_one_pass_stream(lb_ctx->cid_encryption_context, target.id + 1, lb_ctx->nonce_length, + target.id + id_offset, lb_ctx->server_id_length); + /* Second pass -- obtain nonce */ + picoquic_lb_compat_cid_one_pass_stream(lb_ctx->cid_encryption_context, + target.id + id_offset, lb_ctx->server_id_length, target.id + 1, lb_ctx->nonce_length); + /* First pass -- obtain server-id */ + picoquic_lb_compat_cid_one_pass_stream(lb_ctx->cid_encryption_context, target.id + 1, lb_ctx->nonce_length, + target.id + id_offset, lb_ctx->server_id_length); + + /* decode the server ID */ + for (size_t i = 0; i < lb_ctx->server_id_length; i++) { + s_id64 <<= 8; + s_id64 += target.id[id_offset + i]; + } + + return s_id64; +} + +static uint64_t picoquic_lb_compat_cid_verify_block_cipher(picoquic_quic_t* quic, + picoquic_load_balancer_cid_context_t* lb_ctx, picoquic_connection_id_t const* cnx_id) +{ + uint8_t decoded[16]; + uint64_t s_id64 = 0; + + /* decrypt 16 bytes */ + picoquic_aes128_ecb_encrypt(lb_ctx->cid_decryption_context, decoded, cnx_id->id + 1, 16); + + /* Check that the nonce is all zeros */ + for (size_t i = 0; i < lb_ctx->zero_pad_length; i++) { + if (decoded[i + lb_ctx->server_id_length] != 0) { + s_id64 = UINT64_MAX; + break; + } + } + /* Decode the server ID */ + if (s_id64 == 0) { + for (size_t i = 0; i < lb_ctx->server_id_length; i++) { + s_id64 <<= 8; + s_id64 += decoded[i]; + } + } + + return s_id64; +} + +uint64_t picoquic_lb_compat_cid_verify(picoquic_quic_t* quic, void* cnx_id_cb_data, picoquic_connection_id_t const* cnx_id) +{ + picoquic_load_balancer_cid_context_t* lb_ctx = (picoquic_load_balancer_cid_context_t*)cnx_id_cb_data; + uint64_t server_id64; + + if (cnx_id->id_len != lb_ctx->connection_id_length) { + server_id64 = UINT64_MAX; + } + else { + switch (lb_ctx->method) { + case picoquic_load_balancer_cid_clear: + server_id64 = picoquic_lb_compat_cid_verify_clear(quic, lb_ctx, cnx_id); + break; + case picoquic_load_balancer_cid_obfuscated: + server_id64 = picoquic_lb_compat_cid_verify_obfuscated(quic, lb_ctx, cnx_id); + break; + case picoquic_load_balancer_cid_stream_cipher: + server_id64 = picoquic_lb_compat_cid_verify_stream_cipher(quic, lb_ctx, cnx_id); + break; + case picoquic_load_balancer_cid_block_cipher: + server_id64 = picoquic_lb_compat_cid_verify_block_cipher(quic, lb_ctx, cnx_id); + break; + default: + /* Error, unknown method */ + server_id64 = UINT64_MAX; + break; + } + } + + return server_id64; +} + +int picoquic_lb_compat_cid_config(picoquic_quic_t* quic, picoquic_load_balancer_config_t * lb_config) +{ + int ret = 0; + + if (quic->cnx_list != NULL && quic->local_cnxid_length != lb_config->connection_id_length) { + /* Error. Changing the CID length now will break existing connections */ + ret = -1; + } + else if (quic->cnx_id_callback_fn != NULL && quic->cnx_id_callback_ctx != NULL){ + /* Error. Some other CID generation is configured, cannot be changed */ + ret = -1; + } + else { + /* Verify that the method is supported and the parameters are compatible. + * If valid, configure the connection ID generation */ + if (lb_config->connection_id_length > PICOQUIC_CONNECTION_ID_MAX_SIZE) { + ret = -1; + } + else { + switch (lb_config->method) { + case picoquic_load_balancer_cid_clear: + /* Require at least 2 bytes to make the CID unique */ + if (lb_config->server_id_length + 1 + 2 > lb_config->connection_id_length) { + ret = -1; + } + break; + case picoquic_load_balancer_cid_obfuscated: + /* Require at least 2 bytes to obfuscate the server CID, + * cannot handle undivided values larger than 8 bytes */ + if (lb_config->routing_bits_length + 1 > lb_config->connection_id_length || + lb_config->server_id_length + 2 > lb_config->routing_bits_length || + lb_config->routing_bits_length > 8 || + lb_config->divider == 0) { + ret = -1; + } + break; + case picoquic_load_balancer_cid_stream_cipher: + /* Nonce length must be 8 to 16 bytes, CID should be long enough */ + if (lb_config->nonce_length < 8 || lb_config->nonce_length > 16 || + lb_config->nonce_length + lb_config->server_id_length + 1 > lb_config->connection_id_length) { + ret = -1; + } + break; + case picoquic_load_balancer_cid_block_cipher: + /* CID should include a whole AES-ECB block, + * there should be at least 2 bytes available for uniqueness, + * zero padding length should be 4 bytes for security */ + if (lb_config->connection_id_length < 17 || + lb_config->server_id_length + lb_config->zero_pad_length + 1 + 2 > lb_config->connection_id_length || + lb_config->zero_pad_length < 4 ) { + ret = -1; + } + break; + default: + /* Error, unknown method */ + ret = -1; + break; + } + } + if (ret == 0) { + /* Create a copy */ + picoquic_load_balancer_cid_context_t* lb_ctx = (picoquic_load_balancer_cid_context_t*)malloc(sizeof(picoquic_load_balancer_cid_context_t)); + + if (lb_ctx == NULL) { + ret = -1; + } + else { + /* if allocated, create the necessary encryption contexts or variables */ + uint64_t s_id64 = lb_config->server_id64; + memset(lb_ctx, 0, sizeof(picoquic_load_balancer_cid_context_t)); + lb_ctx->method = lb_config->method; + lb_ctx->server_id_length = lb_config->server_id_length; + lb_ctx->routing_bits_length = lb_config->routing_bits_length; + lb_ctx->nonce_length = lb_config->nonce_length; + lb_ctx->zero_pad_length = lb_config->zero_pad_length; + lb_ctx->connection_id_length = lb_config->connection_id_length; + lb_ctx->first_byte = lb_config->first_byte; + lb_ctx->server_id64 = lb_config->server_id64; + lb_ctx->divider = lb_config->divider; + lb_ctx->cid_encryption_context = NULL; + lb_ctx->cid_decryption_context = NULL; + /* Compute the server ID bytes and set encryption contexts */ + for (size_t i = 0; i < lb_ctx->server_id_length; i++) { + size_t j = lb_ctx->server_id_length - i - 1; + lb_ctx->server_id[j] = (uint8_t)s_id64; + s_id64 >>= 8; + } + if (s_id64 != 0) { + /* Server ID not long enough to encode actual value */ + ret = -1; + } else if (lb_config->method == picoquic_load_balancer_cid_stream_cipher || + lb_config->method == picoquic_load_balancer_cid_block_cipher) { + lb_ctx->cid_encryption_context = picoquic_aes128_ecb_create(1, lb_config->cid_encryption_key); + if (lb_ctx->cid_encryption_context == NULL) { + ret = -1; + } + else if (lb_config->method == picoquic_load_balancer_cid_block_cipher) { + lb_ctx->cid_decryption_context = picoquic_aes128_ecb_create(0, lb_config->cid_encryption_key); + if (lb_ctx->cid_decryption_context == NULL) { + picoquic_aes128_ecb_free(lb_ctx->cid_encryption_context); + lb_ctx->cid_encryption_context = NULL; + ret = -1; + } + } + } + if (ret != 0) { + /* if context allocation failed, free the copy */ + free(lb_ctx); + lb_ctx = NULL; + } else { + /* Configure the CID generation */ + quic->local_cnxid_length = lb_ctx->connection_id_length; + quic->cnx_id_callback_fn = picoquic_lb_compat_cid_generate; + quic->cnx_id_callback_ctx = (void*)lb_ctx; + } + } + } + } + + return ret; +} + +void picoquic_lb_compat_cid_config_free(picoquic_quic_t* quic) +{ + if (quic->cnx_id_callback_fn == picoquic_lb_compat_cid_generate && + quic->cnx_id_callback_ctx != NULL) { + picoquic_load_balancer_config_t* lb_config = (picoquic_load_balancer_config_t*)quic->cnx_id_callback_ctx; + /* Release the encryption contexts so as to avoid memory leaks */ + /* Free the data */ + free(lb_config); + /* Reset the Quic context */ + quic->cnx_id_callback_fn = NULL; + quic->cnx_id_callback_ctx = NULL; + } +} \ No newline at end of file diff --git a/deps/picoquic/sacks.c b/deps/picoquic/sacks.c new file mode 100644 index 0000000..f1b099f --- /dev/null +++ b/deps/picoquic/sacks.c @@ -0,0 +1,233 @@ +/* +* Author: Christian Huitema +* Copyright (c) 2017, Private Octopus, Inc. +* All rights reserved. +* +* Permission to use, copy, modify, and distribute this software for any +* purpose with or without fee is hereby granted, provided that the above +* copyright notice and this permission notice appear in all copies. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL Private Octopus, Inc. BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "picoquic_internal.h" +#include + +/* +* Packet sequence recording prepares the next ACK: +* +* Maintain largest acknowledged number & the timestamp of that +* arrival used to calculate the ACK delay. +* +* Maintain the list of ACK +*/ + +/* + * Check whether the packet was already received. + */ +int picoquic_is_pn_already_received(picoquic_cnx_t* cnx, + picoquic_packet_context_enum pc, uint64_t pn64) +{ + int is_received = 0; + picoquic_sack_item_t* sack = &cnx->pkt_ctx[pc].first_sack_item; + + if (sack->start_of_sack_range != (uint64_t)((int64_t)-1)) { + do { + if (pn64 > sack->end_of_sack_range) + break; + else if (pn64 >= sack->start_of_sack_range) { + is_received = 1; + break; + } + else { + sack = sack->next_sack; + } + } while (sack != NULL); + } + + return is_received; +} + +/* + * Packet was already received and checksum, etc. was properly verified. + * Record it in the chain. + */ + +int picoquic_update_sack_list(picoquic_sack_item_t* sack, + uint64_t pn64_min, uint64_t pn64_max) +{ + int ret = 1; /* duplicate by default, reset to 0 if update found */ + picoquic_sack_item_t* previous = NULL; + + if (sack->start_of_sack_range == (uint64_t)((int64_t)-1)) { + /* This is the first packet ever received.. */ + sack->start_of_sack_range = pn64_min; + sack->end_of_sack_range = pn64_max; + ret = 0; + } else { + do { + if (pn64_max > sack->end_of_sack_range) { + ret = 0; + + if (pn64_min <= sack->end_of_sack_range + 1) { + /* if this actually fills the hole, merge with previous item */ + if (previous != NULL && pn64_max + 1 >= previous->start_of_sack_range) { + previous->start_of_sack_range = sack->start_of_sack_range; + previous->next_sack = sack->next_sack; + free(sack); + sack = previous; + } else { + /* add at end of range */ + sack->end_of_sack_range = pn64_max; + } + + /* Check whether there is a need to continue */ + if (pn64_min >= sack->start_of_sack_range) { + break; + } else if (sack->next_sack == NULL) { + /* Last in range. Just expand. */ + sack->start_of_sack_range = pn64_min; + break; + } else { + /* Continue with reminder of range */ + pn64_max = sack->start_of_sack_range - 1; + previous = sack; + sack = sack->next_sack; + } + } else if (previous != NULL && pn64_max + 1 >= previous->start_of_sack_range) { + /* Extend the previous range */ + previous->start_of_sack_range = pn64_min; + break; + } else { + /* Found a new hole */ + picoquic_sack_item_t* new_hole = (picoquic_sack_item_t*)malloc(sizeof(picoquic_sack_item_t)); + if (new_hole == NULL) { + /* memory error. That's infortunate */ + ret = -1; + } else { + /* swap old and new, so it works even if previous == NULL */ + new_hole->start_of_sack_range = sack->start_of_sack_range; + new_hole->end_of_sack_range = sack->end_of_sack_range; + new_hole->next_sack = sack->next_sack; + sack->start_of_sack_range = pn64_min; + sack->end_of_sack_range = pn64_max; + sack->next_sack = new_hole; + } + /* No need to continue, everything is consumed. */ + break; + } + } else if (pn64_max >= sack->start_of_sack_range) { + if (pn64_min < sack->start_of_sack_range) { + ret = 0; + + if (sack->next_sack == NULL) { + /* Just extend the last range */ + sack->start_of_sack_range = pn64_min; + break; + } else { + /* continue with reminder. */ + pn64_max = sack->start_of_sack_range - 1; + previous = sack; + sack = sack->next_sack; + } + } else { + /*comple overlap */ + break; + } + } else if (sack->next_sack == NULL) { + ret = 0; + if (pn64_max + 1 == sack->start_of_sack_range) { + sack->start_of_sack_range = pn64_min; + } else { + /* this is an old packet, beyond the current range of SACK */ + /* Found a new hole */ + picoquic_sack_item_t* new_hole = (picoquic_sack_item_t*)malloc(sizeof(picoquic_sack_item_t)); + if (new_hole == NULL) { + /* memory error. That's infortunate */ + ret = -1; + } else { + /* Create new hole at the tail. */ + new_hole->start_of_sack_range = pn64_min; + new_hole->end_of_sack_range = pn64_max; + new_hole->next_sack = NULL; + sack->next_sack = new_hole; + } + } + break; + } else { + previous = sack; + sack = sack->next_sack; + } + } while (sack != NULL); + } + + return ret; +} + +int picoquic_record_pn_received(picoquic_cnx_t* cnx, + picoquic_packet_context_enum pc, uint64_t pn64, + uint64_t current_microsec) +{ + int ret = 0; + picoquic_sack_item_t* sack = &cnx->pkt_ctx[pc].first_sack_item; + + if (sack->start_of_sack_range == (uint64_t)((int64_t)-1)) { + /* This is the first packet ever received.. */ + sack->start_of_sack_range = pn64; + sack->end_of_sack_range = pn64; + cnx->pkt_ctx[pc].time_stamp_largest_received = current_microsec; + } + else { + if (pn64 > sack->end_of_sack_range) { + cnx->pkt_ctx[pc].time_stamp_largest_received = current_microsec; + } + + ret = picoquic_update_sack_list(sack, pn64, pn64); + } + + return ret; +} + +/* + * Check whether the data fills a hole. returns 0 if it does, -1 otherwise. + */ +int picoquic_check_sack_list(picoquic_sack_item_t* sack, + uint64_t pn64_min, uint64_t pn64_max) +{ + int ret = -1; /* duplicate by default, reset to 0 if update found */ + + if (sack->start_of_sack_range == (uint64_t)((int64_t)-1)) { + ret = 0; + } else { + for(;;) { + if (pn64_max > sack->end_of_sack_range) { + ret = 0; + break; + } else if (pn64_max >= sack->start_of_sack_range) { + if (pn64_min < sack->start_of_sack_range) { + ret = 0; + } else { + /*complete overlap */ + ret = -1; + } + break; + } else if (sack->next_sack == NULL) { + ret = 0; + break; + } else { + sack = sack->next_sack; + } + }; + } + + return ret; +} diff --git a/deps/picoquic/sender.c b/deps/picoquic/sender.c new file mode 100644 index 0000000..d2ac802 --- /dev/null +++ b/deps/picoquic/sender.c @@ -0,0 +1,3926 @@ +/* +* Author: Christian Huitema +* Copyright (c) 2017, Private Octopus, Inc. +* All rights reserved. +* +* Permission to use, copy, modify, and distribute this software for any +* purpose with or without fee is hereby granted, provided that the above +* copyright notice and this permission notice appear in all copies. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL Private Octopus, Inc. BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "picoquic_internal.h" +#include "picoquic_unified_log.h" +#include "tls_api.h" +#include +#include + +/* + * Sending logic. + * + * Data is sent over streams. This is instantiated by the "Post to stream" command, which + * chains data to the head of stream structure. Data is unchained when it sent for the + * first time. + * + * Data is sent in packets, which contain stream frames and possibly other frames. + * The retransmission logic operates on packets. If a packet is seen as lost, the + * important frames that it contains will have to be retransmitted. + * + * Unacknowledged packets are kept in a chained list. Packets get removed from that + * list during the processing of acknowledgements. Packets are marked lost when a + * sufficiently older packet is acknowledged, or after a timer. Lost packets + * generate new packets, which are queued in the chained list. + */ + +static picoquic_stream_head_t* picoquic_find_stream_for_writing(picoquic_cnx_t* cnx, + uint64_t stream_id, int * ret) +{ + picoquic_stream_head_t* stream = picoquic_find_stream(cnx, stream_id); + + *ret = 0; + + if (stream == NULL) { + /* Need to check that the ID is authorized */ + + /* Check parity */ + if (IS_CLIENT_STREAM_ID(stream_id) != cnx->client_mode) { + *ret = PICOQUIC_ERROR_INVALID_STREAM_ID; + } + + if (*ret == 0) { + stream = picoquic_create_missing_streams(cnx, stream_id, 0); + + if (stream == NULL) { + *ret = PICOQUIC_ERROR_MEMORY; + } + } + } + + return stream; +} + +int picoquic_set_app_stream_ctx(picoquic_cnx_t* cnx, + uint64_t stream_id, void* app_stream_ctx) +{ + int ret = 0; + picoquic_stream_head_t* stream = picoquic_find_stream_for_writing(cnx, stream_id, &ret); + + if (ret == 0) { + stream->app_stream_ctx = app_stream_ctx; + } + + return ret; +} + +int picoquic_mark_active_stream(picoquic_cnx_t* cnx, + uint64_t stream_id, int is_active, void * app_stream_ctx) +{ + int ret = 0; + picoquic_stream_head_t* stream = picoquic_find_stream_for_writing(cnx, stream_id, &ret); + + if (ret == 0) { + if (is_active) { + /* The call only fails if the stream was closed or reset */ + if (!stream->fin_requested && !stream->reset_requested && + cnx->callback_fn != NULL) { + stream->is_active = 1; + stream->app_stream_ctx = app_stream_ctx; + picoquic_reinsert_by_wake_time(cnx->quic, cnx, picoquic_get_quic_time(cnx->quic)); + } + else { + ret = PICOQUIC_ERROR_CANNOT_SET_ACTIVE_STREAM; + } + } + else { + stream->is_active = 0; + } + } + + return ret; +} + +int picoquic_mark_high_priority_stream(picoquic_cnx_t * cnx, uint64_t stream_id, int is_high_priority) +{ + if (is_high_priority) { + cnx->high_priority_stream_id = stream_id; + } + else if (cnx->high_priority_stream_id == stream_id) { + cnx->high_priority_stream_id = (uint64_t)((int64_t)-1); + } + + return 0; +} + +int picoquic_add_to_stream_with_ctx(picoquic_cnx_t* cnx, uint64_t stream_id, + const uint8_t* data, size_t length, int set_fin, void * app_stream_ctx) +{ + int ret = 0; + picoquic_stream_head_t* stream = picoquic_find_stream_for_writing(cnx, stream_id, &ret); + + if (ret == 0 && set_fin) { + if (stream->fin_requested) { + /* app error, notified the fin twice*/ + if (length > 0) { + ret = -1; + } + } else { + stream->fin_requested = 1; + } + } + + /* If our side has sent RST_STREAM or received STOP_SENDING, we should not send anymore data. */ + if (ret == 0 && (stream->reset_sent || stream->stop_sending_received)) { + ret = -1; + } + + if (ret == 0 && length > 0) { + picoquic_stream_data_node_t* stream_data = (picoquic_stream_data_node_t*)malloc(sizeof(picoquic_stream_data_node_t)); + + if (stream_data == 0) { + ret = -1; + } else { + stream_data->bytes = (uint8_t*)malloc(length); + + if (stream_data->bytes == NULL) { + free(stream_data); + stream_data = NULL; + ret = -1; + } else { + picoquic_stream_data_node_t** pprevious = &stream->send_queue; + picoquic_stream_data_node_t* next = stream->send_queue; + + memcpy(stream_data->bytes, data, length); + stream_data->length = length; + stream_data->offset = 0; + stream_data->next_stream_data = NULL; + + while (next != NULL) { + pprevious = &next->next_stream_data; + next = next->next_stream_data; + } + + *pprevious = stream_data; + } + } + + picoquic_reinsert_by_wake_time(cnx->quic, cnx, picoquic_get_quic_time(cnx->quic)); + } + + if (ret == 0) { + cnx->nb_bytes_queued += length; + stream->is_active = 0; + stream->app_stream_ctx = app_stream_ctx; + } + + return ret; +} + +int picoquic_add_to_stream(picoquic_cnx_t* cnx, uint64_t stream_id, + const uint8_t* data, size_t length, int set_fin) +{ + return picoquic_add_to_stream_with_ctx(cnx, stream_id, data, length, set_fin, NULL); +} + +int picoquic_open_flow_control(picoquic_cnx_t* cnx, uint64_t stream_id, uint64_t expected_data_size) +{ + int ret = 0; + uint8_t buffer[512]; + size_t length = 0; + size_t consumed = 0; + picoquic_stream_head_t* stream = picoquic_find_stream(cnx, stream_id); + + if (cnx->cnx_state == picoquic_state_ready){ + /* Only send the update in ready state, so that the misc frame is not picked by the + * wrong transport context. + * TODO: find way to queue the update so it is only sent as 0RTT or 1RTT packet. + */ + if (stream == NULL) { + ret = PICOQUIC_ERROR_INVALID_STREAM_ID; + } + else { + uint64_t max_required = stream->consumed_offset + expected_data_size; + uint8_t* bytes_max = buffer + sizeof(buffer); + int more_data = 0; + int is_pure_ack = 1; + + if (max_required > stream->maxdata_local) { + uint8_t* bytes_next = picoquic_format_max_stream_data_frame(stream, buffer + consumed, bytes_max, &more_data, &is_pure_ack, max_required); + bytes_next = picoquic_format_max_data_frame(cnx, bytes_next, bytes_max, &more_data, &is_pure_ack, expected_data_size); + if ((length = bytes_next - buffer) > 0) { + ret = picoquic_queue_misc_frame(cnx, buffer, length, is_pure_ack); + } + } + } + } + + return ret; +} + +void picoquic_reset_stream_ctx(picoquic_cnx_t* cnx, uint64_t stream_id) +{ + picoquic_stream_head_t* stream = picoquic_find_stream(cnx, stream_id); + if (stream != NULL) { + stream->app_stream_ctx = NULL; + } +} + +int picoquic_reset_stream(picoquic_cnx_t* cnx, + uint64_t stream_id, uint16_t local_stream_error) +{ + int ret = 0; + picoquic_stream_head_t* stream = NULL; + + stream = picoquic_find_stream(cnx, stream_id); + + if (stream == NULL) { + ret = PICOQUIC_ERROR_INVALID_STREAM_ID; + } + else { + stream->app_stream_ctx = NULL; + if (stream->fin_sent) { + ret = PICOQUIC_ERROR_STREAM_ALREADY_CLOSED; + } + else if (!stream->reset_requested) { + stream->local_error = local_stream_error; + stream->reset_requested = 1; + } + } + + picoquic_reinsert_by_wake_time(cnx->quic, cnx, picoquic_get_quic_time(cnx->quic)); + + return ret; +} + +uint64_t picoquic_get_next_local_stream_id(picoquic_cnx_t* cnx, int is_unidir) +{ + int stream_type_id = (cnx->client_mode ^ 1) | ((is_unidir) ? 2 : 0); + + return cnx->next_stream_id[stream_type_id]; +} + +int picoquic_stop_sending(picoquic_cnx_t* cnx, + uint64_t stream_id, uint16_t local_stream_error) +{ + int ret = 0; + picoquic_stream_head_t* stream = NULL; + + stream = picoquic_find_stream(cnx, stream_id); + + if (stream == NULL) { + ret = PICOQUIC_ERROR_INVALID_STREAM_ID; + } + else { + stream->app_stream_ctx = NULL; + + if (stream->reset_received) { + ret = PICOQUIC_ERROR_STREAM_ALREADY_CLOSED; + } + else if (!stream->stop_sending_requested) { + stream->local_stop_error = local_stream_error; + stream->stop_sending_requested = 1; + picoquic_insert_output_stream(cnx, stream); + } + } + + picoquic_reinsert_by_wake_time(cnx->quic, cnx, picoquic_get_quic_time(cnx->quic)); + + return ret; +} + +/* + * Manage content padding + */ + +size_t picoquic_pad_to_target_length(uint8_t * bytes, size_t length, size_t target) +{ + if (length < target) { + memset(bytes + length, 0, target - length); + length = target; + } + + return length; +} + +size_t picoquic_pad_to_policy(picoquic_cnx_t * cnx, uint8_t * bytes, size_t length, uint32_t max_length) +{ + size_t target = cnx->padding_minsize; + + if (length > target && cnx->padding_multiple != 0) { + uint32_t delta = (length - target) % cnx->padding_multiple; + + if (delta == 0) { + target = length; + } + else { + target = length + cnx->padding_multiple - delta; + } + } + + if (target > max_length) { + target = max_length; + } + + return picoquic_pad_to_target_length(bytes, length, target); +} + + +/* + * Packet management + */ + +picoquic_packet_t* picoquic_create_packet(picoquic_quic_t * quic) +{ + picoquic_packet_t* packet = quic->p_first_packet; + + if (packet == NULL) { + packet = (picoquic_packet_t*)malloc(sizeof(picoquic_packet_t)); + } + else { + quic->p_first_packet = packet->next_packet; + quic->nb_packets_in_pool--; + } + + if (packet != NULL) { + memset(packet, 0, offsetof(struct st_picoquic_packet_t, bytes)); + } + + return packet; +} + +void picoquic_recycle_packet(picoquic_quic_t * quic, picoquic_packet_t* packet) +{ + if (packet != NULL) { + if (quic->nb_packets_in_pool >= PICOQUIC_MAX_PACKETS_IN_POOL) { + free(packet); + } + else { + packet->next_packet = quic->p_first_packet; + quic->p_first_packet = packet; + quic->nb_packets_in_pool++; + } + } +} + +void picoquic_update_payload_length( + uint8_t* bytes, size_t pnum_index, size_t header_length, size_t packet_length) +{ + if ((bytes[0] & 0x80) != 0 && header_length > 6 && packet_length > header_length && packet_length < 0x4000) + { + picoquic_varint_encode_16(bytes + pnum_index - 2, (uint16_t)(packet_length - header_length)); + } +} + +size_t picoquic_create_packet_header( + picoquic_cnx_t* cnx, + picoquic_packet_type_enum packet_type, + uint64_t sequence_number, + picoquic_connection_id_t * remote_cnxid, + picoquic_connection_id_t * local_cnxid, + size_t header_length, + uint8_t* bytes, + size_t* pn_offset, + size_t* pn_length) +{ + size_t length = 0; + picoquic_connection_id_t dest_cnx_id = + (cnx->client_mode && (packet_type == picoquic_packet_initial || + packet_type == picoquic_packet_0rtt_protected) + && picoquic_is_connection_id_null(remote_cnxid)) ? + cnx->initial_cnxid : *remote_cnxid; + + /* Prepare the packet header */ + if (packet_type == picoquic_packet_1rtt_protected) { + /* Create a short packet -- using 32 bit sequence numbers for now */ + uint8_t K = (cnx->key_phase_enc) ? 0x04 : 0; + uint8_t C = 0x40; /* set the QUIC bit */ + size_t pn_l = 4; /* default packet length to 4 bytes */ + + if (cnx->do_grease_quic_bit) { + /* we grease the quic bit if both local and remote agreed to do so */ + C &= (uint8_t)picoquic_public_random_64(); + cnx->quic_bit_greased |= (C == 0); + } + + length = 0; + bytes[length++] = (K | C | picoquic_spin_function_table[cnx->spin_policy].spinbit_outgoing(cnx)); + length += picoquic_format_connection_id(&bytes[length], PICOQUIC_MAX_PACKET_SIZE - length, dest_cnx_id); + + *pn_offset = length; + if (header_length > length && header_length < length + 4) { + pn_l = header_length - length; + } + *pn_length = pn_l; + bytes[0] |= (pn_l - 1); + switch (pn_l) { + case 1: + bytes[length] = (uint8_t)sequence_number; + break; + case 2: + picoformat_16(&bytes[length], (uint16_t)sequence_number); + break; + case 3: + picoformat_24(&bytes[length], (uint32_t)sequence_number); + break; + default: + picoformat_32(&bytes[length], (uint32_t)sequence_number); + break; + } + length += pn_l; + } + else { + /* Create a long packet -- default encode PP=3 */ + + switch (packet_type) { + case picoquic_packet_initial: + bytes[0] = 0xC3; + break; + case picoquic_packet_0rtt_protected: + bytes[0] = 0xD3; + break; + case picoquic_packet_handshake: + bytes[0] = 0xE3; + break; + case picoquic_packet_retry: + /* Do not set PP in retry header, the bits are later used for ODCIL */ + bytes[0] = 0xF0; + break; + default: + bytes[0] = 0xFF; /* Will cause an error... */ + break; + } + + if (cnx->do_grease_quic_bit) { + bytes[0] &= 0xBF; + } + + length = 1; + if ((cnx->cnx_state == picoquic_state_client_init || cnx->cnx_state == picoquic_state_client_init_sent) && packet_type == picoquic_packet_initial) { + picoformat_32(&bytes[length], cnx->proposed_version); + } + else { + picoformat_32(&bytes[length], picoquic_supported_versions[cnx->version_index].version); + } + length += 4; + + bytes[length++] = dest_cnx_id.id_len; + length += picoquic_format_connection_id(&bytes[length], PICOQUIC_MAX_PACKET_SIZE - length, dest_cnx_id); + bytes[length++] = local_cnxid->id_len; + length += picoquic_format_connection_id(&bytes[length], PICOQUIC_MAX_PACKET_SIZE - length, *local_cnxid); + + /* Special case of packet initial -- encode token as part of header */ + if (packet_type == picoquic_packet_initial) { + length += picoquic_varint_encode(&bytes[length], PICOQUIC_MAX_PACKET_SIZE - length, cnx->retry_token_length); + if (cnx->retry_token_length > 0) { + memcpy(&bytes[length], cnx->retry_token, cnx->retry_token_length); + length += cnx->retry_token_length; + } + } + + if (packet_type == picoquic_packet_retry) { + /* No payload length and no sequence number for Retry */ + *pn_offset = 0; + *pn_length = 0; + } else { + /* Reserve two bytes for payload length */ + bytes[length++] = 0; + bytes[length++] = 0; + /* Encode the sequence number */ + *pn_offset = length; + *pn_length = 4; + picoformat_32(&bytes[length], (uint32_t) sequence_number); + length += 4; + } + } + + return length; +} + +size_t picoquic_predict_packet_header_length( + picoquic_cnx_t* cnx, + picoquic_packet_type_enum packet_type) +{ + uint32_t header_length = 0; + + /* The only purpose of the test below is to appease the static analyzer, so it + * wont complain of possible NULL deref. On windows we could use "__assume(cnx != NULL) + * but the documentation does not say anything about that for GCC and CLANG */ + if (cnx == NULL) { + return 0; + } + + if (packet_type == picoquic_packet_1rtt_protected) { + /* Predict acceptable length of packet number */ + uint8_t pn_l = 4; + int64_t delta = cnx->pkt_ctx[picoquic_packet_context_application].send_sequence; + if (cnx->pkt_ctx[picoquic_packet_context_application].retransmit_oldest != NULL) { + delta -= cnx->pkt_ctx[picoquic_packet_context_application].retransmit_oldest->sequence_number; + } + if (delta < 262144) { + pn_l = 3; + if (cnx->pkt_ctx[picoquic_packet_context_application].send_sequence < 1024) { + pn_l = 2; + if (cnx->pkt_ctx[picoquic_packet_context_application].send_sequence < 16) { + pn_l = 1; + } + } + } + + /* Compute length of a short packet header */ + header_length = 1 + cnx->path[0]->remote_cnxid.id_len + pn_l; + } + else { + /* Compute length of a long packet header */ + header_length = 1 + /* version */ 4 + /* cnx_id length bytes */ 2; + + /* add dest-id length */ + if (cnx->client_mode && (packet_type == picoquic_packet_initial || + packet_type == picoquic_packet_0rtt_protected) + && picoquic_is_connection_id_null(&cnx->path[0]->remote_cnxid)) { + header_length += cnx->initial_cnxid.id_len; + } + else { + header_length += cnx->path[0]->remote_cnxid.id_len; + } + + /* add srce-id length */ + header_length += cnx->path[0]->p_local_cnxid->cnx_id.id_len; + + /* add length of payload length and packet number */ + header_length += 2 + 4; + + /* add length of tokens for initial packets */ + if (packet_type == picoquic_packet_initial) { + uint8_t useless[16]; + header_length += (uint32_t)picoquic_varint_encode(useless, 16, cnx->retry_token_length); + header_length += (uint32_t)cnx->retry_token_length; + } + } + + return header_length; +} + +/* + * Management of packet protection + */ +size_t picoquic_get_checksum_length(picoquic_cnx_t* cnx, picoquic_epoch_enum epoch) +{ + size_t ret = 16; + + if (cnx->crypto_context[epoch].aead_encrypt != NULL) { + ret = picoquic_aead_get_checksum_length(cnx->crypto_context[epoch].aead_encrypt); + } + else { + DBG_PRINTF("Try getting checksum for empty context, epoch %d", epoch); + } + + return ret; +} + +static size_t picoquic_protect_packet(picoquic_cnx_t* cnx, + picoquic_packet_type_enum ptype, + uint8_t * bytes, + uint64_t sequence_number, + picoquic_connection_id_t * remote_cnxid, + picoquic_connection_id_t * local_cnxid, + size_t length, size_t header_length, + uint8_t* send_buffer, size_t send_buffer_max, + void * aead_context, void* pn_enc, + picoquic_path_t* path_x, uint64_t current_time) +{ + size_t send_length; + size_t h_length; + size_t pn_offset = 0; + size_t sample_offset = 0; + size_t pn_length = 0; + size_t aead_checksum_length = picoquic_aead_get_checksum_length(aead_context); + uint8_t first_mask = 0x0F; + + /* Create the packet header just before encrypting the content */ + h_length = picoquic_create_packet_header(cnx, ptype, + sequence_number, remote_cnxid, local_cnxid, header_length, send_buffer, &pn_offset, &pn_length); + if (ptype == picoquic_packet_1rtt_protected) { + if (remote_cnxid != &path_x->remote_cnxid) { + /* Packet is sent to a different CID: reset the spin bit and loss bit Q to 0 */ + send_buffer[0] &= 0xDF; + path_x->q_square = 0; + } + + if (cnx->is_loss_bit_enabled_outgoing) { + first_mask = 0x07; + path_x->q_square++; + if ((path_x->q_square & PICOQUIC_LOSS_BIT_Q_HALF_PERIOD) != 0) { + send_buffer[0] |= 0x10; + } + if (path_x->nb_losses_found > path_x->nb_losses_reported) { + send_buffer[0] |= 0x08; + path_x->nb_losses_reported++; + } + } + else { + first_mask = 0x1F; + } + } + + /* Make sure that the payload length is encoded in the header */ + /* Using encryption, the "payload" length also includes the encrypted packet length */ + picoquic_update_payload_length(send_buffer, pn_offset, h_length - pn_length, length + aead_checksum_length); + + /* If fuzzing is required, apply it*/ + if (cnx->quic->fuzz_fn != NULL) { + if (h_length == header_length) { + memcpy(bytes, send_buffer, header_length); + } + length = cnx->quic->fuzz_fn(cnx->quic->fuzz_ctx, cnx, bytes, + send_buffer_max - aead_checksum_length, length, header_length); + if (h_length == header_length) { + memcpy(send_buffer, bytes, header_length); + } + } + + /* Encrypt the packet */ + send_length = picoquic_aead_encrypt_generic(send_buffer + /* header_length */ h_length, + bytes + header_length, length - header_length, + sequence_number, send_buffer, /* header_length */ h_length, aead_context); + + send_length += /* header_length */ h_length; + + /* if needed, log the segment before header protection is applied */ + picoquic_log_outgoing_packet(cnx, + bytes, sequence_number, pn_length, length, + send_buffer, send_length, current_time); + + /* Next, encrypt the PN -- The sample is located after the pn_offset */ + sample_offset = /* header_length */ pn_offset + 4; + + if (pn_offset < sample_offset) + { + /* This is always true, as use pn_length = 4 */ + uint8_t mask_bytes[5] = { 0, 0, 0, 0, 0 }; + uint8_t pn_l; + + picoquic_pn_encrypt(pn_enc, send_buffer + sample_offset, mask_bytes, mask_bytes, 5); + /* Encode the first byte */ + pn_l = (send_buffer[0] & 3) + 1; + send_buffer[0] ^= (mask_bytes[0] & first_mask); + + /* Packet encoding is 1 to 4 bytes */ + for (uint8_t i = 0; i < pn_l; i++) { + send_buffer[pn_offset+i] ^= mask_bytes[i+1]; + } + } + + return send_length; +} + +/* Update the leaky bucket used for pacing. + */ +static void picoquic_update_pacing_bucket(picoquic_path_t * path_x, uint64_t current_time) +{ + if (path_x->pacing_bucket_nanosec < -path_x->pacing_packet_time_nanosec) { + path_x->pacing_bucket_nanosec = -path_x->pacing_packet_time_nanosec; + } + + if (current_time > path_x->pacing_evaluation_time) { + path_x->pacing_bucket_nanosec += (current_time - path_x->pacing_evaluation_time) * 1000; + path_x->pacing_evaluation_time = current_time; + if (path_x->pacing_bucket_nanosec > path_x->pacing_bucket_max) { + path_x->pacing_bucket_nanosec = path_x->pacing_bucket_max; + } + } +} + +/* + * Check pacing to see whether the next transmission is authorized. + * If if is not, update the next wait time to reflect pacing. + * + * In packet train mode, the wait will last until the bucket is completely full, or + * if at least N packets are received. + */ +int picoquic_is_sending_authorized_by_pacing(picoquic_cnx_t * cnx, picoquic_path_t * path_x, uint64_t current_time, uint64_t * next_time) +{ + int ret = 1; + + picoquic_update_pacing_bucket(path_x, current_time); + if (path_x->pacing_bucket_nanosec < path_x->pacing_packet_time_nanosec) { + uint64_t next_pacing_time; + int64_t bucket_required; + + if (cnx->quic->packet_train_mode) { + bucket_required = path_x->pacing_bucket_max; + + if (bucket_required > 10 * path_x->pacing_packet_time_nanosec) { + bucket_required = 10 * path_x->pacing_packet_time_nanosec; + } + + bucket_required -= path_x->pacing_bucket_nanosec; + } + else { + bucket_required = path_x->pacing_packet_time_nanosec - path_x->pacing_bucket_nanosec; + } + + next_pacing_time = current_time + 1 + bucket_required / 1000; + if (next_pacing_time < *next_time) { + *next_time = next_pacing_time; + SET_LAST_WAKE(cnx->quic, PICOQUIC_SENDER); + } + ret = 0; + } + + return ret; +} + +/* Reset the pacing data after recomputing the pacing rate + */ +void picoquic_update_pacing_rate(picoquic_cnx_t * cnx, picoquic_path_t* path_x, double pacing_rate, uint64_t quantum) +{ + double packet_time = (double)path_x->send_mtu / pacing_rate; + double quantum_time = (double)quantum / pacing_rate; + uint64_t rtt_nanosec = path_x->smoothed_rtt * 1000; + + path_x->pacing_rate = (uint64_t)pacing_rate; + + path_x->pacing_packet_time_nanosec = (uint64_t)(packet_time * 1000000000.0); + + if (path_x->pacing_packet_time_nanosec <= 0) { + path_x->pacing_packet_time_nanosec = 1; + path_x->pacing_packet_time_microsec = 1; + } + else { + if ((uint64_t)path_x->pacing_packet_time_nanosec > rtt_nanosec) { + path_x->pacing_packet_time_nanosec = rtt_nanosec; + } + path_x->pacing_packet_time_microsec = (path_x->pacing_packet_time_nanosec + 999ull) / 1000; + } + + path_x->pacing_bucket_max = (uint64_t)(quantum_time * 1000000000.0); + if (path_x->pacing_bucket_max <= 0) { + path_x->pacing_bucket_max = 16 * path_x->pacing_packet_time_nanosec; + } + + if (path_x->pacing_bucket_nanosec > path_x->pacing_bucket_max) { + path_x->pacing_bucket_nanosec = path_x->pacing_bucket_max; + } + + if (cnx->is_pacing_update_requested && path_x == cnx->path[0] && + cnx->callback_fn != NULL) { + if ((path_x->pacing_rate > cnx->pacing_rate_signalled && + (path_x->pacing_rate - cnx->pacing_rate_signalled >= cnx->pacing_increase_threshold)) || + (path_x->pacing_rate < cnx->pacing_rate_signalled && + (cnx->pacing_rate_signalled - path_x->pacing_rate > cnx->pacing_decrease_threshold))){ + (void)cnx->callback_fn(cnx, path_x->pacing_rate, NULL, 0, picoquic_callback_pacing_changed, cnx->callback_ctx, NULL); + cnx->pacing_rate_signalled = path_x->pacing_rate; + } + } +} + +/* + * Reset the pacing data after CWIN is updated. + * The max bucket is set to contain at least 2 packets more than 1/8th of the congestion window. + */ + +void picoquic_update_pacing_data(picoquic_cnx_t* cnx, picoquic_path_t * path_x, int slow_start) +{ + uint64_t rtt_nanosec = path_x->smoothed_rtt * 1000; + + if ((path_x->cwin < ((uint64_t)path_x->send_mtu) * 8) || rtt_nanosec <= 1000) { + /* Small windows, should only relie on ACK clocking */ + path_x->pacing_bucket_max = rtt_nanosec; + path_x->pacing_packet_time_nanosec = 1; + path_x->pacing_packet_time_microsec = 1; + + if (path_x->pacing_bucket_nanosec > path_x->pacing_bucket_max) { + path_x->pacing_bucket_nanosec = path_x->pacing_bucket_max; + } + } + else { + double pacing_rate = ((double)path_x->cwin / (double)rtt_nanosec) * 1000000000.0; + uint64_t quantum = path_x->cwin / 4; + + if (quantum < 2ull * path_x->send_mtu) { + quantum = 2ull * path_x->send_mtu; + } + else { + if (slow_start && path_x->smoothed_rtt > 4*PICOQUIC_MAX_BANDWIDTH_TIME_INTERVAL_MAX) { + const uint64_t quantum_min = 0x8000; + if (quantum < quantum_min){ + quantum = quantum_min; + } + else { + uint64_t quantum2 = (uint64_t)((pacing_rate * PICOQUIC_MAX_BANDWIDTH_TIME_INTERVAL_MAX) / 1000000.0); + if (quantum2 > quantum_min) { + quantum = quantum2; + } + } + } + else if (quantum > 16ull * path_x->send_mtu) { + quantum = 16ull * path_x->send_mtu; + } + + } + + if (slow_start) { + pacing_rate *= 1.25; + } + + picoquic_update_pacing_rate(cnx, path_x, pacing_rate, quantum); + } +} + +/* + * Update the pacing data after sending a packet. + */ +void picoquic_update_pacing_after_send(picoquic_path_t * path_x, uint64_t current_time) +{ + picoquic_update_pacing_bucket(path_x, current_time); + + path_x->pacing_bucket_nanosec -= path_x->pacing_packet_time_nanosec; +} + +/* + * Final steps in packet transmission: queue for retransmission, etc + */ + +void picoquic_queue_for_retransmit(picoquic_cnx_t* cnx, picoquic_path_t * path_x, picoquic_packet_t* packet, + size_t length, uint64_t current_time) +{ + picoquic_packet_context_enum pc = packet->pc; + + /* Manage the double linked packet list for retransmissions */ + packet->previous_packet = NULL; + if (cnx->pkt_ctx[pc].retransmit_newest == NULL) { + packet->next_packet = NULL; + cnx->pkt_ctx[pc].retransmit_oldest = packet; + } else { + packet->next_packet = cnx->pkt_ctx[pc].retransmit_newest; + packet->next_packet->previous_packet = packet; + } + cnx->pkt_ctx[pc].retransmit_newest = packet; + + if (!packet->is_ack_trap) { + /* Account for bytes in transit, for congestion control */ + path_x->bytes_in_transit += length; + /* Update the pacing data */ + picoquic_update_pacing_after_send(path_x, current_time); + } +} + +picoquic_packet_t* picoquic_dequeue_retransmit_packet(picoquic_cnx_t* cnx, picoquic_packet_t* p, int should_free) +{ + size_t dequeued_length = p->length + p->checksum_overhead; + picoquic_packet_context_enum pc = p->pc; + + if (p->previous_packet == NULL) { + cnx->pkt_ctx[pc].retransmit_newest = p->next_packet; + } + else { + p->previous_packet->next_packet = p->next_packet; + } + + if (p->next_packet == NULL) { + cnx->pkt_ctx[pc].retransmit_oldest = p->previous_packet; + } + else { +#ifdef _DEBUG + if (p->next_packet->pc != pc) { + DBG_PRINTF("Inconsistent PC in queue, %d vs %d\n", p->next_packet->pc, pc); + } + + if (p->next_packet->previous_packet != p) { + DBG_PRINTF("Inconsistent chain of packets, pc = %d\n", pc); + } +#endif + p->next_packet->previous_packet = p->previous_packet; + } + + /* Account for bytes in transit, for congestion control */ + + if (p->send_path != NULL && !p->is_ack_trap) { + if (p->send_path->bytes_in_transit > dequeued_length) { + p->send_path->bytes_in_transit -= dequeued_length; + } + else { + p->send_path->bytes_in_transit = 0; + } + } + + if (should_free || p->is_ack_trap) { + picoquic_recycle_packet(cnx->quic, p); + p = NULL; + } + else { + p->next_packet = NULL; + + /* add this packet to the retransmitted list */ + if (cnx->pkt_ctx[pc].retransmitted_oldest == NULL) { + cnx->pkt_ctx[pc].retransmitted_newest = p; + cnx->pkt_ctx[pc].retransmitted_oldest = p; + p->previous_packet = NULL; + } + else { + cnx->pkt_ctx[pc].retransmitted_newest->next_packet = p; + p->previous_packet = cnx->pkt_ctx[pc].retransmitted_newest; + cnx->pkt_ctx[pc].retransmitted_newest = p; + } + } + + return p; +} + +void picoquic_dequeue_retransmitted_packet(picoquic_cnx_t* cnx, picoquic_packet_t* p) +{ + picoquic_packet_context_enum pc = p->pc; + + if (p->next_packet == NULL) { + cnx->pkt_ctx[pc].retransmitted_newest = p->previous_packet; + } + else { + p->next_packet->previous_packet = p->previous_packet; + } + + if (p->previous_packet == NULL) { + cnx->pkt_ctx[pc].retransmitted_oldest = p->next_packet; + } + else { +#ifdef _DEBUG + if (p->previous_packet->pc != pc) { + DBG_PRINTF("Inconsistent PC in queue, %d vs %d\n", p->previous_packet->pc, pc); + } + + if (p->previous_packet->next_packet != p) { + DBG_PRINTF("Inconsistent chain of packets, pc = %d\n", pc); + } +#endif + p->previous_packet->next_packet = p->next_packet; + } + + picoquic_recycle_packet(cnx->quic, p); +} + +/* + * Inserting holes in the send sequence to trap optimistic ack. + * return 0 if hole was inserted, !0 if packet should be freed. + */ +void picoquic_insert_hole_in_send_sequence_if_needed(picoquic_cnx_t* cnx, uint64_t current_time, uint64_t * next_wake_time) +{ + if (cnx->quic->sequence_hole_pseudo_period == 0) { + /* Holing disabled. Set to max value, never worry about it later */ + cnx->pkt_ctx[0].next_sequence_hole = (uint64_t)((int64_t)-1); + } else if (cnx->cnx_state == picoquic_state_ready && + cnx->pkt_ctx[0].retransmit_newest != NULL && + cnx->pkt_ctx[0].send_sequence >= cnx->pkt_ctx[0].next_sequence_hole) { + if (cnx->pkt_ctx[0].next_sequence_hole != 0 && + !cnx->pkt_ctx[0].retransmit_newest->is_ack_trap) { + /* Insert a hole in sequence */ + picoquic_packet_t* packet = picoquic_create_packet(cnx->quic); + + if (packet != NULL) { + packet->is_ack_trap = 1; + packet->pc = picoquic_packet_context_application; + packet->ptype = picoquic_packet_1rtt_protected; + packet->send_path = cnx->path[0]; + packet->send_time = current_time; + packet->sequence_number = cnx->pkt_ctx[picoquic_packet_context_application].send_sequence++; + picoquic_queue_for_retransmit(cnx, cnx->path[0], packet, 0, current_time); + *next_wake_time = current_time; + SET_LAST_WAKE(cnx->quic, PICOQUIC_SENDER); + /* Simulate local loss on the Q bit square function. */ + cnx->path[0]->q_square++; + } + } + /* Predict the next hole*/ + cnx->pkt_ctx[0].next_sequence_hole = cnx->pkt_ctx[picoquic_packet_context_application].send_sequence + 3 + picoquic_public_uniform_random(cnx->quic->sequence_hole_pseudo_period); + } +} + +/* + * Final steps of encoding and protecting the packet before sending + */ + +void picoquic_finalize_and_protect_packet(picoquic_cnx_t *cnx, picoquic_packet_t * packet, int ret, + size_t length, size_t header_length, size_t checksum_overhead, + size_t * send_length, uint8_t * send_buffer, size_t send_buffer_max, + picoquic_connection_id_t * remote_cnxid, + picoquic_connection_id_t * local_cnxid, + picoquic_path_t * path_x, uint64_t current_time) +{ + if (length != 0 && length < header_length) { + length = 0; + } + + if (ret == 0 && length > 0) { + packet->length = length; + cnx->pkt_ctx[packet->pc].send_sequence++; + path_x->latest_sent_time = current_time; + path_x->path_cid_rotated = 0; + packet->delivered_prior = path_x->delivered_last; + packet->delivered_time_prior = path_x->delivered_time_last; + packet->delivered_sent_prior = path_x->delivered_sent_last; + packet->delivered_app_limited = (cnx->cnx_state < picoquic_state_ready || path_x->delivered_limited_index != 0); + + switch (packet->ptype) { + case picoquic_packet_version_negotiation: + /* Packet is not encrypted */ + break; + case picoquic_packet_initial: + length = picoquic_protect_packet(cnx, packet->ptype, packet->bytes, packet->sequence_number, + remote_cnxid, local_cnxid, + length, header_length, + send_buffer, send_buffer_max, cnx->crypto_context[picoquic_epoch_initial].aead_encrypt, cnx->crypto_context[picoquic_epoch_initial].pn_enc, + path_x, current_time); + break; + case picoquic_packet_handshake: + length = picoquic_protect_packet(cnx, packet->ptype, packet->bytes, packet->sequence_number, + remote_cnxid, local_cnxid, + length, header_length, + send_buffer, send_buffer_max, cnx->crypto_context[picoquic_epoch_handshake].aead_encrypt, cnx->crypto_context[picoquic_epoch_handshake].pn_enc, + path_x, current_time); + break; + case picoquic_packet_retry: + length = picoquic_protect_packet(cnx, packet->ptype, packet->bytes, packet->sequence_number, + remote_cnxid, local_cnxid, + length, header_length, + send_buffer, send_buffer_max, cnx->crypto_context[picoquic_epoch_0rtt].aead_encrypt, cnx->crypto_context[picoquic_epoch_0rtt].pn_enc, + path_x, current_time); + break; + case picoquic_packet_0rtt_protected: + length = picoquic_protect_packet(cnx, packet->ptype, packet->bytes, packet->sequence_number, + remote_cnxid, local_cnxid, + length, header_length, + send_buffer, send_buffer_max, cnx->crypto_context[picoquic_epoch_0rtt].aead_encrypt, cnx->crypto_context[picoquic_epoch_0rtt].pn_enc, + path_x, current_time); + break; + case picoquic_packet_1rtt_protected: + length = picoquic_protect_packet(cnx, packet->ptype, packet->bytes, packet->sequence_number, + remote_cnxid, local_cnxid, + length, header_length, + send_buffer, send_buffer_max, cnx->crypto_context[picoquic_epoch_1rtt].aead_encrypt, cnx->crypto_context[picoquic_epoch_1rtt].pn_enc, + path_x, current_time); + break; + default: + /* Packet type error. Do nothing at all. */ + length = 0; + break; + } + + *send_length = length; + + if (length > 0) { + packet->checksum_overhead = checksum_overhead; + picoquic_queue_for_retransmit(cnx, path_x, packet, length, current_time); + } else { + *send_length = 0; + } + } + else { + *send_length = 0; + } +} + +/* + * If a retransmit is needed, fill the packet with the required + * retransmission. Also, prune the retransmit queue as needed. + * + * TODO: consider that the retransmit timer is per path, from the path on + * which the packet was first sent, but the retransmission may be on + * a different path, with different MTU. + */ + +static uint64_t picoquic_current_retransmit_timer(picoquic_cnx_t* cnx, picoquic_packet_context_enum pc) +{ + uint64_t rto = cnx->path[0]->retransmit_timer; + + rto <<= cnx->pkt_ctx[pc].nb_retransmit; + if (cnx->cnx_state < picoquic_state_ready) { + if (rto > PICOQUIC_INITIAL_MAX_RETRANSMIT_TIMER) { + rto = PICOQUIC_INITIAL_MAX_RETRANSMIT_TIMER; + } + } + else if (rto > PICOQUIC_LARGE_RETRANSMIT_TIMER){ + uint64_t alt_rto = PICOQUIC_LARGE_RETRANSMIT_TIMER; + if (cnx->path[0]->rtt_min > PICOQUIC_TARGET_SATELLITE_RTT) { + alt_rto = (cnx->path[0]->smoothed_rtt * 3) >> 1; + } + if (alt_rto < rto) { + rto = alt_rto; + } + } + + return rto; +} + +static int picoquic_retransmit_needed_by_packet(picoquic_cnx_t* cnx, + picoquic_packet_t* p, uint64_t current_time, uint64_t * next_retransmit_time, int* timer_based) +{ + picoquic_packet_context_enum pc = p->pc; + uint64_t retransmit_time; + int64_t delta_seq = cnx->pkt_ctx[pc].highest_acknowledged - p->sequence_number; + int should_retransmit = 0; + int is_timer_based = 0; + + if (delta_seq > 0) { + /* By default, we use an RTO */ + retransmit_time = p->send_time + cnx->path[0]->retransmit_timer; + /* RACK logic works best when the amount of reordering is not too large */ + if (delta_seq < 3) { + /* When just a few ulterior packets are acknowledged, we work from the right edge. */ + uint64_t rack_timer_min = cnx->pkt_ctx[pc].highest_acknowledged_time + + cnx->remote_parameters.max_ack_delay + (cnx->path[0]->smoothed_rtt >> 2); + if (retransmit_time > rack_timer_min) { + retransmit_time = rack_timer_min; + } + } else { + /* When enough ulterior packets are acknowledged, we work from the right edge. */ + uint64_t rack_timer_min = p->send_time + (cnx->path[0]->smoothed_rtt >> 2); + if (rack_timer_min < cnx->pkt_ctx[pc].latest_time_acknowledged) { + retransmit_time = cnx->pkt_ctx[pc].highest_acknowledged_time; + } + } + } + else + { + /* There has not been any higher packet acknowledged, thus we fall back on timer logic. */ + retransmit_time = p->send_time + picoquic_current_retransmit_timer(cnx, pc); + is_timer_based = 1; + } + + if (p->ptype == picoquic_packet_0rtt_protected) { + /* Special case for 0RTT packets */ + if (cnx->cnx_state != picoquic_state_ready && + cnx->cnx_state != picoquic_state_client_ready_start) { + /* Set the retransmit time ahead of current time since the connection is not ready */ + retransmit_time = current_time + cnx->path[0]->smoothed_rtt + PICOQUIC_RACK_DELAY; + } else if (!cnx->zero_rtt_data_accepted) { + /* Zero RTT data was not accepted by the peer, the packets are considered lost */ + retransmit_time = current_time; + } + } + + if (current_time >= retransmit_time || (p->is_ack_trap && delta_seq > 0)) { + should_retransmit = 1; + *timer_based = is_timer_based; + if (cnx->quic->sequence_hole_pseudo_period != 0 && pc == picoquic_packet_context_application && !p->is_ack_trap) { + DBG_PRINTF("Retransmit #%d, delta=%d, timer=%d, time=%d, sent: %d, ack_t: %d, s_rtt: %d, rt: %d", + (int)p->sequence_number, (int)delta_seq, is_timer_based, (int)current_time, (int)p->send_time, + (int)cnx->pkt_ctx[pc].latest_time_acknowledged, (int)cnx->path[0]->smoothed_rtt, (int) retransmit_time); + } + } + + *next_retransmit_time = retransmit_time; + + return should_retransmit; +} + +int picoquic_queue_stream_frame_for_retransmit(picoquic_cnx_t* cnx, uint8_t * bytes, size_t length) +{ + int ret = 0; + picoquic_misc_frame_header_t* misc = picoquic_create_misc_frame(bytes, length, 0); + + if (misc == NULL) { + ret = PICOQUIC_ERROR_MEMORY; + } + else { + misc->next_misc_frame = NULL; + if (cnx->stream_frame_retransmit_queue_last == NULL) { + cnx->stream_frame_retransmit_queue = misc; + cnx->stream_frame_retransmit_queue_last = misc; + } + else { + misc->previous_misc_frame = cnx->stream_frame_retransmit_queue_last; + cnx->stream_frame_retransmit_queue_last->next_misc_frame = misc; + cnx->stream_frame_retransmit_queue_last = misc; + } + } + + return ret; +} + +int picoquic_copy_before_retransmit(picoquic_packet_t * old_p, + picoquic_cnx_t * cnx, + uint8_t * new_bytes, + size_t send_buffer_max_minus_checksum, + int * packet_is_pure_ack, + int * do_not_detect_spurious, + size_t * length) +{ + /* check if this is an ACK only packet */ + int ret = 0; + int frame_is_pure_ack = 0; + size_t frame_length = 0; + size_t byte_index = 0; /* Used when parsing the old packet */ + + if (old_p->is_mtu_probe) { + if (old_p->send_path != NULL) { + /* MTU probe was lost, presumably because of packet too big */ + old_p->send_path->mtu_probe_sent = 0; + old_p->send_path->send_mtu_max_tried = old_p->length + old_p->checksum_overhead; + } + /* MTU probes should not be retransmitted */ + *packet_is_pure_ack = 1; + *do_not_detect_spurious = 0; + } + else if (old_p->is_ack_trap) { + *packet_is_pure_ack = 1; + *do_not_detect_spurious = 0; + } + else { + /* Copy the relevant bytes from one packet to the next */ + byte_index = old_p->offset; + + while (ret == 0 && byte_index < old_p->length) { + ret = picoquic_skip_frame(&old_p->bytes[byte_index], + old_p->length - byte_index, &frame_length, &frame_is_pure_ack); + + /* Check whether the data was already acked, which may happen in + * case of spurious retransmissions */ + if (ret == 0 && frame_is_pure_ack == 0) { + ret = picoquic_check_frame_needs_repeat(cnx, &old_p->bytes[byte_index], + frame_length, &frame_is_pure_ack); + } + + /* Prepare retransmission if needed */ + if (ret == 0 && !frame_is_pure_ack) { + if (PICOQUIC_IN_RANGE(old_p->bytes[byte_index], picoquic_frame_type_stream_range_min, picoquic_frame_type_stream_range_max)) { + ret = picoquic_queue_stream_frame_for_retransmit(cnx, &old_p->bytes[byte_index], frame_length); + } + else { + if (frame_length > send_buffer_max_minus_checksum - *length && + (old_p->ptype == picoquic_packet_0rtt_protected || old_p->ptype == picoquic_packet_1rtt_protected)) { + ret = picoquic_queue_misc_frame(cnx, &old_p->bytes[byte_index], frame_length, 0); + } + else { + memcpy(&new_bytes[*length], &old_p->bytes[byte_index], frame_length); + *length += frame_length; + } + } + *packet_is_pure_ack = 0; + } + byte_index += frame_length; + } + } + + return ret; +} + +int picoquic_retransmit_needed(picoquic_cnx_t* cnx, + picoquic_packet_context_enum pc, + picoquic_path_t * path_x, uint64_t current_time, uint64_t * next_wake_time, + picoquic_packet_t* packet, size_t send_buffer_max, size_t* header_length) +{ + picoquic_packet_t* old_p = cnx->pkt_ctx[pc].retransmit_oldest; + size_t length = 0; + + /* TODO: while packets are pure ACK, drop them from retransmit queue */ + while (old_p != NULL) { + picoquic_path_t * old_path = old_p->send_path; /* should be the path on which the packet was transmitted */ + int should_retransmit = 0; + int timer_based_retransmit = 0; + uint64_t next_retransmit_time = *next_wake_time; + uint64_t lost_packet_number = old_p->sequence_number; + picoquic_packet_t* p_next = old_p->previous_packet; + uint8_t * new_bytes = packet->bytes; + int ret = 0; + + length = 0; + + /* Get the packet type */ + + should_retransmit = cnx->initial_repeat_needed || + picoquic_retransmit_needed_by_packet(cnx, old_p, current_time, &next_retransmit_time, &timer_based_retransmit); + + if (should_retransmit == 0) { + /* + * Always retransmit in order. If not this one, then nothing. + * But make an exception for 0-RTT packets. + */ + if (old_p->ptype == picoquic_packet_0rtt_protected) { + old_p = p_next; + continue; + } + else { + if (next_retransmit_time < *next_wake_time) { + *next_wake_time = next_retransmit_time; + SET_LAST_WAKE(cnx->quic, PICOQUIC_SENDER); + } + break; + } + } else if (old_p->is_ack_trap){ + picoquic_dequeue_retransmit_packet(cnx, old_p, 1); + old_p = p_next; + continue; + } else { + /* check if this is an ACK only packet */ + int packet_is_pure_ack = 1; + int do_not_detect_spurious = 1; + int frame_is_pure_ack = 0; + uint8_t* old_bytes = old_p->bytes; + size_t frame_length = 0; + size_t byte_index = 0; /* Used when parsing the old packet */ + size_t checksum_length = 0; + + /* we'll report it where it got lost */ + if (old_path) { + old_path->retrans_count++; + } + + *header_length = 0; + + if (old_p->ptype == picoquic_packet_0rtt_protected) { + /* Only retransmit as 0-RTT if contains crypto data */ + int contains_crypto = 0; + byte_index = old_p->offset; + + if (old_p->is_evaluated == 0) { + while (ret == 0 && byte_index < old_p->length) { + if (old_bytes[byte_index] == picoquic_frame_type_crypto_hs) { + contains_crypto = 1; + packet_is_pure_ack = 0; + break; + } + ret = picoquic_skip_frame(&old_p->bytes[byte_index], + old_p->length - byte_index, &frame_length, &frame_is_pure_ack); + byte_index += frame_length; + } + old_p->contains_crypto = contains_crypto; + old_p->is_pure_ack = packet_is_pure_ack; + old_p->is_evaluated = 1; + } else { + contains_crypto = old_p->contains_crypto; + packet_is_pure_ack = old_p->is_pure_ack; + } + + if (contains_crypto) { + length = picoquic_predict_packet_header_length(cnx, picoquic_packet_0rtt_protected); + packet->ptype = picoquic_packet_0rtt_protected; + packet->offset = length; + } else if (cnx->cnx_state < picoquic_state_client_ready_start) { + should_retransmit = 0; + } else { + length = picoquic_predict_packet_header_length(cnx, picoquic_packet_1rtt_protected); + packet->ptype = picoquic_packet_1rtt_protected; + packet->offset = length; + } + } else { + length = picoquic_predict_packet_header_length(cnx, old_p->ptype); + packet->ptype = old_p->ptype; + packet->offset = length; + } + + if (should_retransmit != 0) { + packet->sequence_number = cnx->pkt_ctx[pc].send_sequence; + packet->send_path = path_x; + packet->pc = pc; + + *header_length = length; + + switch (packet->ptype) { + case picoquic_packet_1rtt_protected: + checksum_length = picoquic_get_checksum_length(cnx, picoquic_epoch_1rtt); + break; + case picoquic_packet_initial: + checksum_length = picoquic_get_checksum_length(cnx, picoquic_epoch_initial); + break; + case picoquic_packet_handshake: + checksum_length = picoquic_get_checksum_length(cnx, picoquic_epoch_handshake); + break; + case picoquic_packet_0rtt_protected: + checksum_length = picoquic_get_checksum_length(cnx, picoquic_epoch_0rtt); + break; + default: + DBG_PRINTF("Trying to retransmit packet type %d", old_p->ptype); + checksum_length = 0; + break; + } + + ret = picoquic_copy_before_retransmit(old_p, cnx, + new_bytes, + send_buffer_max - checksum_length, + &packet_is_pure_ack, + &do_not_detect_spurious, + &length); + + if (ret != 0) { + DBG_PRINTF("Copy before retransmit returns %d\n", ret); + } + + /* Update the number of bytes in transit and remove old packet from queue */ + /* If not pure ack, the packet will be placed in the "retransmitted" queue, + * in order to enable detection of spurious restransmissions */ + + picoquic_log_packet_lost(cnx, old_p->ptype, old_p->sequence_number, + (timer_based_retransmit == 0) ? "repeat" : "timer", + (old_p->send_path == NULL) ? NULL : &old_p->send_path->remote_cnxid, + old_p->length, current_time); + + old_p = picoquic_dequeue_retransmit_packet(cnx, old_p, packet_is_pure_ack & do_not_detect_spurious); + + /* If we have a good packet, return it */ + if (old_p == NULL || packet_is_pure_ack) { + length = 0; + } else { + if (old_p->send_path != NULL && + (old_p->length + old_p->checksum_overhead) == old_p->send_path->send_mtu) { + old_p->send_path->nb_mtu_losses++; + if (old_p->send_path->nb_mtu_losses > PICOQUIC_MTU_LOSS_THRESHOLD) { + picoquic_reset_path_mtu(old_p->send_path); + picoquic_log_app_message(cnx, + "Reset path MTU after %d retransmissions, %d MTU losses", + cnx->pkt_ctx[pc].nb_retransmit, + old_p->send_path->nb_mtu_losses); + } + } + + if (timer_based_retransmit != 0) { + if (cnx->pkt_ctx[pc].nb_retransmit > 7 && cnx->cnx_state >= picoquic_state_ready) { + /* + * Max retransmission count was exceeded. Disconnect. + */ + DBG_PRINTF("Too many retransmits of packet number %d, disconnect", (int)old_p->sequence_number); + cnx->cnx_state = picoquic_state_disconnected; + if (cnx->callback_fn) { + (void)(cnx->callback_fn)(cnx, 0, NULL, 0, picoquic_callback_close, cnx->callback_ctx, NULL); + } + length = 0; + break; + } else { + if (cnx->pkt_ctx[pc].nb_retransmit == 0 || + old_p->sequence_number >= cnx->pkt_ctx[pc].retransmit_sequence) { + cnx->pkt_ctx[pc].nb_retransmit++; + cnx->pkt_ctx[pc].latest_retransmit_time = current_time; + cnx->pkt_ctx[pc].retransmit_sequence = packet->sequence_number; + } + } + } + + if (old_p->ptype < picoquic_packet_1rtt_protected) { + DBG_PRINTF("Retransmit packet type %d, pc=%d, seq = %llx, is_client = %d\n", + old_p->ptype, old_p->pc, + (unsigned long long)old_p->sequence_number, cnx->client_mode); + } + + /* special case for the client initial */ + if (old_p->ptype == picoquic_packet_initial && cnx->client_mode) { + length = picoquic_pad_to_target_length(new_bytes, length, send_buffer_max - checksum_length); + } + packet->length = length; + cnx->nb_retransmission_total++; + + if (old_path != NULL) { + old_path->nb_losses_found++; + old_path->total_bytes_lost += old_p->length; + + if (cnx->congestion_alg != NULL && cnx->cnx_state >= picoquic_state_ready) { + cnx->congestion_alg->alg_notify(cnx, old_path, + (timer_based_retransmit == 0) ? picoquic_congestion_notification_repeat : picoquic_congestion_notification_timeout, + 0, 0, 0, lost_packet_number, current_time); + } + } + + if (length <= packet->offset) { + length = 0; + if (!packet_is_pure_ack) { + /* Pace down the next retransmission so as to not pile up error upon error */ + path_x->pacing_bucket_nanosec -= path_x->pacing_packet_time_nanosec; + } + } + else { + break; + } + } + } + } + /* + * If the loop is continuing, this means that we need to look + * at the next candidate packet. + */ + old_p = p_next; + } + + return (int)length; +} + +/* + * Returns true if there is nothing to repeat in the retransmission queue + */ +int picoquic_is_cnx_backlog_empty(picoquic_cnx_t* cnx) +{ + int backlog_empty = 1; + + for (picoquic_packet_context_enum pc = 0; + backlog_empty == 1 && pc < picoquic_nb_packet_context; pc++) + { + picoquic_packet_t* p = cnx->pkt_ctx[pc].retransmit_oldest; + + if ((cnx->cnx_state == picoquic_state_ready) && (pc != picoquic_packet_context_application)) { + continue; + } + + while (p != NULL && backlog_empty == 1) { + /* check if this is an ACK only packet */ + int ret = 0; + int frame_is_pure_ack = 0; + size_t frame_length = 0; + size_t byte_index = 0; /* Used when parsing the old packet */ + + byte_index = p->offset; + + + while (ret == 0 && byte_index < p->length) { + ret = picoquic_skip_frame(&p->bytes[byte_index], + p->length - p->offset, &frame_length, &frame_is_pure_ack); + + if (!frame_is_pure_ack) { + backlog_empty = 0; + break; + } + byte_index += frame_length; + } + + p = p->previous_packet; + } + } + + return backlog_empty; +} + +/* Decide whether MAX data need to be sent or not */ +int picoquic_should_send_max_data(picoquic_cnx_t* cnx) +{ + int ret = 0; + + if (2 * cnx->data_received > cnx->maxdata_local) + ret = 1; + + return ret; +} + +/* Compute the next logical probe length */ +static size_t picoquic_next_mtu_probe_length(picoquic_cnx_t* cnx, picoquic_path_t * path_x) +{ + size_t probe_length; + + if (path_x->send_mtu_max_tried == 0) { + if (cnx->remote_parameters.max_packet_size > 0) { + probe_length = cnx->remote_parameters.max_packet_size; + + if (cnx->quic->mtu_max > 0 && (int)probe_length > cnx->quic->mtu_max) { + probe_length = cnx->quic->mtu_max; + } + else if (probe_length > PICOQUIC_MAX_PACKET_SIZE) { + probe_length = PICOQUIC_MAX_PACKET_SIZE; + } + if (probe_length < path_x->send_mtu) { + probe_length = path_x->send_mtu; + } + } + else if (cnx->quic->mtu_max > 0) { + probe_length = cnx->quic->mtu_max; + } + else { + probe_length = PICOQUIC_PRACTICAL_MAX_MTU; + } + } + else { + if (path_x->send_mtu_max_tried > 1500) { + probe_length = 1500; + } + else if (path_x->send_mtu_max_tried > 1400) { + probe_length = 1400; + } + else { + probe_length = (path_x->send_mtu + path_x->send_mtu_max_tried) / 2; + } + } + + return probe_length; +} + +/* Decide whether to send an MTU probe */ +picoquic_pmtu_discovery_status_enum picoquic_is_mtu_probe_needed(picoquic_cnx_t* cnx, picoquic_path_t * path_x) +{ + int ret = picoquic_pmtu_discovery_not_needed; + + if ((cnx->cnx_state == picoquic_state_ready || cnx->cnx_state == picoquic_state_client_ready_start || cnx->cnx_state == picoquic_state_server_false_start) + && path_x->mtu_probe_sent == 0) { + if (path_x->send_mtu_max_tried == 0 || path_x->send_mtu_max_tried > 1400) { + /* MTU discovery is required if the chances of success are large enough + * and there are enough packets to send to amortize the discovery cost. + * Of course we don't know at this stage how much data will be sent + * on the connection; we take the amount of data queued as a proxy + * for that. */ + uint64_t next_probe = picoquic_next_mtu_probe_length(cnx, path_x); + if (next_probe > path_x->send_mtu) { + if (cnx->is_pmtud_required) { + ret = picoquic_pmtu_discovery_required; + } + else { + uint64_t packets_to_send_before = cnx->nb_bytes_queued / path_x->send_mtu; + uint64_t packets_to_send_after = cnx->nb_bytes_queued / next_probe; + uint64_t delta = (packets_to_send_before - packets_to_send_after) * 60; + if (delta > next_probe) { + ret = picoquic_pmtu_discovery_required; + } + else { + ret = picoquic_pmtu_discovery_optional; + } + } + } + } + } + + return ret; +} + +/* Prepare an MTU probe packet */ +size_t picoquic_prepare_mtu_probe(picoquic_cnx_t* cnx, + picoquic_path_t * path_x, + size_t header_length, size_t checksum_length, + uint8_t* bytes, size_t bytes_max) +{ + size_t probe_length = picoquic_next_mtu_probe_length(cnx, path_x); + size_t length = header_length; + + if (probe_length > bytes_max) { + probe_length = bytes_max; + } + + bytes[length++] = picoquic_frame_type_ping; + memset(&bytes[length], 0, probe_length - checksum_length - length); + + return probe_length - checksum_length; +} + +/* Prepare the next packet to 0-RTT packet to send in the client initial + * state, when 0-RTT is available + */ +int picoquic_prepare_packet_0rtt(picoquic_cnx_t* cnx, picoquic_path_t * path_x, picoquic_packet_t* packet, + uint64_t current_time, uint8_t* send_buffer, size_t send_buffer_max, size_t* send_length, + int padding_required, uint64_t * next_wake_time) +{ + int ret = 0; + picoquic_stream_head_t* stream = NULL; + picoquic_packet_type_enum packet_type = picoquic_packet_0rtt_protected; + size_t header_length = 0; + uint8_t* bytes = packet->bytes; + size_t length = 0; + size_t checksum_overhead = picoquic_aead_get_checksum_length(cnx->crypto_context[1].aead_encrypt); + uint8_t* bytes_max; + uint8_t* bytes_next; + int more_data = 0; + int is_pure_ack = 1; + int stream_tried_and_failed = 0; + + send_buffer_max = (send_buffer_max > path_x->send_mtu) ? path_x->send_mtu : send_buffer_max; + if (path_x->bytes_in_transit + send_buffer_max > PICOQUIC_DEFAULT_0RTT_WINDOW) { + if (path_x->bytes_in_transit > PICOQUIC_DEFAULT_0RTT_WINDOW) { + send_buffer_max = 0; + } + else { + send_buffer_max = (size_t)PICOQUIC_DEFAULT_0RTT_WINDOW - (size_t)path_x->bytes_in_transit; + } + } + bytes_max = bytes + send_buffer_max - checksum_overhead; + + stream = picoquic_find_ready_stream(cnx); + length = picoquic_predict_packet_header_length(cnx, packet_type); + packet->ptype = picoquic_packet_0rtt_protected; + packet->offset = length; + header_length = length; + packet->pc = picoquic_packet_context_application; + packet->sequence_number = cnx->pkt_ctx[picoquic_packet_context_application].send_sequence; + packet->send_time = current_time; + packet->send_path = path_x; + packet->checksum_overhead = checksum_overhead; + bytes_next = bytes + length; + + + + /* Consider sending 0-RTT */ + if ((stream == NULL && cnx->first_misc_frame == NULL && padding_required == 0) || + send_buffer_max < PICOQUIC_MIN_SEGMENT_SIZE) { + length = 0; + } else { + /* If present, send misc frame */ + while (cnx->first_misc_frame != NULL) { + uint8_t* bytes_misc = bytes_next; + bytes_next = picoquic_format_first_misc_frame(cnx, bytes_next, bytes_max, &more_data, &is_pure_ack); + if (bytes_next == bytes_misc) { + break; + } + } + + /* Encode the stream frame, or frames */ + bytes_next = picoquic_format_available_stream_frames(cnx, bytes_next, bytes_max, &more_data, &is_pure_ack, &stream_tried_and_failed, &ret); + + length = bytes_next - bytes; + + if (more_data) { + *next_wake_time = current_time; + SET_LAST_WAKE(cnx->quic, PICOQUIC_SENDER); + } + + if (stream_tried_and_failed) { + path_x->last_sender_limited_time = current_time; + } + + /* Add padding if required */ + if (padding_required) { + length = picoquic_pad_to_target_length(bytes, length, send_buffer_max - checksum_overhead); + } + } + + picoquic_finalize_and_protect_packet(cnx, packet, + ret, length, header_length, checksum_overhead, + send_length, send_buffer, send_buffer_max, + &path_x->remote_cnxid, + &path_x->p_local_cnxid->cnx_id, + path_x, current_time); + + if (length > 0) { + /* Accounting of zero rtt packets sent */ + cnx->nb_zero_rtt_sent++; + } + + /* the reinsertion by wake up time will happen in the calling function */ + + return ret; +} + +/* Get packet type from epoch */ +picoquic_packet_type_enum picoquic_packet_type_from_epoch(int epoch) +{ + picoquic_packet_type_enum ptype; + + switch (epoch) { + case 0: + ptype = picoquic_packet_initial; + break; + case 1: + ptype = picoquic_packet_0rtt_protected; + break; + case 2: + ptype = picoquic_packet_handshake; + break; + case 3: + ptype = picoquic_packet_1rtt_protected; + break; + default: + ptype = picoquic_packet_error; + break; + } + + return ptype; +} + +/* Prepare a required repetition or ack in a previous context */ +size_t picoquic_prepare_packet_old_context(picoquic_cnx_t* cnx, picoquic_packet_context_enum pc, + picoquic_path_t* path_x, picoquic_packet_t* packet, size_t send_buffer_max, uint64_t current_time, + uint64_t* next_wake_time, size_t* header_length) +{ + picoquic_epoch_enum epoch = (pc == picoquic_packet_context_initial) ? picoquic_epoch_initial : + (pc == picoquic_packet_context_application) ? picoquic_epoch_0rtt : picoquic_epoch_handshake; + size_t length = 0; + uint8_t* bytes = packet->bytes; + int more_data = 0; + size_t checksum_overhead = picoquic_get_checksum_length(cnx, epoch); + uint8_t* bytes_max = bytes + send_buffer_max - checksum_overhead; + uint8_t* bytes_next; + + *header_length = 0; + + send_buffer_max = (send_buffer_max > path_x->send_mtu) ? path_x->send_mtu : send_buffer_max; + + length = picoquic_retransmit_needed(cnx, pc, path_x, current_time, next_wake_time, packet, send_buffer_max, header_length); + if (length > 0 && (pc == picoquic_packet_context_handshake || cnx->pkt_ctx[picoquic_packet_context_handshake].retransmit_oldest == NULL || + cnx->cnx_state == picoquic_state_server_init || cnx->cnx_state == picoquic_state_server_handshake)) { + cnx->initial_repeat_needed = 0; + } + + if (length == 0 && cnx->pkt_ctx[pc].ack_needed != 0 && + pc != picoquic_packet_context_application) { + packet->ptype = + (pc == picoquic_packet_context_initial) ? picoquic_packet_initial : + (pc == picoquic_packet_context_handshake) ? picoquic_packet_handshake : + picoquic_packet_0rtt_protected; + length = picoquic_predict_packet_header_length(cnx, packet->ptype); + packet->offset = length; + *header_length = length; + packet->sequence_number = cnx->pkt_ctx[pc].send_sequence; + packet->send_time = current_time; + packet->send_path = path_x; + } + + if (length > 0) { + if (packet->ptype != picoquic_packet_0rtt_protected) { + /* Check whether it makes sens to add an ACK at the end of the retransmission */ + bytes_next = picoquic_format_ack_frame(cnx, bytes + length, bytes_max, &more_data, current_time, pc); + length = bytes_next - bytes; + } + packet->length = length; + /* document the send time & overhead */ + packet->send_time = current_time; + packet->checksum_overhead = checksum_overhead; + packet->pc = pc; + } + + return length; +} + +/* Empty the handshake repeat queues when transitioning to the completely ready state */ +void picoquic_implicit_handshake_ack(picoquic_cnx_t* cnx, picoquic_packet_context_enum pc, uint64_t current_time) +{ + picoquic_packet_t* p = cnx->pkt_ctx[pc].retransmit_oldest; + + /* Remove packets from the retransmit queue */ + while (p != NULL) { + picoquic_packet_t* p_next = p->next_packet; + picoquic_path_t * old_path = p->send_path; + + /* Update the congestion control state for the path, but only for the packets sent + * before the initial timer. */ + if (old_path != NULL && cnx->congestion_alg != NULL && p->send_time < cnx->start_time + PICOQUIC_INITIAL_RTT) { + cnx->congestion_alg->alg_notify(cnx, old_path, + picoquic_congestion_notification_acknowledgement, + 0, 0, p->length, 0, current_time); + } + /* Update the number of bytes in transit and remove old packet from queue */ + /* The packet will not be placed in the "retransmitted" queue */ + (void)picoquic_dequeue_retransmit_packet(cnx, p, 1); + + p = p_next; + } +} + +/* Program a migration to the server preferred address if present */ +int picoquic_prepare_server_address_migration(picoquic_cnx_t* cnx) +{ + int ret = 0; + + if (cnx->remote_parameters.prefered_address.is_defined) + { + int ipv4_received = cnx->remote_parameters.prefered_address.ipv4Port != 0; + int ipv6_received = cnx->remote_parameters.prefered_address.ipv6Port != 0; + + /* Add the connection ID to the local stash */ + ret = picoquic_enqueue_cnxid_stash(cnx, 1, + cnx->remote_parameters.prefered_address.connection_id.id_len, + cnx->remote_parameters.prefered_address.connection_id.id, + cnx->remote_parameters.prefered_address.statelessResetToken, + NULL); + if (ret != 0) { + ret = picoquic_connection_error(cnx, (uint16_t)ret, picoquic_frame_type_new_connection_id); + } + else if(ipv4_received || ipv6_received) { + struct sockaddr_storage dest_addr; + + memset(&dest_addr, 0, sizeof(struct sockaddr_storage)); + + /* program a migration. */ + if (ipv4_received && cnx->path[0]->peer_addr.ss_family == AF_INET) { + /* select IPv4 */ + ipv6_received = 0; + } + + if (ipv6_received) { + /* configure an IPv6 sockaddr */ + struct sockaddr_in6 * d6 = (struct sockaddr_in6 *)&dest_addr; + d6->sin6_family = AF_INET6; + d6->sin6_port = htons(cnx->remote_parameters.prefered_address.ipv6Port); + memcpy(&d6->sin6_addr, cnx->remote_parameters.prefered_address.ipv6Address, 16); + } + else { + /* configure an IPv4 sockaddr */ + struct sockaddr_in * d4 = (struct sockaddr_in *)&dest_addr; + d4->sin_family = AF_INET; + d4->sin_port = htons(cnx->remote_parameters.prefered_address.ipv4Port); + memcpy(&d4->sin_addr, cnx->remote_parameters.prefered_address.ipv4Address, 4); + } + + /* Only send a probe if not already using that address */ + if (picoquic_compare_addr((struct sockaddr *)&dest_addr, (struct sockaddr *)&cnx->path[0]->peer_addr) != 0) { + struct sockaddr* local_addr = NULL; + if (cnx->path[0]->local_addr.ss_family != 0) { + local_addr = (struct sockaddr*) & cnx->path[0]->local_addr; + } + + ret = picoquic_probe_new_path_ex(cnx, (struct sockaddr *)&dest_addr, local_addr, + picoquic_get_quic_time(cnx->quic), 1); + } + } + } + + return ret; +} + +/* Prepare the next packet to send when in one of the client initial states */ +int picoquic_prepare_packet_client_init(picoquic_cnx_t* cnx, picoquic_path_t * path_x, picoquic_packet_t* packet, + uint64_t current_time, uint8_t* send_buffer, size_t send_buffer_max, size_t* send_length, uint64_t * next_wake_time, + int * is_initial_sent) +{ + int ret = 0; + int tls_ready = 0; + picoquic_packet_type_enum packet_type = 0; + size_t checksum_overhead = 16; + int is_cleartext_mode = 1; + int retransmit_possible = 0; + size_t header_length = 0; + uint8_t* bytes = packet->bytes; + uint8_t* bytes_max; + uint8_t* bytes_next; + size_t length = 0; + int epoch = 0; + int is_pure_ack = 1; + int more_data = 0; + picoquic_packet_context_enum pc = picoquic_packet_context_initial; + + if (*next_wake_time > cnx->start_time + PICOQUIC_MICROSEC_HANDSHAKE_MAX) { + *next_wake_time = cnx->start_time + PICOQUIC_MICROSEC_HANDSHAKE_MAX; + SET_LAST_WAKE(cnx->quic, PICOQUIC_SENDER); + } + + cnx->initial_validated = 1; /* always validated on client */ + + if (cnx->tls_stream[0].send_queue == NULL) { + if (cnx->crypto_context[1].aead_encrypt != NULL && + cnx->tls_stream[1].send_queue != NULL) { + epoch = 1; + pc = picoquic_packet_context_application; + } else if (cnx->crypto_context[2].aead_encrypt != NULL && + cnx->tls_stream[1].send_queue == NULL) { + epoch = 2; + pc = picoquic_packet_context_handshake; + } + } + + packet_type = picoquic_packet_type_from_epoch(epoch); + + send_buffer_max = (send_buffer_max > path_x->send_mtu) ? path_x->send_mtu : send_buffer_max; + + /* Prepare header -- depend on connection state */ + switch (cnx->cnx_state) { + case picoquic_state_client_init: + if (cnx->retry_token_length == 0 && cnx->sni != NULL) { + (void)picoquic_get_token(cnx->quic->p_first_token, current_time, cnx->sni, (uint16_t)strlen(cnx->sni), + NULL, 0, &cnx->retry_token, &cnx->retry_token_length, 1); + } + break; + case picoquic_state_client_init_sent: + case picoquic_state_client_init_resent: + retransmit_possible = 1; + break; + case picoquic_state_client_renegotiate: + packet_type = picoquic_packet_initial; + break; + case picoquic_state_client_handshake_start: + retransmit_possible = 1; + break; + case picoquic_state_client_almost_ready: + break; + default: + ret = -1; + break; + } + + /* If context is handshake, verify first that there is no need for retransmit or ack + * on initial context */ + int force_handshake_padding = 0; + + if (ret == 0) { + if (epoch > picoquic_epoch_initial) { + if (cnx->crypto_context[picoquic_epoch_handshake].aead_encrypt != NULL) { + if (cnx->pkt_ctx[picoquic_packet_context_initial].ack_needed) { + /* Apply some ack delay, because handshake from server arrive in trains */ + uint64_t ack_delay = cnx->path[0]->smoothed_rtt / 8; + uint64_t ack_time; + if (ack_delay > PICOQUIC_ACK_DELAY_MAX) { + ack_delay = PICOQUIC_ACK_DELAY_MAX; + } + ack_time = cnx->pkt_ctx[picoquic_packet_context_initial].time_oldest_unack_packet_received + ack_delay; + if (ack_time <= current_time) { + force_handshake_padding = 1; + } + else if (ack_time < *next_wake_time) { + *next_wake_time = ack_time; + SET_LAST_WAKE(cnx->quic, PICOQUIC_SENDER); + } + } + else if (!force_handshake_padding && cnx->pkt_ctx[pc].retransmit_newest != NULL) { + /* There is a risk of deadlock if the server is doing DDOS mitigation + * and does not receive the Handshake sent by the client. If more than RTT has elapsed since + * the last handshake packet was sent, force another one to be sent. */ + uint64_t rto = picoquic_current_retransmit_timer(cnx, picoquic_packet_context_handshake); + uint64_t repeat_time = cnx->pkt_ctx[pc].retransmit_newest->send_time + rto; + + if (repeat_time <= current_time) { + force_handshake_padding = 1; + cnx->pkt_ctx[pc].nb_retransmit++; + } + else if (repeat_time < *next_wake_time) { + *next_wake_time = repeat_time; + SET_LAST_WAKE(cnx->quic, PICOQUIC_SENDER); + } + } + } + else { + length = picoquic_prepare_packet_old_context(cnx, picoquic_packet_context_initial, + path_x, packet, send_buffer_max, current_time, next_wake_time, &header_length); + *is_initial_sent |= (length > 0); + } + } + else { + /* There is a risk of deadlock if the server is doing DDOS mitigation + * and does not repeat an initial or handshake packet that was lost. If more than RTT has elapsed since + * the last initial packet was sent, force another one to be sent. */ + uint64_t rto = picoquic_current_retransmit_timer(cnx, picoquic_packet_context_initial); + uint64_t repeat_time = cnx->path[0]->latest_sent_time + rto; + force_handshake_padding = (repeat_time <= current_time); + } + } + + if (ret == 0 && epoch > picoquic_epoch_0rtt && length == 0 && + cnx->crypto_context[picoquic_epoch_0rtt].aead_encrypt != NULL) { + length = picoquic_prepare_packet_old_context(cnx, picoquic_packet_context_application, + path_x, packet, send_buffer_max, current_time, next_wake_time, &header_length); + } + + /* If there is nothing to send in previous context, check this one too */ + if (length == 0) { + checksum_overhead = picoquic_get_checksum_length(cnx, epoch); + packet->checksum_overhead = checksum_overhead; + bytes_max = bytes + send_buffer_max - checksum_overhead; + packet->pc = pc; + + tls_ready = picoquic_is_tls_stream_ready(cnx); + + if (ret == 0 && retransmit_possible && + (length = picoquic_retransmit_needed(cnx, pc, path_x, current_time, next_wake_time, packet, send_buffer_max, &header_length)) > 0) { + /* Check whether it makes sens to add an ACK at the end of the retransmission */ + if (epoch != picoquic_epoch_0rtt) { + bytes_next = picoquic_format_ack_frame(cnx, bytes + length, bytes_max, &more_data, current_time, pc); + length = bytes_next - bytes; + } + /* document the send time & overhead */ + packet->length = length; + packet->send_time = current_time; + packet->checksum_overhead = checksum_overhead; + } + else if (ret == 0 && is_cleartext_mode && tls_ready == 0 + && cnx->first_misc_frame == NULL && !cnx->pkt_ctx[pc].ack_needed && !force_handshake_padding) { + /* when in a clear text mode, only send packets if there is + * actually something to send, or resend. */ + + packet->length = 0; + } + else if (ret == 0) { + if (cnx->crypto_context[epoch].aead_encrypt == NULL) { + packet->length = 0; + } + else { + length = picoquic_predict_packet_header_length(cnx, packet_type); + packet->ptype = packet_type; + packet->offset = length; + header_length = length; + packet->sequence_number = cnx->pkt_ctx[pc].send_sequence; + packet->send_time = current_time; + packet->send_path = path_x; + bytes_next = bytes + length; + bytes_max = bytes + send_buffer_max - checksum_overhead; + + if ((tls_ready == 0 || path_x->cwin <= path_x->bytes_in_transit) + && (cnx->cnx_state == picoquic_state_client_almost_ready + || picoquic_is_ack_needed(cnx, current_time, next_wake_time, pc) == 0) + && cnx->first_misc_frame == NULL && !force_handshake_padding) { + length = 0; + } + else { + if (epoch != 1 && cnx->pkt_ctx[pc].ack_needed) { + bytes_next = picoquic_format_ack_frame(cnx, bytes_next, bytes_max, &more_data, current_time, pc); + } + + /* If present, send misc frame */ + while (cnx->first_misc_frame != NULL) { + uint8_t* bytes_misc = bytes_next; + bytes_next = picoquic_format_first_misc_frame(cnx, bytes_next, bytes_max, &more_data, &is_pure_ack); + if (bytes_next == bytes_misc) { + break; + } + } + length = bytes_next - bytes; + + if (ret == 0 && path_x->cwin > path_x->bytes_in_transit) { + /* Encode the crypto handshake frame */ + if (tls_ready != 0) { + /* Encode the crypto frame */ + bytes_next = picoquic_format_crypto_hs_frame(&cnx->tls_stream[epoch], + bytes_next, bytes_max, &more_data, &is_pure_ack); + length = bytes_next - bytes; + } + + if (packet_type == picoquic_packet_initial) { + *is_initial_sent = 1; + if (cnx->crypto_context[1].aead_encrypt == NULL || + cnx->cnx_state == picoquic_state_client_renegotiate || + cnx->original_cnxid.id_len != 0) { + /* Pad to minimum packet length. But don't do that if the + * initial packet will be coalesced with 0-RTT packet */ + length = picoquic_pad_to_target_length(bytes, length, send_buffer_max - checksum_overhead); + } + } + } + + if (length == header_length) { + length = picoquic_pad_to_target_length(bytes, length, length + 8); + } + + if (length > header_length && epoch == picoquic_epoch_handshake) { + cnx->pkt_ctx[picoquic_packet_context_initial].ack_needed = 0; + } + + /* If TLS packets are sent, progress the state */ + if (ret == 0 && tls_ready != 0 && + cnx->tls_stream[epoch].send_queue == NULL) { + switch (cnx->cnx_state) { + case picoquic_state_client_init: + cnx->cnx_state = picoquic_state_client_init_sent; + break; + case picoquic_state_client_renegotiate: + cnx->cnx_state = picoquic_state_client_init_resent; + break; + case picoquic_state_client_almost_ready: + if (cnx->tls_stream[0].send_queue == NULL && + cnx->tls_stream[1].send_queue == NULL && + cnx->tls_stream[2].send_queue == NULL) { + cnx->cnx_state = picoquic_state_client_ready_start; + /* Reset the HS retransmission count, since end of flight counts as acknowledgement */ + cnx->pkt_ctx[picoquic_packet_context_handshake].nb_retransmit = 0; + /* Signal the application, because data can now be sent. */ + if (cnx->callback_fn != NULL) { + if (cnx->callback_fn(cnx, 0, NULL, 0, picoquic_callback_almost_ready, cnx->callback_ctx, NULL) != 0) { + picoquic_log_app_message(cnx, "Callback almost ready returns error 0x%x", PICOQUIC_TRANSPORT_INTERNAL_ERROR); + picoquic_connection_error(cnx, PICOQUIC_TRANSPORT_INTERNAL_ERROR, 0); + } + } + } + break; + default: + break; + } + } + } + } + } + } + + if (ret == 0 && length == 0) { + /* In some circumstances, there is a risk that the handshakes stops because the + * server is performing anti-dos mitigation and the client has nothing to repeat */ + if ((packet->ptype == picoquic_packet_initial && cnx->crypto_context[picoquic_epoch_handshake].aead_encrypt == NULL && + cnx->pkt_ctx[picoquic_packet_context_initial].retransmit_newest == NULL && + cnx->pkt_ctx[picoquic_packet_context_initial].first_sack_item.end_of_sack_range != UINT64_MAX) || + (packet->ptype == picoquic_packet_handshake && + cnx->pkt_ctx[picoquic_packet_context_handshake].retransmit_newest == NULL && + cnx->pkt_ctx[picoquic_packet_context_handshake].first_sack_item.end_of_sack_range == UINT64_MAX && + cnx->pkt_ctx[picoquic_packet_context_handshake].send_sequence == 0)) + { + uint64_t try_time_next = cnx->path[0]->latest_sent_time + cnx->path[0]->smoothed_rtt; + if (current_time < try_time_next) { + /* schedule a wake time to repeat the probing. */ + if (*next_wake_time > try_time_next) { + *next_wake_time = try_time_next; + SET_LAST_WAKE(cnx->quic, PICOQUIC_SENDER); + } + } + else { + length = header_length; + packet->offset = length; + if (packet->ptype == picoquic_packet_initial) { + /* Repeat an ACK because it helps. */ + bytes_max = bytes + send_buffer_max - checksum_overhead; + bytes_next = picoquic_format_ack_frame(cnx, bytes + length, bytes_max, &more_data, current_time, pc); + length = bytes_next - bytes; + + length = picoquic_pad_to_target_length(bytes, length, send_buffer_max - checksum_overhead); + } + else { + length = picoquic_pad_to_target_length(bytes, length, length + 8); + } + } + } + } + + if (ret == 0 && length == 0 && cnx->crypto_context[1].aead_encrypt != NULL) { + ret = picoquic_prepare_packet_0rtt(cnx, path_x, packet, current_time, send_buffer, send_buffer_max, send_length, + *is_initial_sent, next_wake_time); + } + else { + if (ret == 0 && more_data) { + *next_wake_time = current_time; + SET_LAST_WAKE(cnx->quic, PICOQUIC_SENDER); + } + + if (ret == 0 && *is_initial_sent) { + if (packet->ptype == picoquic_packet_initial) { + if (length > 0 && cnx->crypto_context[1].aead_encrypt == NULL && + (cnx->crypto_context[2].aead_encrypt == NULL || length + checksum_overhead + PICOQUIC_MIN_SEGMENT_SIZE > send_buffer_max || + !picoquic_is_tls_stream_ready(cnx))) { + length = picoquic_pad_to_target_length(bytes, length, send_buffer_max - checksum_overhead); + } + } + else if (packet->ptype == picoquic_packet_handshake && length + checksum_overhead < send_buffer_max && + (cnx->crypto_context[3].aead_encrypt == NULL || length + checksum_overhead + PICOQUIC_MIN_SEGMENT_SIZE > send_buffer_max)) { + length = picoquic_pad_to_target_length(bytes, length, send_buffer_max - checksum_overhead); + } + else if (packet->ptype == picoquic_packet_1rtt_protected) { + length = picoquic_pad_to_target_length(bytes, length, send_buffer_max - checksum_overhead); + } + } + + if (length > 0 && packet->ptype == picoquic_packet_handshake && !is_pure_ack) { + /* Sending an ack eliciting handshake packet terminates the use of the initial context */ + picoquic_implicit_handshake_ack(cnx, picoquic_packet_context_initial, current_time); + picoquic_crypto_context_free(&cnx->crypto_context[picoquic_epoch_initial]); + } + + picoquic_finalize_and_protect_packet(cnx, packet, + ret, length, header_length, checksum_overhead, + send_length, send_buffer, send_buffer_max, + &path_x->remote_cnxid, &path_x->p_local_cnxid->cnx_id, path_x, current_time); + } + + return ret; +} + +/* Compute the time at which to send the next challenge + */ + +uint64_t picoquic_next_challenge_time(picoquic_cnx_t* cnx, picoquic_path_t* path_x) +{ + uint64_t next_challenge_time = path_x->challenge_time; + + if (path_x->challenge_repeat_count >= 2) { + next_challenge_time += path_x->retransmit_timer << path_x->challenge_repeat_count; + } + else { + next_challenge_time += PICOQUIC_INITIAL_RETRANSMIT_TIMER; + } + + return next_challenge_time; +} + +/* Prepare the next packet to send when in one the server initial states */ +int picoquic_prepare_packet_server_init(picoquic_cnx_t* cnx, picoquic_path_t * path_x, picoquic_packet_t* packet, + uint64_t current_time, uint8_t* send_buffer, size_t send_buffer_max, size_t* send_length, uint64_t * next_wake_time) +{ + int ret = 0; + int tls_ready = 0; + picoquic_epoch_enum epoch = picoquic_epoch_initial; + picoquic_packet_type_enum packet_type = picoquic_packet_initial; + picoquic_packet_context_enum pc = picoquic_packet_context_initial; + size_t checksum_overhead = 8; + size_t header_length = 0; + uint8_t* bytes = packet->bytes; + uint8_t* bytes_max; + uint8_t* bytes_next; + size_t length = 0; + int more_data = 0; + int is_pure_ack = 1; + + if (*next_wake_time > cnx->start_time + PICOQUIC_MICROSEC_HANDSHAKE_MAX) { + *next_wake_time = cnx->start_time + PICOQUIC_MICROSEC_HANDSHAKE_MAX; + SET_LAST_WAKE(cnx->quic, PICOQUIC_SENDER); + } + + /* The only purpose of the test below is to appease the static analyzer, so it + * wont complain of possible NULL deref. On windows we could use "__assume(path_x != NULL)" + * but the documentation does not say anything about that for GCC and CLANG */ + if (path_x == NULL) { + return PICOQUIC_ERROR_UNEXPECTED_ERROR; + } + + if (cnx->crypto_context[picoquic_epoch_handshake].aead_encrypt != NULL && + cnx->tls_stream[0].send_queue == NULL) { + epoch = picoquic_epoch_handshake; + pc = picoquic_packet_context_handshake; + packet_type = picoquic_packet_handshake; + } + + send_buffer_max = (send_buffer_max > path_x->send_mtu) ? path_x->send_mtu : send_buffer_max; + + if (!cnx->initial_validated && + (cnx->initial_data_sent + send_buffer_max) > 3 * cnx->initial_data_received){ + /* Sending more data now would break the amplication limit */ + *send_length = 0; + return 0; + } + + /* If context is handshake, verify first that there is no need for retransmit or ack + * on initial context */ + if (pc == picoquic_packet_context_handshake) { + length = picoquic_prepare_packet_old_context(cnx, picoquic_packet_context_initial, + path_x, packet, send_buffer_max, current_time, next_wake_time, &header_length); + } + + if (length == 0) { + checksum_overhead = picoquic_get_checksum_length(cnx, epoch); + bytes_max = bytes + send_buffer_max - checksum_overhead; + tls_ready = (cnx->tls_stream[epoch].send_queue != NULL && + cnx->tls_stream[epoch].send_queue->length > cnx->tls_stream[epoch].send_queue->offset); + length = picoquic_predict_packet_header_length(cnx, packet_type); + packet->ptype = packet_type; + packet->offset = length; + header_length = length; + packet->sequence_number = cnx->pkt_ctx[pc].send_sequence; + packet->send_time = current_time; + packet->send_path = path_x; + packet->pc = pc; + bytes_next = bytes + length; + + if ((tls_ready != 0 && path_x->cwin > path_x->bytes_in_transit) + || cnx->pkt_ctx[pc].ack_needed) { + bytes_next = picoquic_format_ack_frame(cnx, bytes_next, bytes_max, &more_data, current_time, pc); + /* Encode the crypto frame */ + bytes_next = picoquic_format_crypto_hs_frame(&cnx->tls_stream[epoch], + bytes_next, bytes_max, &more_data, &is_pure_ack); + length = bytes_next - bytes; + + /* progress the state if the epoch data is all sent */ + if (ret == 0 && tls_ready != 0 && cnx->tls_stream[epoch].send_queue == NULL) { + if (epoch == picoquic_epoch_handshake && picoquic_tls_client_authentication_activated(cnx->quic) == 0) { + cnx->cnx_state = picoquic_state_server_false_start; + /* On a server that does address validation, send a NEW TOKEN frame */ + if (!cnx->client_mode && (cnx->quic->check_token || cnx->quic->provide_token)) { + uint8_t token_buffer[256]; + size_t token_size; + picoquic_connection_id_t n_cid = picoquic_null_connection_id; + + if (picoquic_prepare_retry_token(cnx->quic, (struct sockaddr *)&cnx->path[0]->peer_addr, + current_time + PICOQUIC_TOKEN_DELAY_LONG, &n_cid, &n_cid, 0, + token_buffer, sizeof(token_buffer), &token_size) == 0) { + if (picoquic_queue_new_token_frame(cnx, token_buffer, token_size) != 0) { + picoquic_connection_error(cnx, PICOQUIC_TRANSPORT_INTERNAL_ERROR, picoquic_frame_type_new_token); + } + } + } + if (cnx->callback_fn != NULL) { + if (cnx->callback_fn(cnx, 0, NULL, 0, picoquic_callback_almost_ready, cnx->callback_ctx, NULL) != 0) { + picoquic_log_app_message(cnx, "Callback almost ready returns error 0x%x", PICOQUIC_TRANSPORT_INTERNAL_ERROR); + picoquic_connection_error(cnx, PICOQUIC_TRANSPORT_INTERNAL_ERROR, 0); + } + } + } + else { + cnx->cnx_state = picoquic_state_server_handshake; + } + } + packet->length = length; + } + else if ((length = picoquic_retransmit_needed(cnx, pc, path_x, current_time, next_wake_time, packet, send_buffer_max, &header_length)) > 0) { + /* Set the new checksum length */ + checksum_overhead = picoquic_get_checksum_length(cnx, epoch); + cnx->initial_repeat_needed = 0; + /* Check whether it makes sens to add an ACK at the end of the retransmission */ + bytes_max = bytes + send_buffer_max - checksum_overhead; + bytes_next = picoquic_format_ack_frame(cnx, bytes + length, bytes_max, &more_data, current_time, pc); + length = bytes_next - bytes; + packet->length = length; + /* document the send time & overhead */ + packet->send_time = current_time; + packet->checksum_overhead = checksum_overhead; + } + else if (cnx->pkt_ctx[pc].ack_needed) { + /* when in a handshake mode, send acks asap. */ + length = picoquic_predict_packet_header_length(cnx, packet_type); + bytes_next = bytes + length; + bytes_max = bytes + send_buffer_max - checksum_overhead; + bytes_next = picoquic_format_ack_frame(cnx, bytes_next, bytes_max, &more_data, current_time, pc); + length = bytes_next - bytes; + } else { + length = 0; + packet->length = 0; + } + } + + if (ret == 0 && length == 0 && more_data) { + *next_wake_time = current_time; + SET_LAST_WAKE(cnx->quic, PICOQUIC_SENDER); + } + + picoquic_finalize_and_protect_packet(cnx, packet, + ret, length, header_length, checksum_overhead, + send_length, send_buffer, send_buffer_max, + &path_x->remote_cnxid, &path_x->p_local_cnxid->cnx_id, path_x, current_time); + + /* Account for data sent during handshake */ + if (!cnx->initial_validated) { + cnx->initial_data_sent += *send_length; + } + + return ret; +} + +/* Prepare the next packet to send when in one the closing states */ +int picoquic_prepare_packet_closing(picoquic_cnx_t* cnx, picoquic_path_t * path_x, picoquic_packet_t* packet, + uint64_t current_time, uint8_t* send_buffer, size_t send_buffer_max, size_t* send_length, uint64_t * next_wake_time) +{ + int ret = 0; + /* TODO: manage multiple streams. */ + picoquic_packet_type_enum packet_type = 0; + size_t checksum_overhead = 8; + size_t header_length = 0; + uint8_t* bytes = packet->bytes; + uint8_t* bytes_max; + uint8_t* bytes_next; + int more_data = 0; + size_t length = 0; + int is_pure_ack = 1; + picoquic_packet_context_enum pc = picoquic_packet_context_application; + picoquic_epoch_enum epoch = picoquic_epoch_1rtt; + + /* The only purpose of the test below is to appease the static analyzer, so it + * wont complain of possible NULL deref. On windows we could use "__assume(path_x != NULL)" + * but the documentation does not say anything about that for GCC and CLANG */ + if (path_x == NULL) { + return PICOQUIC_ERROR_UNEXPECTED_ERROR; + } + + send_buffer_max = (send_buffer_max > path_x->send_mtu) ? path_x->send_mtu : send_buffer_max; + + /* Prepare header -- depend on connection state */ + /* TODO: 0-RTT work. */ + switch (cnx->cnx_state) { + case picoquic_state_handshake_failure: + /* TODO: check whether closing can be requested in "initial" mode */ + if (cnx->crypto_context[picoquic_epoch_handshake].aead_encrypt != NULL && + cnx->pkt_ctx[picoquic_packet_context_handshake].first_sack_item.start_of_sack_range != (uint64_t)((int64_t)-1)) { + pc = picoquic_packet_context_handshake; + packet_type = picoquic_packet_handshake; + epoch = picoquic_epoch_handshake; + } + else { + pc = picoquic_packet_context_initial; + packet_type = picoquic_packet_initial; + } + break; + case picoquic_state_handshake_failure_resend: + pc = picoquic_packet_context_handshake; + packet_type = picoquic_packet_handshake; + epoch = picoquic_epoch_handshake; + break; + case picoquic_state_disconnecting: + packet_type = picoquic_packet_1rtt_protected; + break; + case picoquic_state_closing_received: + packet_type = picoquic_packet_1rtt_protected; + break; + case picoquic_state_closing: + packet_type = picoquic_packet_1rtt_protected; + break; + case picoquic_state_draining: + packet_type = picoquic_packet_1rtt_protected; + break; + case picoquic_state_disconnected: + ret = PICOQUIC_ERROR_DISCONNECTED; + break; + default: + ret = -1; + break; + } + + /* At this stage, we don't try to retransmit any old packet, whether in + * the current context or in previous contexts. */ + + checksum_overhead = picoquic_get_checksum_length(cnx, epoch); + packet->pc = pc; + bytes_max = bytes + send_buffer_max - checksum_overhead; + + if (ret == 0 && cnx->cnx_state == picoquic_state_closing_received) { + /* Send a closing frame, move to closing state */ + uint64_t exit_time = cnx->latest_progress_time + 3 * path_x->retransmit_timer; + + length = picoquic_predict_packet_header_length(cnx, packet_type); + bytes_next = bytes + length; + packet->ptype = packet_type; + packet->offset = length; + header_length = length; + packet->sequence_number = cnx->pkt_ctx[pc].send_sequence; + packet->send_time = current_time; + packet->send_path = path_x; + + /* Send the disconnect frame */ + bytes_next = picoquic_format_connection_close_frame(cnx, bytes_next, bytes_max, &more_data, &is_pure_ack); + length = bytes_next - bytes; + + cnx->cnx_state = picoquic_state_draining; + *next_wake_time = exit_time; + SET_LAST_WAKE(cnx->quic, PICOQUIC_SENDER); + } else if (ret == 0 && cnx->cnx_state == picoquic_state_closing) { + /* if more than 3*RTO is elapsed, move to disconnected */ + uint64_t exit_time = cnx->latest_progress_time + 3 * path_x->retransmit_timer; + + if (current_time >= exit_time) { + cnx->cnx_state = picoquic_state_disconnected; + *next_wake_time = current_time; + SET_LAST_WAKE(cnx->quic, PICOQUIC_SENDER); + } + else if (current_time >= cnx->next_wake_time) { + uint64_t delta_t = path_x->rtt_min; + uint64_t next_time = 0; + + if (delta_t * 2 < path_x->retransmit_timer) { + delta_t = path_x->retransmit_timer / 2; + } + /* if more than N packet received, repeat and erase */ + if (cnx->pkt_ctx[pc].ack_needed) { + length = picoquic_predict_packet_header_length( + cnx, packet_type); + packet->ptype = packet_type; + packet->offset = length; + header_length = length; + packet->sequence_number = cnx->pkt_ctx[pc].send_sequence; + packet->send_time = current_time; + packet->send_path = path_x; + bytes_next = bytes + length; + + /* Resend the disconnect frame */ + if (cnx->local_error == 0) { + bytes_next = picoquic_format_application_close_frame(cnx, bytes_next, bytes_max, &more_data, &is_pure_ack); + } else { + bytes_next = picoquic_format_connection_close_frame(cnx, bytes_next, bytes_max, &more_data, &is_pure_ack); + } + length = bytes_next - bytes; + cnx->pkt_ctx[pc].ack_needed = 0; + } + next_time = current_time + delta_t; + if (next_time > exit_time) { + next_time = exit_time; + } + + *next_wake_time = next_time; + SET_LAST_WAKE(cnx->quic, PICOQUIC_SENDER); + } + } else if (ret == 0 && cnx->cnx_state == picoquic_state_draining) { + /* Nothing is ever sent in the draining state */ + /* if more than 3*RTO is elapsed, move to disconnected */ + uint64_t exit_time = cnx->latest_progress_time + 3 * path_x->retransmit_timer; + + if (current_time >= exit_time) { + cnx->cnx_state = picoquic_state_disconnected; + *next_wake_time = current_time; + SET_LAST_WAKE(cnx->quic, PICOQUIC_SENDER); + } + else { + *next_wake_time = exit_time; + SET_LAST_WAKE(cnx->quic, PICOQUIC_SENDER); + } + length = 0; + } else if (ret == 0 && (cnx->cnx_state == picoquic_state_disconnecting || + cnx->cnx_state == picoquic_state_handshake_failure || + cnx->cnx_state == picoquic_state_handshake_failure_resend)) { + length = picoquic_predict_packet_header_length( + cnx, packet_type); + bytes_next = bytes + length; + packet->ptype = packet_type; + packet->offset = length; + header_length = length; + packet->sequence_number = cnx->pkt_ctx[pc].send_sequence; + packet->send_time = current_time; + packet->send_path = path_x; + + /* send either app close or connection close, depending on error code */ + uint64_t delta_t = path_x->rtt_min; + + if (2 * delta_t < path_x->retransmit_timer) { + delta_t = path_x->retransmit_timer / 2; + } + + /* add a final ack so receiver gets clean state */ + bytes_next = picoquic_format_ack_frame(cnx, bytes_next, bytes_max, &more_data, current_time, pc); + + /* Send the disconnect frame */ + if (cnx->local_error == 0) { + bytes_next = picoquic_format_application_close_frame(cnx, bytes_next, bytes_max, &more_data, &is_pure_ack); + } + else { + bytes_next = picoquic_format_connection_close_frame(cnx, bytes_next, bytes_max, &more_data, &is_pure_ack); + } + length = bytes_next - bytes; + + if (cnx->cnx_state == picoquic_state_handshake_failure) { + if (pc == picoquic_packet_context_initial && + cnx->crypto_context[2].aead_encrypt != NULL) { + cnx->cnx_state = picoquic_state_handshake_failure_resend; + } + else { + cnx->cnx_state = picoquic_state_disconnected; + } + } + else if (cnx->cnx_state == picoquic_state_handshake_failure_resend) { + cnx->cnx_state = picoquic_state_disconnected; + } + else { + cnx->cnx_state = picoquic_state_closing; + } + cnx->latest_progress_time = current_time; + *next_wake_time = current_time + delta_t; + SET_LAST_WAKE(cnx->quic, PICOQUIC_SENDER); + cnx->pkt_ctx[pc].ack_needed = 0; + + if (cnx->callback_fn) { + (void)(cnx->callback_fn)(cnx, 0, NULL, 0, picoquic_callback_close, cnx->callback_ctx, NULL); + } + } + else { + length = 0; + } + + if (length > 0 && packet->ptype == picoquic_packet_initial && cnx->client_mode) { + length = picoquic_pad_to_target_length(bytes, length, send_buffer_max - checksum_overhead); + } + + picoquic_finalize_and_protect_packet(cnx, packet, + ret, length, header_length, checksum_overhead, + send_length, send_buffer, send_buffer_max, + &path_x->remote_cnxid, &path_x->p_local_cnxid->cnx_id, path_x, current_time); + + return ret; +} + +/* Create required ID, register, and format the corresponding connection ID frame */ +uint8_t * picoquic_format_new_local_id_as_needed(picoquic_cnx_t* cnx, uint8_t* bytes, uint8_t * bytes_max, int * more_data, int * is_pure_ack) +{ + while ((cnx->remote_parameters.migration_disabled == 0 || cnx->remote_parameters.prefered_address.is_defined) && + cnx->local_parameters.migration_disabled == 0 && + cnx->nb_local_cnxid < (int)(cnx->remote_parameters.active_connection_id_limit) && + cnx->nb_local_cnxid <= PICOQUIC_NB_PATH_TARGET) { + uint8_t* bytes0 = bytes; + picoquic_local_cnxid_t* l_cid = picoquic_create_local_cnxid(cnx, NULL); + + if (l_cid == NULL) { + /* OOPS, memory error */ + break; + } else { + bytes = picoquic_format_new_connection_id_frame(cnx, bytes, bytes_max, more_data, is_pure_ack, l_cid); + + if (bytes == bytes0) { + /* Oops. Try again next time. */ + picoquic_delete_local_cnxid(cnx, l_cid); + cnx->local_cnxid_sequence_next--; + break; + } + } + } + + return bytes; +} + + +void picoquic_ready_state_transition(picoquic_cnx_t* cnx, uint64_t current_time) +{ + /* Transition to server ready state. + * The handshake is complete, all the handshake packets are implicitly acknowledged */ + cnx->cnx_state = picoquic_state_ready; + cnx->is_handshake_finished = 1; + picoquic_implicit_handshake_ack(cnx, picoquic_packet_context_initial, current_time); + picoquic_implicit_handshake_ack(cnx, picoquic_packet_context_handshake, current_time); + + (void)picoquic_register_net_secret(cnx); + picoquic_public_random_seed(cnx->quic); + + if (!cnx->client_mode) { + (void)picoquic_queue_handshake_done_frame(cnx); + } + + + if (cnx->is_half_open){ + if (cnx->quic->current_number_half_open > 0) { + cnx->quic->current_number_half_open--; + } + cnx->is_half_open = 0; + if (cnx->quic->current_number_half_open < cnx->quic->max_half_open_before_retry) { + cnx->quic->check_token = cnx->quic->force_check_token; + } + } + + /* Remove handshake and initial keys if they are still around */ + picoquic_crypto_context_free(&cnx->crypto_context[picoquic_epoch_initial]); + picoquic_crypto_context_free(&cnx->crypto_context[picoquic_epoch_0rtt]); + picoquic_crypto_context_free(&cnx->crypto_context[picoquic_epoch_handshake]); + + /* Set the confidentiality limit if not already set */ + if (cnx->crypto_epoch_length_max == 0) { + cnx->crypto_epoch_length_max = + picoquic_aead_confidentiality_limit(cnx->crypto_context[picoquic_epoch_1rtt].aead_decrypt); + } + + /* Start migration to server preferred address if present */ + if (cnx->client_mode) { + (void)picoquic_prepare_server_address_migration(cnx); + } + + /* Notify the application */ + if (cnx->callback_fn != NULL) { + if (cnx->callback_fn(cnx, 0, NULL, 0, picoquic_callback_ready, cnx->callback_ctx, NULL) != 0) { + picoquic_log_app_message(cnx, "Callback ready returns error 0x%x", PICOQUIC_TRANSPORT_INTERNAL_ERROR); + picoquic_connection_error(cnx, PICOQUIC_TRANSPORT_INTERNAL_ERROR, 0); + } + } + + /* Ask for ACK frequency update, or initialize variables if not available */ + if (cnx->is_ack_frequency_negotiated) { + cnx->is_ack_frequency_updated = 1; + } + else { + cnx->ack_gap_remote = picoquic_compute_ack_gap(cnx, cnx->path[0]->receive_rate_max); + cnx->ack_delay_remote = picoquic_compute_ack_delay_max(cnx->path[0]->rtt_min, PICOQUIC_ACK_DELAY_MIN); + } + + /* Perform a check of the PN decryption key, for sanity */ + picoquic_log_pn_dec_trial(cnx); +} + +/* Prepare the next packet to send when in one the ready states + * Lots of the same code as in the "ready" case, but we deal here with extra + * complexity because the handshake is not finished. + */ +int picoquic_prepare_packet_almost_ready(picoquic_cnx_t* cnx, picoquic_path_t* path_x, picoquic_packet_t* packet, + uint64_t current_time, uint8_t* send_buffer, size_t send_buffer_max, size_t* send_length, uint64_t* next_wake_time, + int* is_initial_sent) +{ + int ret = 0; + picoquic_packet_type_enum packet_type = picoquic_packet_1rtt_protected; + picoquic_packet_context_enum pc = picoquic_packet_context_application; + int tls_ready = 0; + int is_pure_ack = 1; + size_t header_length = 0; + uint8_t* bytes = packet->bytes; + size_t length = 0; + size_t checksum_overhead = picoquic_get_checksum_length(cnx, picoquic_epoch_1rtt); + size_t send_buffer_min_max = (send_buffer_max > path_x->send_mtu) ? path_x->send_mtu : send_buffer_max; + uint8_t* bytes_max = bytes + send_buffer_min_max - checksum_overhead; + uint8_t* bytes_next = NULL; + int more_data = 0; + int stream_tried_and_failed = 0; + int is_challenge_padding_needed = 0; + + /* Perform amplification prevention check */ + if (!cnx->initial_validated && + (cnx->initial_data_sent + send_buffer_min_max) > 3 * cnx->initial_data_received) { + *send_length = 0; + return 0; + } + + /* Verify first that there is no need for retransmit or ack + * on initial or handshake context. */ + if (cnx->crypto_context[picoquic_epoch_initial].aead_encrypt != NULL) { + length = picoquic_prepare_packet_old_context(cnx, picoquic_packet_context_initial, + path_x, packet, send_buffer_min_max, current_time, next_wake_time, &header_length); + } + else { + length = 0; + } + + if (length == 0) { + length = picoquic_prepare_packet_old_context(cnx, picoquic_packet_context_handshake, + path_x, packet, send_buffer_min_max, current_time, next_wake_time, &header_length); + if (length > 0) { + checksum_overhead = picoquic_get_checksum_length(cnx, picoquic_epoch_handshake); + bytes_max = bytes + send_buffer_min_max - checksum_overhead; + } + } + else { + checksum_overhead = picoquic_get_checksum_length(cnx, picoquic_epoch_initial); + bytes_max = bytes + send_buffer_min_max - checksum_overhead; + + *is_initial_sent = 1; + } + + if (length > 0) { + cnx->initial_repeat_needed = 0; + + if (cnx->client_mode && *is_initial_sent && send_buffer_min_max < length + checksum_overhead + PICOQUIC_MIN_SEGMENT_SIZE) { + length = picoquic_pad_to_target_length(packet->bytes, length, send_buffer_min_max - checksum_overhead); + } + } + + if (length == 0) { + tls_ready = picoquic_is_tls_stream_ready(cnx); + packet->pc = pc; + length = picoquic_predict_packet_header_length( + cnx, packet_type); + packet->ptype = packet_type; + packet->offset = length; + header_length = length; + packet->sequence_number = cnx->pkt_ctx[pc].send_sequence; + packet->send_time = current_time; + packet->send_path = path_x; + bytes_next = bytes + length; + + if (path_x->challenge_verified == 0 && path_x->challenge_failed == 0) { + uint64_t next_challenge_time = picoquic_next_challenge_time(cnx, path_x); + if (next_challenge_time <= current_time || path_x->challenge_repeat_count == 0) { + if (path_x->challenge_repeat_count < PICOQUIC_CHALLENGE_REPEAT_MAX) { + int ack_needed = cnx->pkt_ctx[pc].ack_needed; + uint8_t* bytes_challenge = bytes_next; + + bytes_next = picoquic_format_path_challenge_frame(bytes_next, bytes_max, &more_data, &is_pure_ack, + path_x->challenge[path_x->challenge_repeat_count]); + if (bytes_next > bytes_challenge) { + path_x->challenge_time = current_time; + path_x->challenge_repeat_count++; + is_challenge_padding_needed = 1; + } + + /* add an ACK just to be nice */ + if (ack_needed) { + bytes_next = picoquic_format_ack_frame(cnx, bytes_next, bytes_max, &more_data, current_time, pc); + /* Restore the ACK needed flags, because challenges are not reliable. */ + cnx->pkt_ctx[pc].ack_needed = ack_needed; + } + } + else { + if (path_x == cnx->path[0]) { + /* TODO: consider alt address. Also, consider other available path. */ + DBG_PRINTF("%s\n", "Too many challenge retransmits, disconnect"); + picoquic_log_app_message(cnx, "%s", "Too many challenge retransmits, disconnect"); + cnx->cnx_state = picoquic_state_disconnected; + if (cnx->callback_fn) { + (void)(cnx->callback_fn)(cnx, 0, NULL, 0, picoquic_callback_close, cnx->callback_ctx, NULL); + } + } + else { + DBG_PRINTF("%s\n", "Too many challenge retransmits, abandon path"); + picoquic_log_app_message(cnx, "%s", "Too many challenge retransmits, abandon path"); + path_x->challenge_failed = 1; + cnx->path_demotion_needed = 1; + } + } + } + else { + if (next_challenge_time < *next_wake_time) { + *next_wake_time = next_challenge_time; + SET_LAST_WAKE(cnx->quic, PICOQUIC_SENDER); + } + } + } + + if (path_x->response_required) { + uint8_t* bytes_response = bytes_next; + if ((bytes_next = picoquic_format_path_response_frame(bytes_response, bytes_max, + &more_data, &is_pure_ack, path_x->challenge_response)) > bytes_response) { + path_x->response_required = 0; + is_challenge_padding_needed = 1; + } + } + + length = bytes_next - bytes; + + if (cnx->cnx_state != picoquic_state_disconnected && path_x->challenge_verified != 0) { + /* There are no frames yet that would be exempt from pacing control, but if there + * was they should be sent here. */ + + if (picoquic_is_sending_authorized_by_pacing(cnx, path_x, current_time, next_wake_time)) { + /* Send here the frames that are not exempt from the pacing control, + * but are exempt for congestion control */ + if (picoquic_is_ack_needed(cnx, current_time, next_wake_time, pc)) { + bytes_next = picoquic_format_ack_frame(cnx, bytes_next, bytes_max, &more_data, + current_time, pc); + } + + length = bytes_next - bytes; + if (path_x->cwin < path_x->bytes_in_transit) { + cnx->cwin_blocked = 1; + if (cnx->congestion_alg != NULL) { + cnx->congestion_alg->alg_notify(cnx, path_x, + picoquic_congestion_notification_cwin_blocked, + 0, 0, 0, 0, current_time); + } + } + else { + /* Send here the frames that are subject to both congestion and pacing control. + * this includes the PMTU probes. + * Check whether PMTU discovery is required. The call will return + * three values: not needed at all, optional, or required. + * If required, PMTU discovery takes priority over sending stream data. + */ + picoquic_pmtu_discovery_status_enum pmtu_discovery_needed = picoquic_is_mtu_probe_needed(cnx, path_x); + + /* if present, send tls data */ + if (tls_ready) { + bytes_next = picoquic_format_crypto_hs_frame(&cnx->tls_stream[picoquic_epoch_1rtt], + bytes_next, bytes_max, &more_data, &is_pure_ack); + } + + length = bytes_next - bytes; + + if (length > header_length || pmtu_discovery_needed != picoquic_pmtu_discovery_required || + send_buffer_max <= path_x->send_mtu) { + /* No need or no way to do pmtu discovery */ + /* If present, send misc frame */ + while (cnx->first_misc_frame != NULL) { + uint8_t* bytes_misc = bytes_next; + bytes_next = picoquic_format_first_misc_frame(cnx, bytes_next, bytes_max, &more_data, &is_pure_ack); + if (bytes_next == bytes_misc) { + break; + } + } + + /* If there are not enough published CID, create and advertise */ + if (ret == 0) { + bytes_next = picoquic_format_new_local_id_as_needed(cnx, bytes_next, bytes_max, &more_data, &is_pure_ack); + } + + /* Start of CC controlled frames */ + if (ret == 0 && length <= header_length && cnx->first_datagram != NULL) { + bytes_next = picoquic_format_first_datagram_frame(cnx, bytes_next, bytes_max, &more_data, &is_pure_ack); + } + + /* If present, send stream frames queued for retransmission */ + if (ret == 0) { + bytes_next = picoquic_format_stream_frames_queued_for_retransmit(cnx, bytes_next, bytes_max, + &more_data, &is_pure_ack); + } + + if (cnx->is_ack_frequency_updated && cnx->is_ack_frequency_negotiated) { + bytes_next = picoquic_format_ack_frequency_frame(cnx, bytes_next, bytes_max, &more_data); + } + + bytes_next = picoquic_format_available_stream_frames(cnx, bytes_next, bytes_max, &more_data, &is_pure_ack, &stream_tried_and_failed, &ret); + + length = bytes_next - bytes; + + if (length <= header_length) { + /* Mark the bandwidth estimation as application limited */ + path_x->delivered_limited_index = path_x->delivered; + /* Notify the peer if something is blocked */ + bytes_next = picoquic_format_blocked_frames(cnx, &bytes[length], bytes_max, &more_data, &is_pure_ack); + length = bytes_next - bytes; + } + + if (stream_tried_and_failed) { + path_x->last_sender_limited_time = current_time; + } + } /* end of PMTU not required */ + + if (ret == 0 && length <= header_length && send_buffer_max > path_x->send_mtu + && path_x->cwin > path_x->bytes_in_transit&& pmtu_discovery_needed != picoquic_pmtu_discovery_not_needed) { + /* Since there is no data to send, this is an opportunity to send an MTU probe */ + length = picoquic_prepare_mtu_probe(cnx, path_x, header_length, checksum_overhead, bytes, send_buffer_max); + packet->length = length; + packet->send_path = path_x; + packet->is_mtu_probe = 1; + path_x->mtu_probe_sent = 1; + is_pure_ack = 0; + } + } /* end of PMTU references */ + } /* end of CC */ + } /* End of pacing */ + if (length <= header_length) { + length = 0; + } + + if (cnx->cnx_state != picoquic_state_disconnected) { + /* If necessary, encode and send the keep alive packet! + * We only send keep alive packets when no other data is sent! + */ + if (is_pure_ack == 0) + { + cnx->latest_progress_time = current_time; + } + else if (cnx->keep_alive_interval != 0) { + if (cnx->latest_progress_time + cnx->keep_alive_interval <= current_time && length == 0) { + length = picoquic_predict_packet_header_length( + cnx, packet_type); + packet->ptype = packet_type; + packet->pc = pc; + packet->offset = length; + header_length = length; + packet->sequence_number = cnx->pkt_ctx[pc].send_sequence; + packet->send_path = path_x; + packet->send_time = current_time; + bytes[length++] = picoquic_frame_type_ping; + bytes[length++] = 0; + cnx->latest_progress_time = current_time; + } + else if (cnx->latest_progress_time + cnx->keep_alive_interval < *next_wake_time) { + *next_wake_time = cnx->latest_progress_time + cnx->keep_alive_interval; + SET_LAST_WAKE(cnx->quic, PICOQUIC_SENDER); + } + } + } + } + + if (ret == 0 && length > header_length) { + if (more_data) { + *next_wake_time = current_time; + SET_LAST_WAKE(cnx->quic, PICOQUIC_SENDER); + ret = 0; + } + + /* Ensure that all packets are properly padded before being sent. */ + + if ((*is_initial_sent && cnx->client_mode) || (is_challenge_padding_needed && length < PICOQUIC_ENFORCED_INITIAL_MTU)){ + length = picoquic_pad_to_target_length(bytes, length, (uint32_t)(send_buffer_min_max - checksum_overhead)); + } + else { + length = picoquic_pad_to_policy(cnx, bytes, length, (uint32_t)(send_buffer_min_max - checksum_overhead)); + } + } + + picoquic_finalize_and_protect_packet(cnx, packet, + ret, length, header_length, checksum_overhead, + send_length, send_buffer, send_buffer_min_max, + &path_x->remote_cnxid, &path_x->p_local_cnxid->cnx_id, path_x, current_time); + + if (*send_length > 0) { + /* Account for data sent during handshake */ + if (!cnx->initial_validated) { + cnx->initial_data_sent += *send_length; + } + *next_wake_time = current_time; + SET_LAST_WAKE(cnx->quic, PICOQUIC_SENDER); + + if (picoquic_cnx_is_still_logging(cnx)) { + picoquic_log_cc_dump(cnx, current_time); + } + } + + return ret; +} + +/* Prepare the next packet to send when in the ready state */ +int picoquic_prepare_packet_ready(picoquic_cnx_t* cnx, picoquic_path_t* path_x, picoquic_packet_t* packet, + uint64_t current_time, uint8_t* send_buffer, size_t send_buffer_max, size_t* send_length, uint64_t* next_wake_time, + int* is_initial_sent) +{ + int ret = 0; + picoquic_packet_type_enum packet_type = picoquic_packet_1rtt_protected; + picoquic_packet_context_enum pc = picoquic_packet_context_application; + int is_pure_ack = 1; + size_t header_length = 0; + size_t length = 0; + size_t checksum_overhead = picoquic_get_checksum_length(cnx, picoquic_epoch_1rtt); + size_t send_buffer_min_max = (send_buffer_max > path_x->send_mtu) ? path_x->send_mtu : send_buffer_max; + int split_repeat_queued = 0; + uint8_t* bytes = packet->bytes; + uint8_t* bytes_max = bytes + send_buffer_min_max - checksum_overhead; + uint8_t* bytes_next; + int more_data = 0; + int ack_sent = 0; + int is_challenge_padding_needed = 0; + + packet->pc = pc; + + /* If there was no packet sent on this path for a long time, rotate the + * CID prior to sending a new packet. The point is to make it harder for + * casual observers to track traffic, especially across NAT resets */ + if (cnx->client_mode && + path_x->challenge_verified && + !path_x->path_cid_rotated && + path_x->latest_sent_time + PICOQUIC_CID_REFRESH_DELAY < current_time) + { + /* Ignore renewal failure mode, since this is an optional feature */ + (void)picoquic_renew_path_connection_id(cnx, path_x); + path_x->path_cid_rotated = 1; + } + + /* If the number of packets sent is larger that the max length of + * a crypto epoch, prepare a key rotation */ + if ((cnx->pkt_ctx[picoquic_packet_context_application].send_sequence - cnx->crypto_epoch_sequence > + cnx->crypto_epoch_length_max) && + cnx->crypto_epoch_sequence < + cnx->pkt_ctx[picoquic_packet_context_application].first_sack_item.end_of_sack_range) { + if (picoquic_start_key_rotation(cnx) != 0) { + picoquic_log_app_message(cnx, "Cannot start key rotation after %"PRIu64" packets", + cnx->pkt_ctx[picoquic_packet_context_application].send_sequence); + } + } + + /* The first action is normally to retransmit lost packets. But if retransmit follows an + * MTU drop, the stream frame will be fragmented and a fragment will be queued as a + * misc frame. These fragments should have chance to go out before more retransmit is + * permitted, hence the test here for the misc-frame */ + if (cnx->first_misc_frame == NULL && + (length = picoquic_retransmit_needed(cnx, pc, path_x, current_time, next_wake_time, packet, + send_buffer_min_max, &header_length)) > 0) { + /* Check whether it makes sense to add an ACK at the end of the retransmission */ + /* Don't do that if it risks mixing clear text and encrypted ack */ + bytes_next = picoquic_format_ack_frame(cnx, bytes + length, bytes_max, &more_data, + current_time, pc); + length = bytes_next - bytes; + /* document the send time & overhead */ + is_pure_ack = 0; + packet->send_time = current_time; + packet->checksum_overhead = checksum_overhead; + } + else if (ret == 0) { + length = picoquic_predict_packet_header_length( + cnx, packet_type); + packet->ptype = packet_type; + packet->offset = length; + header_length = length; + packet->sequence_number = cnx->pkt_ctx[pc].send_sequence; + packet->send_time = current_time; + packet->send_path = path_x; + bytes_next = bytes + length; + + /* If required, prepare challenge and response frames. + * These frames will be sent immediately, regardless of pacing or flow control. + */ + + if (path_x->challenge_verified == 0 && path_x->challenge_failed == 0) { + uint64_t next_challenge_time = picoquic_next_challenge_time(cnx, path_x); + if (next_challenge_time <= current_time || path_x->challenge_repeat_count == 0) { + if (path_x->challenge_repeat_count < PICOQUIC_CHALLENGE_REPEAT_MAX) { + int ack_needed = cnx->pkt_ctx[pc].ack_needed; + /* When blocked, repeat the path challenge or wait */ + uint8_t* bytes_challenge = bytes_next; + + bytes_next = picoquic_format_path_challenge_frame(bytes_next, bytes_max, &more_data, &is_pure_ack, + path_x->challenge[path_x->challenge_repeat_count]); + if (bytes_next > bytes_challenge) { + path_x->challenge_time = current_time; + path_x->challenge_repeat_count++; + is_challenge_padding_needed = 1; + } + + /* add an ACK just to be nice */ + if (ack_needed) { + bytes_next = picoquic_format_ack_frame(cnx, bytes_next, bytes_max, &more_data, + current_time, pc); + /* Restore the ACK needed flags, because challenges are not reliable. */ + cnx->pkt_ctx[pc].ack_needed = ack_needed; + } + } + else { + if (path_x == cnx->path[0]) { + /* Try to find an alternate path */ + for (int i = 1; i < cnx->nb_paths; i++) { + if (cnx->path[i]->challenge_failed == 0) { + cnx->path[0] = cnx->path[i]; + cnx->path[i] = path_x; + } + } + } + + if (path_x == cnx->path[0]) { + DBG_PRINTF("%s\n", "Too many challenge retransmits, disconnect"); + picoquic_log_app_message(cnx, "%s", "Too many challenge retransmits, disconnect"); + cnx->cnx_state = picoquic_state_disconnected; + if (cnx->callback_fn) { + (void)(cnx->callback_fn)(cnx, 0, NULL, 0, picoquic_callback_close, cnx->callback_ctx, NULL); + } + } + else { + DBG_PRINTF("%s\n", "Too many challenge retransmits, abandon path"); + picoquic_log_app_message(cnx, "%s", "Too many challenge retransmits, abandon path"); + path_x->challenge_failed = 1; + } + } + } + else { + if (next_challenge_time < *next_wake_time) { + *next_wake_time = next_challenge_time; + SET_LAST_WAKE(cnx->quic, PICOQUIC_SENDER); + } + } + } + + if (path_x->response_required) { + uint8_t* bytes_response = bytes_next; + if ((bytes_next = picoquic_format_path_response_frame(bytes_response, bytes_max, + &more_data, &is_pure_ack, path_x->challenge_response)) > bytes_response) { + path_x->response_required = 0; + is_challenge_padding_needed = 1; + } + } + + /* Compute the length before pacing block */ + length = bytes_next - bytes; + + if (cnx->cnx_state != picoquic_state_disconnected && path_x->challenge_verified != 0) { + /* There are no frames yet that would be exempt from pacing control, but if there + * was they should be sent here. */ + + if (picoquic_is_sending_authorized_by_pacing(cnx, path_x, current_time, next_wake_time)) { + /* Send here the frames that are not exempt from the pacing control, + * but are exempt for congestion control */ + if (picoquic_is_ack_needed(cnx, current_time, next_wake_time, pc)) { + uint8_t* bytes_ack = bytes_next; + bytes_next = picoquic_format_ack_frame(cnx, bytes_next, bytes_max, &more_data, + current_time, pc); + ack_sent = (bytes_next > bytes_ack); + } + + /* if necessary, prepare the MAX STREAM frames */ + if (ret == 0) { + bytes_next = picoquic_format_max_streams_frame_if_needed(cnx, bytes_next, bytes_max, &more_data, &is_pure_ack); + } + + /* If necessary, encode the max data frame */ + if (ret == 0){ + if (cnx->is_flow_control_limited) { + if (cnx->data_received + (cnx->local_parameters.initial_max_data / 2) > cnx->maxdata_local) { + bytes_next = picoquic_format_max_data_frame(cnx, bytes_next, bytes_max, &more_data, &is_pure_ack, + cnx->local_parameters.initial_max_data); + } + } + else if (2 * cnx->data_received > cnx->maxdata_local) { + bytes_next = picoquic_format_max_data_frame(cnx, bytes_next, bytes_max, &more_data, &is_pure_ack, + picoquic_cc_increased_window(cnx, cnx->maxdata_local)); + } + } + + /* If necessary, encode the max stream data frames */ + if (ret == 0 && cnx->max_stream_data_needed) { + bytes_next = picoquic_format_required_max_stream_data_frames(cnx, bytes_next, bytes_max, &more_data, &is_pure_ack); + } + + /* If present, send misc frame */ + while (cnx->first_misc_frame != NULL) { + uint8_t* bytes_misc = bytes_next; + bytes_next = picoquic_format_first_misc_frame(cnx, bytes_next, bytes_max, &more_data, &is_pure_ack); + if (bytes_next > bytes_misc) { + split_repeat_queued |= + PICOQUIC_IN_RANGE(*bytes_misc, picoquic_frame_type_stream_range_min, picoquic_frame_type_stream_range_max); + } + else { + break; + } + } + + /* Compute the length before entering the CC block */ + length = bytes_next - bytes; + + if (path_x->cwin < path_x->bytes_in_transit) { + cnx->cwin_blocked = 1; + if (cnx->congestion_alg != NULL) { + cnx->congestion_alg->alg_notify(cnx, path_x, + picoquic_congestion_notification_cwin_blocked, + 0, 0, 0, 0, current_time); + } + } + else { + /* Send here the frames that are subject to both congestion and pacing control. + * this includes the PMTU probes. + * Check whether PMTU discovery is required. The call will return + * three values: not needed at all, optional, or required. + * If required, PMTU discovery takes priority over sending stream data. + */ + int datagram_tried_and_failed = 0; + int stream_tried_and_failed = 0; + picoquic_pmtu_discovery_status_enum pmtu_discovery_needed = picoquic_is_mtu_probe_needed(cnx, path_x); + + /* if present, send tls data */ + if (picoquic_is_tls_stream_ready(cnx)) { + bytes_next = picoquic_format_crypto_hs_frame(&cnx->tls_stream[picoquic_epoch_1rtt], + bytes_next, bytes_max, &more_data, &is_pure_ack); + } + + if (length > header_length || pmtu_discovery_needed != picoquic_pmtu_discovery_required || + send_buffer_max <= path_x->send_mtu) { + /* No need or no way to do path MTU discovery, just go on with formatting packets */ + /* If there are not enough local CID published, create and advertise */ + if (ret == 0) { + bytes_next = picoquic_format_new_local_id_as_needed(cnx, bytes_next, bytes_max, &more_data, &is_pure_ack); + } + + /* Start of CC controlled frames */ + + if (ret == 0 && length <= header_length) { + if (cnx->first_datagram != NULL) { + bytes_next = picoquic_format_first_datagram_frame(cnx, bytes_next, bytes_max, &more_data, &is_pure_ack); + } + else { + datagram_tried_and_failed = 1; + } + } + + /* If present, send stream frames queued for retransmission */ + if (ret == 0) { + bytes_next = picoquic_format_stream_frames_queued_for_retransmit(cnx, bytes_next, bytes_max, + &more_data, &is_pure_ack); + } + + if (ret == 0 && cnx->is_ack_frequency_updated && cnx->is_ack_frequency_negotiated) { + bytes_next = picoquic_format_ack_frequency_frame(cnx, bytes_next, bytes_max, &more_data); + } + + /* Encode the stream frame, or frames */ + if (ret == 0 && !split_repeat_queued && bytes_next + 8 < bytes_max) { + bytes_next = picoquic_format_available_stream_frames(cnx, bytes_next, bytes_max, &more_data, &is_pure_ack, &stream_tried_and_failed, &ret); + } + + length = bytes_next - bytes; + + if (length <= header_length || is_pure_ack) { + /* Mark the bandwidth estimation as application limited */ + path_x->delivered_limited_index = path_x->delivered; + /* Notify the peer if something is blocked */ + bytes_next = picoquic_format_blocked_frames(cnx, &bytes[length], bytes_max, &more_data, &is_pure_ack); + length = bytes_next - bytes; + } + + if (stream_tried_and_failed && datagram_tried_and_failed) { + path_x->last_sender_limited_time = current_time; + } + else if (length <= header_length && stream_tried_and_failed && !cnx->stream_blocked && !cnx->flow_blocked) { + /* Consider redundant retransmission: + * if the redundant retransmission index is null: + * - if the packet loss rate is large enough compared to BDP, set index to last sent packet. + * - if not, do not perform redundant retransmission. + * if the packet contains a stream frame, if that stream is finished, and if the + * data range has not been acked, and it fits: copy it to the data. Move the index to the previous packet. + */ + /* TODO: write that code! */ + } + } /* end of PMTU not required */ + + if (ret == 0 && length <= header_length && send_buffer_max > path_x->send_mtu + && path_x->cwin > path_x->bytes_in_transit&& pmtu_discovery_needed != picoquic_pmtu_discovery_not_needed) { + /* Since there is no data to send, this is an opportunity to send an MTU probe */ + length = picoquic_prepare_mtu_probe(cnx, path_x, header_length, checksum_overhead, bytes, send_buffer_max); + packet->length = length; + packet->send_path = path_x; + packet->is_mtu_probe = 1; + path_x->mtu_probe_sent = 1; + is_pure_ack = 0; + } + } /* end of CC */ + } /* End of pacing */ + } /* End of challenge verified */ + } + + if (length <= header_length) { + length = 0; + } + + if (cnx->cnx_state != picoquic_state_disconnected) { + if (length > 0){ + cnx->pkt_ctx[pc].ack_of_ack_requested |= !is_pure_ack; + if (!cnx->pkt_ctx[pc].ack_of_ack_requested && ack_sent) { + /* If we have sent many ACKs, add a PING to get an ack of ack */ + /* The number 24 is chosen to not break any of the unit tests. If the number is + * too small, the PING mechanism can cause delayed end of the connection, or + * early breakage */ + bytes_next = bytes + length; + if (bytes_next < bytes_max && + cnx->pkt_ctx[pc].highest_acknowledged + 24 < cnx->pkt_ctx[pc].send_sequence && + path_x == cnx->path[0] && + cnx->pkt_ctx[pc].highest_acknowledged_time + cnx->path[0]->smoothed_rtt < current_time) { + /* Bundle a Ping with ACK, so as to get trigger an Acknowledgement */ + *bytes_next++ = picoquic_frame_type_ping; + cnx->pkt_ctx[pc].ack_of_ack_requested = 1; + is_pure_ack = 0; + length = bytes_next - bytes; + } + } + } + + if (is_pure_ack == 0) + { + cnx->latest_progress_time = current_time; + } + else if (cnx->keep_alive_interval != 0) { + /* If necessary, encode and send the keep alive packet. + * We only send keep alive packets when no other data is sent. + */ + if (cnx->latest_progress_time + cnx->keep_alive_interval <= current_time && length == 0) { + length = picoquic_predict_packet_header_length( + cnx, packet_type); + packet->ptype = packet_type; + packet->pc = pc; + packet->offset = length; + header_length = length; + packet->sequence_number = cnx->pkt_ctx[pc].send_sequence; + packet->send_path = path_x; + packet->send_time = current_time; + bytes[length++] = picoquic_frame_type_ping; + bytes[length++] = 0; + cnx->latest_progress_time = current_time; + } + else if (cnx->latest_progress_time + cnx->keep_alive_interval < *next_wake_time) { + *next_wake_time = cnx->latest_progress_time + cnx->keep_alive_interval; + SET_LAST_WAKE(cnx->quic, PICOQUIC_SENDER); + } + } + + if (more_data) { + *next_wake_time = current_time; + SET_LAST_WAKE(cnx->quic, PICOQUIC_SENDER); + ret = 0; + } + } + + if (ret == 0 && length > header_length) { + /* Ensure that all packets are properly padded before being sent. */ + + if ((*is_initial_sent && cnx->client_mode) || (is_challenge_padding_needed && length < PICOQUIC_ENFORCED_INITIAL_MTU)){ + length = picoquic_pad_to_target_length(bytes, length, (uint32_t)(send_buffer_min_max - checksum_overhead)); + } + else { + length = picoquic_pad_to_policy(cnx, bytes, length, (uint32_t)(send_buffer_min_max - checksum_overhead)); + } + } + + picoquic_finalize_and_protect_packet(cnx, packet, + ret, length, header_length, checksum_overhead, + send_length, send_buffer, send_buffer_min_max, + &path_x->remote_cnxid, &path_x->p_local_cnxid->cnx_id, path_x, current_time); + + if (*send_length > 0) { + *next_wake_time = current_time; + SET_LAST_WAKE(cnx->quic, PICOQUIC_SENDER); + + if (ret == 0 && picoquic_cnx_is_still_logging(cnx)) { + picoquic_log_cc_dump(cnx, current_time); + } + } + + return ret; +} + + +static int picoquic_check_idle_timer(picoquic_cnx_t* cnx, uint64_t* next_wake_time, uint64_t current_time) +{ + int ret = 0; + uint64_t idle_timer = 0; + + if (cnx->cnx_state >= picoquic_state_ready) { + uint64_t rto = picoquic_current_retransmit_timer(cnx, picoquic_packet_context_application); + idle_timer = cnx->idle_timeout; + if (idle_timer < 3 * rto) { + idle_timer = 3 * rto; + } + idle_timer += cnx->latest_progress_time; + + if (idle_timer < cnx->idle_timeout) { + idle_timer = UINT64_MAX; + } + } + else { + idle_timer = cnx->start_time + PICOQUIC_MICROSEC_HANDSHAKE_MAX; + } + + if (current_time >= idle_timer) { + /* Too long silence, break it. */ + cnx->cnx_state = picoquic_state_disconnected; + ret = PICOQUIC_ERROR_DISCONNECTED; + if (cnx->callback_fn) { + (void)(cnx->callback_fn)(cnx, 0, NULL, 0, picoquic_callback_close, cnx->callback_ctx, NULL); + } + } else if (idle_timer < *next_wake_time) { + *next_wake_time = idle_timer; + SET_LAST_WAKE(cnx->quic, PICOQUIC_SENDER); + } + + return ret; +} + +/* Prepare next packet to send, or nothing.. */ +int picoquic_prepare_segment(picoquic_cnx_t* cnx, picoquic_path_t* path_x, picoquic_packet_t* packet, + uint64_t current_time, uint8_t* send_buffer, size_t send_buffer_max, size_t* send_length, + uint64_t* next_wake_time, int* is_initial_sent) +{ + int ret = 0; + + /* Reset the blocked indicators */ + cnx->cwin_blocked = 0; + cnx->flow_blocked = 0; + cnx->stream_blocked = 0; + + /* Prepare header -- depend on connection state */ + /* TODO: 0-RTT work. */ + switch (cnx->cnx_state) { + case picoquic_state_client_init: + case picoquic_state_client_init_sent: + case picoquic_state_client_init_resent: + case picoquic_state_client_renegotiate: + case picoquic_state_client_handshake_start: + case picoquic_state_client_almost_ready: + ret = picoquic_prepare_packet_client_init(cnx, path_x, packet, current_time, send_buffer, send_buffer_max, send_length, next_wake_time, is_initial_sent); + break; + case picoquic_state_server_almost_ready: + case picoquic_state_server_init: + case picoquic_state_server_handshake: + ret = picoquic_prepare_packet_server_init(cnx, path_x, packet, current_time, send_buffer, send_buffer_max, send_length, next_wake_time); + break; + case picoquic_state_server_false_start: + /* + * Manage the end of false start transition, and if needed start + * preparing packet in ready state. + */ + if (cnx->cnx_state == picoquic_state_server_false_start && + cnx->crypto_context[3].aead_decrypt != NULL) { + picoquic_ready_state_transition(cnx, current_time); + return picoquic_prepare_packet_ready(cnx, path_x, packet, current_time, send_buffer, send_buffer_max, send_length, next_wake_time, is_initial_sent); + } + /* Else, just fall through to almost ready behavior. + */ + case picoquic_state_client_ready_start: + ret = picoquic_prepare_packet_almost_ready(cnx, path_x, packet, current_time, send_buffer, send_buffer_max, send_length, next_wake_time, is_initial_sent); + break; + case picoquic_state_ready: + ret = picoquic_prepare_packet_ready(cnx, path_x, packet, current_time, send_buffer, send_buffer_max, send_length, next_wake_time, is_initial_sent); + break; + case picoquic_state_handshake_failure: + case picoquic_state_handshake_failure_resend: + case picoquic_state_disconnecting: + case picoquic_state_closing_received: + case picoquic_state_closing: + case picoquic_state_draining: + ret = picoquic_prepare_packet_closing(cnx, path_x, packet, current_time, send_buffer, send_buffer_max, send_length, next_wake_time); + break; + case picoquic_state_disconnected: + ret = PICOQUIC_ERROR_DISCONNECTED; + break; + case picoquic_state_client_retry_received: + DBG_PRINTF("Unexpected connection state: %d\n", cnx->cnx_state); + ret = PICOQUIC_ERROR_UNEXPECTED_STATE; + break; + default: + DBG_PRINTF("Unexpected connection state: %d\n", cnx->cnx_state); + ret = PICOQUIC_ERROR_UNEXPECTED_STATE; + break; + } + + return ret; +} + +/* + * The version 1 of Quic only supports path migration, not full multipath. + * This code finds whether there is a path being probed that could become the + * default path, or that needs an immediate challenge sent or replied to. + * + * If no other path is suitable, the code returns the default path. + */ + +static int picoquic_select_next_path(picoquic_cnx_t * cnx, uint64_t current_time, uint64_t * next_wake_time) +{ + int path_id = -1; + /* Select the path */ + for (int i = 1; i < cnx->nb_paths; i++) { + if (cnx->path[i]->path_is_demoted) { + continue; + } + else if (cnx->path[i]->challenge_failed) { + picoquic_demote_path(cnx, i, current_time); + continue; + } + else if (cnx->path[i]->challenge_verified) { + /* TODO: selection logic if multiple paths are available! */ + /* This path becomes the new default */ + picoquic_promote_path_to_default(cnx, i, current_time); + path_id = 0; + break; + } + else if (path_id < 0) { + if (cnx->path[i]->response_required) { + path_id = i; + } + else if (cnx->path[i]->challenge_required) { + uint64_t next_challenge_time = picoquic_next_challenge_time(cnx, cnx->path[i]); + if (cnx->path[i]->challenge_repeat_count == 0 || + current_time >= next_challenge_time) { + /* will try this path, unless a validated path came in */ + path_id = i; + } + else if (next_challenge_time < *next_wake_time) { + *next_wake_time = next_challenge_time; + SET_LAST_WAKE(cnx->quic, PICOQUIC_SENDER); + } + } + } + } + + if (path_id < 0) { + path_id = 0; + } + + return path_id; +} + +/* Prepare next packet to send, or nothing.. */ +int picoquic_prepare_packet_ex(picoquic_cnx_t* cnx, + uint64_t current_time, uint8_t* send_buffer, size_t send_buffer_max, size_t* send_length, + struct sockaddr_storage * p_addr_to, struct sockaddr_storage * p_addr_from, int* if_index, size_t* send_msg_size) +{ + + int ret; + picoquic_packet_t * packet = NULL; + struct sockaddr_storage addr_to_log; + struct sockaddr_storage addr_from_log; + int is_initial_sent = 0; + uint64_t next_wake_time = cnx->latest_progress_time + 2*PICOQUIC_MICROSEC_SILENCE_MAX; + uint64_t initial_next_time; + + SET_LAST_WAKE(cnx->quic, PICOQUIC_SENDER); + + if (cnx->recycle_sooner_needed) { + picoquic_process_sooner_packets(cnx, current_time); + } + + memset(&addr_to_log, 0, sizeof(addr_to_log)); + memset(&addr_from_log, 0, sizeof(addr_from_log)); + *send_length = 0; + + ret = picoquic_check_idle_timer(cnx, &next_wake_time, current_time); + + if (send_buffer_max < PICOQUIC_ENFORCED_INITIAL_MTU) { + DBG_PRINTF("Invalid buffer size: %zu", send_buffer_max); + ret = -1; + } + + if (ret == 0) { + int path_id; + + /* Remove delete paths */ + if (cnx->path_demotion_needed) { + picoquic_delete_abandoned_paths(cnx, current_time, &next_wake_time); + } + + /* Check whether to insert a hole in the sequence of packets */ + if (cnx->pkt_ctx[0].send_sequence >= cnx->pkt_ctx[0].next_sequence_hole) { + picoquic_insert_hole_in_send_sequence_if_needed(cnx, current_time, &next_wake_time); + } + + /* Select the next path, and the corresponding addresses */ + path_id = picoquic_select_next_path(cnx, current_time, &next_wake_time); + + + picoquic_store_addr(&addr_to_log, (struct sockaddr*) & cnx->path[path_id]->peer_addr); + if (cnx->path[path_id]->local_addr.ss_family != 0) { + picoquic_store_addr(&addr_from_log, (struct sockaddr*) & cnx->path[path_id]->local_addr); + } + + if (p_addr_to != NULL) { + picoquic_store_addr(p_addr_to, (struct sockaddr*) & cnx->path[path_id]->peer_addr); + } + + if (p_addr_from != NULL) { + picoquic_store_addr(p_addr_from, (struct sockaddr*) & cnx->path[path_id]->local_addr); + } + + if (if_index != NULL) { + *if_index = cnx->path[path_id]->if_index_dest; + } + + /* Send the available packets */ + if (send_msg_size != NULL) { + *send_msg_size = cnx->path[path_id]->send_mtu; + } + initial_next_time = next_wake_time; + + while (ret == 0) + { + /* Create a new packet */ + size_t packet_size = 0; + size_t packet_max = send_buffer_max - *send_length; + uint8_t* packet_buffer = send_buffer + *send_length; + /* Reset the wake time to the initial value after sending packets */ + next_wake_time = initial_next_time; + /* Send the available segments in that packet. */ + while (ret == 0) + { + size_t available = packet_max; + size_t segment_length = 0; + + if (packet_size > 0) { + packet_max = cnx->path[path_id]->send_mtu; + + if (packet_max < packet_size + PICOQUIC_MIN_SEGMENT_SIZE) { + break; + } + else { + available = packet_max - packet_size; + } + } + + packet = picoquic_create_packet(cnx->quic); + + if (packet == NULL) { + ret = PICOQUIC_ERROR_MEMORY; + break; + } + else { + ret = picoquic_prepare_segment(cnx, cnx->path[path_id], packet, current_time, + packet_buffer + packet_size, available, &segment_length, &next_wake_time, &is_initial_sent); + + if (ret == 0) { + packet_size += segment_length; + if (packet->length == 0) { + /* Nothing more to send */ + picoquic_recycle_packet(cnx->quic, packet); + break; + } + else if (packet->ptype == picoquic_packet_1rtt_protected) { + /* Cannot coalesce packets after 1 rtt packet */ + break; + } + else if (segment_length == 0) { + DBG_PRINTF("Send bug: segment length = %zu, packet length = %zu\n", segment_length, packet->length); + break; + } + } + else { + picoquic_recycle_packet(cnx->quic, packet); + packet = NULL; + + if (packet_size != 0) { + ret = 0; + } + break; + } + + if (cnx->quic->dont_coalesce_init) { + break; + } + } + } + if (packet_size > 0) { + cnx->nb_packets_sent++; + /* if needed, log that the packet is sent */ + picoquic_log_pdu(cnx, 0, current_time, + (struct sockaddr*) & addr_to_log, (struct sockaddr*) & addr_from_log, packet_size); + } + + /* Update the wake up time for the connection */ + if (packet_size > 0 || cnx->cnx_state == picoquic_state_disconnected) { + next_wake_time = current_time; + SET_LAST_WAKE(cnx->quic, PICOQUIC_SENDER); + } + + /* Account for the bytes in the packet. */ + *send_length += packet_size; + + /* Check whether to keep coalescing multiple packets in the send buffer */ + if (send_msg_size == NULL) { + break; + } + else if (packet_size > *send_msg_size) { + /* This can only happen for the first packet in a batch. */ + *send_msg_size = packet_size; + } + else if (packet_size != *send_msg_size) { + if (*send_length > 0) { + if (packet_size == 0 && *send_length < 4*(*send_msg_size)) { + if (cnx->path[0]->cwin <= cnx->path[0]->bytes_in_transit) { + cnx->nb_trains_blocked_cwin++; + } + else if (cnx->path[0]->pacing_bucket_nanosec < cnx->path[0]->pacing_packet_time_nanosec){ + cnx->nb_trains_blocked_pacing++; + } + else { + cnx->nb_trains_blocked_others++; + } + } + else { + cnx->nb_trains_short++; + } + } + break; + } + else if (*send_length + *send_msg_size > send_buffer_max) { + break; + } + } + if (*send_length > 0) { + cnx->nb_trains_sent++; + } + } + + picoquic_reinsert_by_wake_time(cnx->quic, cnx, next_wake_time); + + return ret; +} + +int picoquic_prepare_packet(picoquic_cnx_t* cnx, + uint64_t current_time, uint8_t* send_buffer, size_t send_buffer_max, size_t* send_length, + struct sockaddr_storage* p_addr_to, struct sockaddr_storage* p_addr_from, int* if_index) +{ + return picoquic_prepare_packet_ex(cnx, current_time, send_buffer, send_buffer_max, send_length, + p_addr_to, p_addr_from, if_index, NULL); +} + +int picoquic_close(picoquic_cnx_t* cnx, uint16_t reason_code) +{ + int ret = 0; + + if (cnx->cnx_state == picoquic_state_ready || + cnx->cnx_state == picoquic_state_server_false_start || cnx->cnx_state == picoquic_state_client_ready_start) { + cnx->cnx_state = picoquic_state_disconnecting; + cnx->application_error = reason_code; + } else if (cnx->cnx_state < picoquic_state_client_ready_start) { + cnx->cnx_state = picoquic_state_handshake_failure; + cnx->application_error = 0; + cnx->local_error = PICOQUIC_TRANSPORT_APPLICATION_ERROR; + } else { + ret = -1; + } + cnx->offending_frame_type = 0; + + picoquic_reinsert_by_wake_time(cnx->quic, cnx, picoquic_get_quic_time(cnx->quic)); + + return ret; +} + +/* Quic context level call. + * will send a stateless packet if one is queued, or ask the first connection in + * the wake list to prepare a packet */ + +int picoquic_prepare_next_packet_ex(picoquic_quic_t* quic, + uint64_t current_time, uint8_t* send_buffer, size_t send_buffer_max, size_t* send_length, + struct sockaddr_storage* p_addr_to, struct sockaddr_storage* p_addr_from, int * if_index, + picoquic_connection_id_t * log_cid, picoquic_cnx_t** p_last_cnx, size_t * send_msg_size) +{ + int ret = 0; + picoquic_stateless_packet_t* sp = picoquic_dequeue_stateless_packet(quic); + + if (p_last_cnx) { + *p_last_cnx = NULL; + } + + if (sp != NULL) { + if (sp->length > send_buffer_max) { + *send_length = 0; + } + else { + memcpy(send_buffer, sp->bytes, sp->length); + *send_length = sp->length; + picoquic_store_addr(p_addr_to, (struct sockaddr*) & sp->addr_to); + picoquic_store_addr(p_addr_from, (struct sockaddr*) & sp->addr_local); + *if_index = sp->if_index_local; + if (log_cid != NULL) { + *log_cid = sp->initial_cid; + } + } + picoquic_delete_stateless_packet(sp); + } + else { + picoquic_cnx_t* cnx = picoquic_get_earliest_cnx_to_wake(quic, current_time); + + if (cnx == NULL) { + *send_length = 0; + } + else { + ret = picoquic_prepare_packet_ex(cnx, current_time, send_buffer, send_buffer_max, send_length, p_addr_to, p_addr_from, + if_index, send_msg_size); + if (log_cid != NULL) { + *log_cid = cnx->initial_cnxid; + } + + if (ret == PICOQUIC_ERROR_DISCONNECTED) { + ret = 0; + + picoquic_log_app_message(cnx, "Closed. Retrans= %d, spurious= %d, max sp gap = %d, max sp delay = %d, dg-coal: %f", + (int)cnx->nb_retransmission_total, (int)cnx->nb_spurious, + (int)cnx->path[0]->max_reorder_gap, (int)cnx->path[0]->max_spurious_rtt, + (cnx->nb_trains_sent > 0) ? ((double)cnx->nb_packets_sent / (double)cnx->nb_trains_sent) : 0.0); + + if (quic->F_log != NULL) { + fflush(quic->F_log); + } + + if (cnx->f_binlog != NULL) { + fflush(cnx->f_binlog); + } + + if (cnx->client_mode) { + /* Do not unilaterally delete the connection context, as it was set by the application */ + picoquic_reinsert_by_wake_time(cnx->quic, cnx, UINT64_MAX); + SET_LAST_WAKE(cnx->quic, PICOQUIC_SENDER); + } + else { + picoquic_delete_cnx(cnx); + } + } + else { + if (*if_index == -1) { + *if_index = picoquic_get_local_if_index(cnx); + } + if (p_last_cnx) { + *p_last_cnx = cnx; + } + } + } + } + + return ret; +} + +int picoquic_prepare_next_packet(picoquic_quic_t* quic, + uint64_t current_time, uint8_t* send_buffer, size_t send_buffer_max, size_t* send_length, + struct sockaddr_storage* p_addr_to, struct sockaddr_storage* p_addr_from, int* if_index, + picoquic_connection_id_t* log_cid, picoquic_cnx_t** p_last_cnx) +{ + return picoquic_prepare_next_packet_ex(quic, current_time, send_buffer, send_buffer_max, send_length, + p_addr_to, p_addr_from, if_index, log_cid, p_last_cnx, NULL); +} \ No newline at end of file diff --git a/deps/picoquic/sim_link.c b/deps/picoquic/sim_link.c new file mode 100644 index 0000000..96e8017 --- /dev/null +++ b/deps/picoquic/sim_link.c @@ -0,0 +1,299 @@ +/* +* Author: Christian Huitema +* Copyright (c) 2017, Private Octopus, Inc. +* All rights reserved. +* +* Permission to use, copy, modify, and distribute this software for any +* purpose with or without fee is hereby granted, provided that the above +* copyright notice and this permission notice appear in all copies. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL Private Octopus, Inc. BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/* + * Really basic network simulator, only simulates a simple link using a + * packet structure. + * Init: link creation. Returns a link structure with defined bandwidth, + * latency, loss pattern and initial time. The link is empty. The loss + * pattern is a 64 bit bit mask. + * Submit packet of length L at time t. The packet is queued to the link. + * Get packet out of link at time T + L + Queue. + */ + +#include "picoquic_internal.h" +#include "picoquic_utils.h" +#include +#include + +picoquictest_sim_link_t* picoquictest_sim_link_create(double data_rate_in_gps, + uint64_t microsec_latency, uint64_t* loss_mask, uint64_t queue_delay_max, uint64_t current_time) +{ + picoquictest_sim_link_t* link = (picoquictest_sim_link_t*)malloc(sizeof(picoquictest_sim_link_t)); + if (link != 0) { + double pico_d = (data_rate_in_gps <= 0) ? 0 : (8000.0 / data_rate_in_gps); + pico_d *= (1.024 * 1.024); /* account for binary units */ + link->next_send_time = current_time; + link->queue_time = current_time; + link->queue_delay_max = queue_delay_max; + link->picosec_per_byte = (uint64_t)pico_d; + link->microsec_latency = microsec_latency; + link->packets_dropped = 0; + link->packets_sent = 0; + link->first_packet = NULL; + link->last_packet = NULL; + link->loss_mask = loss_mask; + link->jitter_seed = 0xDEADBEEFBABAC001ull; + link->jitter = 0; + link->path_mtu = PICOQUIC_MAX_PACKET_SIZE; + link->red_drop_mask = 0; + link->red_queue_max = 0; + link->bucket_increase_per_microsec = 0; + link->bucket_max = 0; + link->bucket_current = 0; + link->bucket_arrival_last = current_time; + } + + return link; +} + +void picoquictest_sim_link_delete(picoquictest_sim_link_t* link) +{ + picoquictest_sim_packet_t* packet; + + while ((packet = link->first_packet) != NULL) { + link->first_packet = packet->next_packet; + free(packet); + } + + free(link); +} + +picoquictest_sim_packet_t* picoquictest_sim_link_create_packet() +{ + picoquictest_sim_packet_t* packet = (picoquictest_sim_packet_t*)malloc(sizeof(picoquictest_sim_packet_t)); + if (packet != NULL) { + packet->next_packet = NULL; + packet->arrival_time = 0; + packet->length = 0; + } + + return packet; +} + +uint64_t picoquictest_sim_link_next_arrival(picoquictest_sim_link_t* link, uint64_t current_time) +{ + picoquictest_sim_packet_t* packet = link->first_packet; + + if (packet != NULL && packet->arrival_time < current_time) { + current_time = packet->arrival_time; + } + + return current_time; +} + +picoquictest_sim_packet_t* picoquictest_sim_link_dequeue(picoquictest_sim_link_t* link, + uint64_t current_time) +{ + picoquictest_sim_packet_t* packet = link->first_packet; + + if (packet != NULL && packet->arrival_time <= current_time) { + link->first_packet = packet->next_packet; + if (link->first_packet == NULL) { + link->last_packet = NULL; + } + } else { + packet = NULL; + } + + return packet; +} + +static int picoquictest_sim_link_testloss(uint64_t* loss_mask) +{ + uint64_t loss_bit = 0; + + if (loss_mask != NULL) { + /* Last bit indicates loss or not */ + loss_bit = (uint64_t)((*loss_mask) & 1ull); + + /* Rotate loss mask by 1 to prepare next round */ + *loss_mask >>= 1; + *loss_mask |= (loss_bit << 63); + } + + return (int)loss_bit; +} + +static uint64_t picoquictest_sim_link_jitter(picoquictest_sim_link_t* link) +{ + uint64_t jitter = link->jitter; + double x = picoquic_test_gauss_random(&link->jitter_seed); + if (x < -3.0) { + x = -3.0; + } + x /= 3.0; + jitter += (int64_t)(x * (double)jitter); + + return jitter; +} + +void picoquictest_sim_link_submit(picoquictest_sim_link_t* link, picoquictest_sim_packet_t* packet, + uint64_t current_time) +{ + uint64_t queue_delay = (current_time > link->queue_time) ? 0 : link->queue_time - current_time; + uint64_t transmit_time = ((link->picosec_per_byte * ((uint64_t)packet->length)) >> 20); + uint64_t should_drop = 0; + + if (transmit_time <= 0) + transmit_time = 1; + + if (link->bucket_increase_per_microsec > 0) { + /* Simulate a rate limiter based on classic leaky bucket algorithm */ + uint64_t delta_microsec = current_time - link->bucket_arrival_last; + link->bucket_arrival_last = current_time; + link->bucket_current += ((double)delta_microsec) * link->bucket_increase_per_microsec; + if (link->bucket_current > (double)link->bucket_max) { + link->bucket_current = (double)link->bucket_max; + } + if (link->bucket_current > (double)packet->length) { + link->bucket_current -= (double)packet->length; + } + else { + should_drop = 1; + } + } else if (link->queue_delay_max > 0 && queue_delay >= link->queue_delay_max) { + if (link->red_drop_mask == 0 || queue_delay >= link->red_queue_max) { + should_drop = 1; + } + else { + should_drop = link->red_drop_mask & 1; + link->red_drop_mask >>= 1; + link->red_drop_mask |= (should_drop << 63); + } + } + + if (!should_drop) { + + link->queue_time = current_time + queue_delay + transmit_time; + + if (packet->length > link->path_mtu || picoquictest_sim_link_testloss(link->loss_mask) != 0) { + link->packets_dropped++; + free(packet); + } else { + link->packets_sent++; + if (link->last_packet == NULL) { + link->first_packet = packet; + } else { + link->last_packet->next_packet = packet; + } + link->last_packet = packet; + packet->next_packet = NULL; + packet->arrival_time = link->queue_time + link->microsec_latency; + if (link->jitter != 0) { + packet->arrival_time += picoquictest_sim_link_jitter(link); + } + } + } else { + /* simulate congestion loss or random drop on queue full */ + link->packets_dropped++; + free(packet); + } +} + +int sim_link_one_test(uint64_t* loss_mask, uint64_t queue_delay_max, uint64_t nb_losses) +{ + int ret = 0; + uint64_t current_time = 0; + uint64_t departure_time = 0; + picoquictest_sim_link_t* link = picoquictest_sim_link_create(0.01, 10000, loss_mask, queue_delay_max, current_time); + uint64_t dequeued = 0; + uint64_t queued = 0; + const uint64_t nb_packets = 16; + + if (link == NULL) { + ret = -1; + } + else { + + while (ret == 0) { + if (queued >= nb_packets) { + departure_time = (uint64_t)((int64_t)-1); + } + + current_time = picoquictest_sim_link_next_arrival(link, departure_time); + + picoquictest_sim_packet_t* packet = picoquictest_sim_link_dequeue(link, current_time); + + if (packet != NULL) { + dequeued++; + free(packet); + } + else if (queued < nb_packets) { + packet = picoquictest_sim_link_create_packet(); + + if (packet == NULL) { + ret = -1; + } + else { + packet->length = sizeof(packet->bytes); + picoquictest_sim_link_submit(link, packet, departure_time); + departure_time += 250; + queued++; + } + } + else { + break; + } + } + + if ((dequeued + nb_losses) != nb_packets) { + ret = -1; + } + + picoquictest_sim_link_delete(link); + } + + return ret; +} + +int sim_link_test() +{ + int ret = 0; + uint64_t loss_mask = 0; + + ret = sim_link_one_test(&loss_mask, 0, 0); + + if (ret == 0) { + loss_mask = 8; + ret = sim_link_one_test(&loss_mask, 0, 1); + } + + if (ret == 0) { + loss_mask = 0x18; + ret = sim_link_one_test(&loss_mask, 0, 2); + } + + return ret; +} + +void picoquic_set_test_address(struct sockaddr_in * addr, uint32_t addr_val, uint16_t port) +{ + /* Init of the IP addresses */ + memset(addr, 0, sizeof(struct sockaddr_in)); + addr->sin_family = AF_INET; +#ifdef _WINDOWS + addr->sin_addr.S_un.S_addr = addr_val; +#else + addr->sin_addr.s_addr = addr_val; +#endif + addr->sin_port = port; +} \ No newline at end of file diff --git a/deps/picoquic/sockloop.c b/deps/picoquic/sockloop.c new file mode 100644 index 0000000..2802297 --- /dev/null +++ b/deps/picoquic/sockloop.c @@ -0,0 +1,382 @@ +/* +* Author: Christian Huitema +* Copyright (c) 2020, Private Octopus, Inc. +* All rights reserved. +* +* Permission to use, copy, modify, and distribute this software for any +* purpose with or without fee is hereby granted, provided that the above +* copyright notice and this permission notice appear in all copies. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL Private Octopus, Inc. BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/* Socket loop implements the "wait for messages" loop common to most servers + * and many clients. + * + * Second step: support simple servers and simple client. + * + * The "call loop back" function is called: when ready, after receiving, and after sending. The + * loop will terminate if the callback return code is not zero -- except for special processing + * of the migration testing code. + * TODO: in Windows, use WSA asynchronous calls instead of sendmsg, allowing for multiple parallel sends. + * TODO: in Linux, use multiple send per call API + * TDOO: trim the #define list. + * TODO: support the QuicDoq scenario, manage extra socket. + */ + +#ifdef _WINDOWS +#define WIN32_LEAN_AND_MEAN +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef SOCKET_TYPE +#define SOCKET_TYPE SOCKET +#endif +#ifndef SOCKET_CLOSE +#define SOCKET_CLOSE(x) closesocket(x) +#endif +#ifndef WSA_LAST_ERROR +#define WSA_LAST_ERROR(x) WSAGetLastError() +#endif +#ifndef socklen_t +#define socklen_t int +#endif + +#else /* Linux */ + +#include +#include +#include +#include +#include +#include +#include + +#ifndef __USE_XOPEN2K +#define __USE_XOPEN2K +#endif +#ifndef __USE_POSIX +#define __USE_POSIX +#endif +#include +#include +#include +#include +#include + +#ifndef SOCKET_TYPE +#define SOCKET_TYPE int +#endif +#ifndef INVALID_SOCKET +#define INVALID_SOCKET -1 +#endif +#ifndef SOCKET_CLOSE +#define SOCKET_CLOSE(x) close(x) +#endif +#ifndef WSA_LAST_ERROR +#define WSA_LAST_ERROR(x) ((long)(x)) +#endif +#endif + +#include "picosocks.h" +#include "picoquic.h" +#include "picoquic_internal.h" +#include "picoquic_packet_loop.h" +#include "picoquic_unified_log.h" + +int picoquic_packet_loop_open_sockets(int local_port, int local_af, SOCKET_TYPE * s_socket, int * sock_af, int nb_sockets_max) +{ + int nb_sockets = (local_af == AF_UNSPEC) ? 2 : 1; + + /* Compute how many sockets are necessary */ + if (nb_sockets > nb_sockets_max) { + DBG_PRINTF("Cannot open %d sockets, max set to %d\n", nb_sockets, nb_sockets_max); + nb_sockets = 0; + } else if (local_af == AF_UNSPEC) { + sock_af[0] = AF_INET; + sock_af[1] = AF_INET6; + } + else if (local_af == AF_INET || local_af == AF_INET6) { + sock_af[0] = local_af; + } + else { + DBG_PRINTF("Cannot open socket(AF=%d), unsupported AF\n", local_af); + nb_sockets = 0; + } + + for (int i = 0; i < nb_sockets; i++) { + int recv_set = 0; + int send_set = 0; + + if ((s_socket[i] = socket(sock_af[i], SOCK_DGRAM, IPPROTO_UDP)) == INVALID_SOCKET || + picoquic_socket_set_ecn_options(s_socket[i], sock_af[i], &recv_set, &send_set) != 0 || + picoquic_socket_set_pkt_info(s_socket[i], sock_af[i]) != 0 || + (local_port != 0 && picoquic_bind_to_port(s_socket[i], sock_af[i], local_port) != 0)) { + DBG_PRINTF("Cannot set socket (af=%d, port = %d)\n", sock_af[i], local_port); + for (int j = 0; j < i; j++) { + if (s_socket[i] != INVALID_SOCKET) { + SOCKET_CLOSE(s_socket[i]); + s_socket[i] = INVALID_SOCKET; + } + } + nb_sockets = 0; + break; + } + } + + return nb_sockets; +} + +int picoquic_packet_loop(picoquic_quic_t* quic, + int local_port, + int local_af, + int dest_if, + picoquic_packet_loop_cb_fn loop_callback, + void* loop_callback_ctx) +{ + int ret = 0; + uint64_t current_time = picoquic_get_quic_time(quic); + int64_t delay_max = 10000000; + struct sockaddr_storage addr_from; + struct sockaddr_storage addr_to; + int if_index_to; + uint8_t buffer[1536]; + uint8_t send_buffer[1536]; + size_t send_length = 0; + int bytes_recv; + uint64_t loop_count_time = current_time; + int nb_loops = 0; + picoquic_connection_id_t log_cid; + SOCKET_TYPE s_socket[PICOQUIC_PACKET_LOOP_SOCKETS_MAX]; + int sock_af[PICOQUIC_PACKET_LOOP_SOCKETS_MAX]; + int nb_sockets = 0; + uint16_t socket_port = (uint16_t)local_port; + int testing_migration = 0; /* Hook for the migration test */ + uint16_t next_port = 0; /* Data for the migration test */ + picoquic_cnx_t* last_cnx = NULL; +#ifdef _WINDOWS + WSADATA wsaData = { 0 }; + (void)WSA_START(MAKEWORD(2, 2), &wsaData); +#endif + memset(sock_af, 0, sizeof(sock_af)); + + if ((nb_sockets = picoquic_packet_loop_open_sockets(local_port, local_af, s_socket, sock_af, PICOQUIC_PACKET_LOOP_SOCKETS_MAX)) == 0) { + ret = PICOQUIC_ERROR_UNEXPECTED_ERROR; + } + else if (loop_callback != NULL) { + ret = loop_callback(quic, picoquic_packet_loop_ready, loop_callback_ctx); + } + + /* Wait for packets */ + /* TODO: add stopping condition, was && (!just_once || !connection_done) */ + while (ret == 0) { + int socket_rank = -1; + int64_t delta_t = picoquic_get_next_wake_delay(quic, current_time, delay_max); + unsigned char received_ecn; + + if_index_to = 0; + + bytes_recv = picoquic_select_ex(s_socket, nb_sockets, + &addr_from, + &addr_to, &if_index_to, &received_ecn, + buffer, sizeof(buffer), + delta_t, &socket_rank, ¤t_time); + + nb_loops++; + if (nb_loops >= 100) { + uint64_t loop_delta = current_time - loop_count_time; + + loop_count_time = current_time; + DBG_PRINTF("Looped %d times in %llu microsec, file: %d, line: %d\n", + nb_loops, (unsigned long long) loop_delta, quic->wake_file, quic->wake_line); + picoquic_log_context_free_app_message(quic, &log_cid, "Looped %d times in %llu microsec, file: %d, line: %d", + nb_loops, (unsigned long long) loop_delta, quic->wake_file, quic->wake_line); + + nb_loops = 0; + } + + if (bytes_recv < 0) { + ret = -1; + } + else { + uint64_t loop_time = current_time; + uint16_t current_recv_port = socket_port; + + if (bytes_recv > 0) { + /* track the local port value if not known yet */ + if (socket_port == 0 && nb_sockets == 1) { + struct sockaddr_storage local_address; + if (picoquic_get_local_address(s_socket[0], &local_address) != 0) { + memset(&local_address, 0, sizeof(struct sockaddr_storage)); + fprintf(stderr, "Could not read local address.\n"); + } + else if (addr_to.ss_family == AF_INET6) { + socket_port = ((struct sockaddr_in6*) & local_address)->sin6_port; + } + else if (addr_to.ss_family == AF_INET) { + socket_port = ((struct sockaddr_in*) & local_address)->sin_port; + } + current_recv_port = socket_port; + } + if (testing_migration) { + if (socket_rank == 0) { + current_recv_port = socket_port; + } + else { + current_recv_port = next_port; + } + } + /* Document incoming port */ + if (addr_to.ss_family == AF_INET6) { + ((struct sockaddr_in6*) & addr_to)->sin6_port = current_recv_port; + } + else if (addr_to.ss_family == AF_INET) { + ((struct sockaddr_in*) & addr_to)->sin_port = current_recv_port; + } + /* Submit the packet to the server */ + (void)picoquic_incoming_packet(quic, buffer, + (size_t)bytes_recv, (struct sockaddr*) & addr_from, + (struct sockaddr*) & addr_to, if_index_to, received_ecn, + current_time); + + if (loop_callback != NULL) { + ret = loop_callback(quic, picoquic_packet_loop_after_receive, loop_callback_ctx); + } + } + + while (ret == 0) { + struct sockaddr_storage peer_addr; + struct sockaddr_storage local_addr; + int if_index = dest_if; + int sock_ret = 0; + int sock_err = 0; + + ret = picoquic_prepare_next_packet(quic, loop_time, + send_buffer, sizeof(send_buffer), &send_length, + &peer_addr, &local_addr, &if_index, &log_cid, &last_cnx); + + if (ret == 0 && send_length > 0) { + SOCKET_TYPE send_socket = INVALID_SOCKET; + loop_count_time = current_time; + nb_loops = 0; + for (int i = 0; i < nb_sockets; i++) { + if (sock_af[i] == peer_addr.ss_family) { + send_socket = s_socket[i]; + break; + } + } + + if (testing_migration) { + /* This code path is only used in the migration tests */ + uint16_t send_port = (local_addr.ss_family == AF_INET) ? + ((struct sockaddr_in*) & local_addr)->sin_port : + ((struct sockaddr_in6*) & local_addr)->sin6_port; + + if (send_port == next_port) { + send_socket = s_socket[nb_sockets - 1]; + } + } + + sock_ret = picoquic_send_through_socket(send_socket, + (struct sockaddr*) & peer_addr, (struct sockaddr*) & local_addr, if_index, + (const char*)send_buffer, (int)send_length, &sock_err); + + if (sock_ret <= 0) { + if (last_cnx == NULL) { + picoquic_log_context_free_app_message(quic, &log_cid, "Could not send message to AF_to=%d, AF_from=%d, if=%d, ret=%d, err=%d", + peer_addr.ss_family, local_addr.ss_family, if_index, sock_ret, sock_err); + } + else { + picoquic_log_app_message(last_cnx, "Could not send message to AF_to=%d, AF_from=%d, if=%d, ret=%d, err=%d", + peer_addr.ss_family, local_addr.ss_family, if_index, sock_ret, sock_err); + } + } + } + else { + break; + } + } + + if (ret == 0 && loop_callback != NULL) { + ret = loop_callback(quic, picoquic_packet_loop_after_send, loop_callback_ctx); + } + } + + if (ret == PICOQUIC_NO_ERROR_SIMULATE_NAT || ret == PICOQUIC_NO_ERROR_SIMULATE_MIGRATION) { + /* Two pseudo error codes used for testing migration! + * What follows is really test code, which we write here because it has to handle + * the sockets, which interferes a lot with the handling of the packet loop. + */ + SOCKET_TYPE s_mig = INVALID_SOCKET; + int s_mig_af; + int sock_ret; + int testing_nat = (ret == PICOQUIC_NO_ERROR_SIMULATE_NAT); + + next_port = (testing_nat) ? 0 : socket_port + 1; + sock_ret = picoquic_packet_loop_open_sockets(next_port, sock_af[0], &s_mig, &s_mig_af, 1); + if (sock_ret != 1 || s_mig == INVALID_SOCKET) { + if (last_cnx != NULL) { + picoquic_log_app_message(last_cnx, "Could not create socket for migration test, port=%d, af=%d, err=%d", + next_port, sock_af[0], sock_ret); + } + } + else if (testing_nat) { + if (s_socket[0] != INVALID_SOCKET) { + SOCKET_CLOSE(s_socket[0]); + } + s_socket[0] = s_mig; + ret = 0; + } else { + /* Testing organized migration */ + if (nb_sockets < PICOQUIC_PACKET_LOOP_SOCKETS_MAX && last_cnx != NULL) { + struct sockaddr_storage local_address; + picoquic_store_addr(&local_address, (struct sockaddr*)& last_cnx->path[0]->local_addr); + if (local_address.ss_family == AF_INET6) { + ((struct sockaddr_in6*) & local_address)->sin6_port = next_port; + } + else if (local_address.ss_family == AF_INET) { + ((struct sockaddr_in*) & local_address)->sin_port = next_port; + } + s_socket[nb_sockets] = s_mig; + nb_sockets++; + testing_migration = 1; + ret = picoquic_probe_new_path(last_cnx, (struct sockaddr*)&last_cnx->path[0]->peer_addr, + (struct sockaddr*) &local_address, current_time); + } + else { + SOCKET_CLOSE(s_mig); + } + } + } + } + + if (ret == PICOQUIC_NO_ERROR_TERMINATE_PACKET_LOOP) { + /* Normal termination requested by the application, returns no error */ + ret = 0; + } + + /* Close the sockets */ + for (int i = 0; i < nb_sockets; i++) { + if (s_socket[i] != INVALID_SOCKET) { + SOCKET_CLOSE(s_socket[i]); + s_socket[i] = INVALID_SOCKET; + } + } + + return ret; +} \ No newline at end of file diff --git a/deps/picoquic/spinbit.c b/deps/picoquic/spinbit.c new file mode 100644 index 0000000..0461311 --- /dev/null +++ b/deps/picoquic/spinbit.c @@ -0,0 +1,85 @@ +/* +* Author: Christian Huitema +* Copyright (c) 2018, Private Octopus, Inc. +* All rights reserved. +* +* Permission to use, copy, modify, and distribute this software for any +* purpose with or without fee is hereby granted, provided that the above +* copyright notice and this permission notice appear in all copies. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL Private Octopus, Inc. BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "picoquic_internal.h" +#include "tls_api.h" + +#ifndef UNREFERENCED_PARAMETER +#define UNREFERENCED_PARAMETER(x) (void)(x) +#endif + +/* + * Two procedures defining the spin bit basic variant + */ +void picoquic_spinbit_basic_incoming(picoquic_cnx_t * cnx, picoquic_path_t * path_x, picoquic_packet_header * ph) +{ + path_x->current_spin = ph->spin ^ cnx->client_mode; +} + +uint8_t picoquic_spinbit_basic_outgoing(picoquic_cnx_t * cnx) +{ + uint8_t spin_bit = (uint8_t)((cnx->path[0]->current_spin) << 5); + + return spin_bit; +} + +/* + * Two procedures defining the null spin bit variant + */ + +void picoquic_spinbit_null_incoming(picoquic_cnx_t * cnx, picoquic_path_t * path_x, picoquic_packet_header * ph) +{ + UNREFERENCED_PARAMETER(cnx); + UNREFERENCED_PARAMETER(path_x); + UNREFERENCED_PARAMETER(ph); +} + +uint8_t picoquic_spinbit_null_outgoing(picoquic_cnx_t * cnx) +{ + UNREFERENCED_PARAMETER(cnx); + return 0; +} + +/* + * Two procedures defining the null spin bit randomized variant + */ + +void picoquic_spinbit_random_incoming(picoquic_cnx_t * cnx, picoquic_path_t * path_x, picoquic_packet_header * ph) +{ + UNREFERENCED_PARAMETER(cnx); + UNREFERENCED_PARAMETER(path_x); + UNREFERENCED_PARAMETER(ph); +} + +uint8_t picoquic_spinbit_random_outgoing(picoquic_cnx_t * cnx) +{ + UNREFERENCED_PARAMETER(cnx); + return (uint8_t) (picoquic_public_random_64()&0x20); +} + +/* + * Table of spin bit functions + */ +picoquic_spinbit_def_t picoquic_spin_function_table[] = { + {picoquic_spinbit_basic_incoming, picoquic_spinbit_basic_outgoing}, + {picoquic_spinbit_random_incoming, picoquic_spinbit_random_outgoing}, + {picoquic_spinbit_null_incoming, picoquic_spinbit_null_outgoing} +}; diff --git a/deps/picoquic/ticket_store.c b/deps/picoquic/ticket_store.c new file mode 100644 index 0000000..295bef3 --- /dev/null +++ b/deps/picoquic/ticket_store.c @@ -0,0 +1,405 @@ +#include "picoquic_internal.h" +/* +* Author: Christian Huitema +* Copyright (c) 2017, Private Octopus, Inc. +* All rights reserved. +* +* Permission to use, copy, modify, and distribute this software for any +* purpose with or without fee is hereby granted, provided that the above +* copyright notice and this permission notice appear in all copies. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL Private Octopus, Inc. BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "picoquic_internal.h" +#include +#include +#include +#include + +picoquic_stored_ticket_t* picoquic_format_ticket(uint64_t time_valid_until, + char const* sni, uint16_t sni_length, char const* alpn, uint16_t alpn_length, + uint8_t* ticket, uint16_t ticket_length, picoquic_tp_t const * tp) +{ + size_t ticket_size = sizeof(picoquic_stored_ticket_t) + sni_length + 1 + alpn_length + 1 + ticket_length; + picoquic_stored_ticket_t* stored = (picoquic_stored_ticket_t*)malloc(ticket_size); + + if (stored != NULL) { + char* next_p = ((char*)stored) + sizeof(picoquic_stored_ticket_t); + + memset(stored, 0, ticket_size); + stored->time_valid_until = time_valid_until; + stored->sni = next_p; + stored->sni_length = sni_length; + memcpy(next_p, sni, sni_length); + next_p += sni_length; + *next_p++ = 0; + + stored->alpn = next_p; + stored->alpn_length = alpn_length; + memcpy(next_p, alpn, alpn_length); + next_p += alpn_length; + *next_p++ = 0; + + if (tp != NULL) { + stored->tp_0rtt[picoquic_tp_0rtt_max_data] = tp->initial_max_data; + stored->tp_0rtt[picoquic_tp_0rtt_max_stream_data_bidi_local] = tp->initial_max_stream_data_bidi_local; + stored->tp_0rtt[picoquic_tp_0rtt_max_stream_data_bidi_remote] = tp->initial_max_stream_data_bidi_remote; + stored->tp_0rtt[picoquic_tp_0rtt_max_stream_data_uni] = tp->initial_max_stream_data_uni; + stored->tp_0rtt[picoquic_tp_0rtt_max_streams_id_bidir] = tp->initial_max_stream_id_bidir; + stored->tp_0rtt[picoquic_tp_0rtt_max_streams_id_unidir] = tp->initial_max_stream_id_unidir; + } + + stored->ticket = (uint8_t*)next_p; + stored->ticket_length = ticket_length; + memcpy(next_p, ticket, ticket_length); + } + + return stored; +} + +int picoquic_serialize_ticket(const picoquic_stored_ticket_t * ticket, uint8_t * bytes, size_t bytes_max, size_t * consumed) +{ + int ret = 0; + size_t byte_index = 0; + size_t required_length; + + /* Compute serialized length */ + required_length = (size_t)(8 + 2 + 2 + 2) + ticket->sni_length + ticket->alpn_length + ticket->ticket_length; + /* Serialize */ + if (required_length > bytes_max) { + ret = PICOQUIC_ERROR_FRAME_BUFFER_TOO_SMALL; + *consumed = 0; + } else { + picoformat_64(bytes + byte_index, ticket->time_valid_until); + byte_index += 8; + + picoformat_16(bytes + byte_index, ticket->sni_length); + byte_index += 2; + memcpy(bytes + byte_index, ticket->sni, ticket->sni_length); + byte_index += ticket->sni_length; + + picoformat_16(bytes + byte_index, ticket->alpn_length); + byte_index += 2; + memcpy(bytes + byte_index, ticket->alpn, ticket->alpn_length); + byte_index += ticket->alpn_length; + + for (int i = 0; i < PICOQUIC_NB_TP_0RTT; i++) { + picoformat_64(bytes + byte_index, ticket->tp_0rtt[i]); + byte_index += 8; + } + + picoformat_16(bytes + byte_index, ticket->ticket_length); + byte_index += 2; + memcpy(bytes + byte_index, ticket->ticket, ticket->ticket_length); + byte_index += ticket->ticket_length; + + *consumed = byte_index; + } + + return ret; +} + +int picoquic_deserialize_ticket(picoquic_stored_ticket_t ** ticket, uint8_t * bytes, size_t bytes_max, size_t * consumed) +{ + int ret = 0; + uint64_t time_valid_until = 0; + size_t required_length = 8 + 2 + 2 + PICOQUIC_NB_TP_0RTT * 8 + 2; + size_t byte_index = 0; + size_t sni_index = 0; + size_t alpn_index = 0; + size_t ticket_index = 0; + uint16_t sni_length = 0; + uint16_t alpn_length = 0; + uint16_t ticket_length = 0; + uint64_t tp_0rtt[PICOQUIC_NB_TP_0RTT] = { 0, 0, 0, 0, 0, 0 }; + + *consumed = 0; + *ticket = NULL; + + if (required_length < bytes_max) { + time_valid_until = PICOPARSE_64(bytes); + byte_index = 8; + sni_length = PICOPARSE_16(bytes + byte_index); + byte_index += 2; + sni_index = byte_index; + required_length += sni_length; + byte_index += sni_length; + } + + if (required_length < bytes_max) { + alpn_length = PICOPARSE_16(bytes + byte_index); + byte_index += 2; + alpn_index = byte_index; + required_length += alpn_length; + byte_index += alpn_length; + } + + if (required_length < bytes_max) { + for (int i = 0; i < PICOQUIC_NB_TP_0RTT; i++) { + tp_0rtt[i] = PICOPARSE_64(bytes + byte_index); + byte_index += 8; + } + } + + if (required_length < bytes_max) { + ticket_length = PICOPARSE_16(bytes + byte_index); + byte_index += 2; + ticket_index = byte_index; + required_length += ticket_length; + } + + if (required_length > bytes_max) { + *ticket = NULL; + ret = PICOQUIC_ERROR_INVALID_TICKET; + } else { + *ticket = picoquic_format_ticket(time_valid_until, (const char *)(bytes + sni_index), sni_length, + (const char *)(bytes + alpn_index), alpn_length, bytes + ticket_index, ticket_length, NULL); + if (*ticket == NULL) { + ret = PICOQUIC_ERROR_MEMORY; + } + else { + for (int i=0; i< PICOQUIC_NB_TP_0RTT; i++) { + (*ticket)->tp_0rtt[i] = tp_0rtt[i]; + } + *consumed = required_length; + } + } + + return ret; +} + +int picoquic_store_ticket(picoquic_stored_ticket_t** pp_first_ticket, + uint64_t current_time, + char const* sni, uint16_t sni_length, char const* alpn, uint16_t alpn_length, + uint8_t* ticket, uint16_t ticket_length, picoquic_tp_t const * tp) +{ + int ret = 0; + + if (ticket_length < 17) { + ret = PICOQUIC_ERROR_INVALID_TICKET; + } else { + uint64_t ticket_issued_time; + uint64_t ttl_seconds; + uint64_t time_valid_until; + + ticket_issued_time = PICOPARSE_64(ticket); + ttl_seconds = PICOPARSE_32(ticket + 13); + + if (ttl_seconds > (7 * 24 * 3600)) { + ttl_seconds = (7 * 24 * 3600); + } + + time_valid_until = (ticket_issued_time * 1000) + (ttl_seconds * 1000000); + + if (current_time != 0 && time_valid_until < current_time) { + ret = PICOQUIC_ERROR_INVALID_TICKET; + } else { + picoquic_stored_ticket_t* stored = picoquic_format_ticket(time_valid_until, sni, sni_length, + alpn, alpn_length, ticket, ticket_length, tp); + if (stored == NULL) { + ret = PICOQUIC_ERROR_MEMORY; + } + else { + picoquic_stored_ticket_t* next; + picoquic_stored_ticket_t** pprevious; + + stored->next_ticket = next = *pp_first_ticket; + *pp_first_ticket = stored; + pprevious = &stored->next_ticket; + + /* Now remove the old tickets for that SNI & ALPN */ + while (next != NULL) { + if (next->time_valid_until <= stored->time_valid_until && next->sni_length == sni_length && next->alpn_length == alpn_length && memcmp(next->sni, sni, sni_length) == 0 && memcmp(next->alpn, alpn, alpn_length) == 0) { + picoquic_stored_ticket_t* deleted = next; + next = next->next_ticket; + *pprevious = next; + memset(&deleted->ticket, 0, deleted->ticket_length); + free(deleted); + } else { + pprevious = &next->next_ticket; + next = next->next_ticket; + } + } + } + } + } + + return ret; +} + +int picoquic_get_ticket(picoquic_stored_ticket_t* p_first_ticket, + uint64_t current_time, + char const* sni, uint16_t sni_length, char const* alpn, uint16_t alpn_length, + uint8_t** ticket, uint16_t* ticket_length, picoquic_tp_t * tp, int mark_used) +{ + int ret = 0; + picoquic_stored_ticket_t* next = p_first_ticket; + + while (next != NULL) { + if (next->time_valid_until > current_time && next->sni_length == sni_length && next->alpn_length == alpn_length && memcmp(next->sni, sni, sni_length) == 0 && memcmp(next->alpn, alpn, alpn_length) == 0 && next->was_used == 0) { + break; + } else { + next = next->next_ticket; + } + } + + if (next == NULL) { + *ticket = NULL; + *ticket_length = 0; + ret = -1; + } else { + if (tp != NULL) { + tp->initial_max_data = next->tp_0rtt[picoquic_tp_0rtt_max_data]; + tp->initial_max_stream_data_bidi_local = next->tp_0rtt[picoquic_tp_0rtt_max_stream_data_bidi_local]; + tp->initial_max_stream_data_bidi_remote = next->tp_0rtt[picoquic_tp_0rtt_max_stream_data_bidi_remote]; + tp->initial_max_stream_data_uni = next->tp_0rtt[picoquic_tp_0rtt_max_stream_data_uni]; + tp->initial_max_stream_id_bidir = next->tp_0rtt[picoquic_tp_0rtt_max_streams_id_bidir]; + tp->initial_max_stream_id_unidir = next->tp_0rtt[picoquic_tp_0rtt_max_streams_id_unidir]; + } + *ticket = next->ticket; + *ticket_length = next->ticket_length; + next->was_used = mark_used; + } + + return ret; +} + +int picoquic_save_tickets(const picoquic_stored_ticket_t* first_ticket, + uint64_t current_time, + char const* ticket_file_name) +{ + int ret = 0; + FILE* F = NULL; + const picoquic_stored_ticket_t* next = first_ticket; + + if ((F = picoquic_file_open(ticket_file_name, "wb")) == NULL) { + ret = -1; + } else { + while (ret == 0 && next != NULL) { + /* Only store the tickets that are valid going forward */ + if (next->time_valid_until > current_time && next->was_used == 0) { + /* Compute the serialized size */ + uint8_t buffer[2048]; + size_t record_size; + + ret = picoquic_serialize_ticket(next, buffer, sizeof(buffer), &record_size); + + if (ret == 0) { + if (fwrite(&record_size, 4, 1, F) != 1 || fwrite(buffer, 1, record_size, F) != record_size) { + ret = PICOQUIC_ERROR_INVALID_FILE; + break; + } + } + } + next = next->next_ticket; + } + (void)picoquic_file_close(F); + } + + return ret; +} + +int picoquic_load_tickets(picoquic_stored_ticket_t** pp_first_ticket, + uint64_t current_time, char const* ticket_file_name) +{ + int ret = 0; + int file_err = 0; + FILE* F = NULL; + picoquic_stored_ticket_t* previous = NULL; + picoquic_stored_ticket_t* next = NULL; + uint32_t record_size; + uint32_t storage_size; + + + if ((F = picoquic_file_open_ex(ticket_file_name, "rb", &file_err)) == NULL) { + ret = (file_err == ENOENT) ? PICOQUIC_ERROR_NO_SUCH_FILE : -1; + } + + while (ret == 0) { + if (fread(&storage_size, 4, 1, F) != 1) { + /* end of file */ + break; + } + else if (storage_size > 2048 || + (record_size = storage_size + offsetof(struct st_picoquic_stored_ticket_t, time_valid_until)) > 2048) { + ret = PICOQUIC_ERROR_INVALID_FILE; + break; + } + else { + uint8_t buffer[2048]; + if (fread(buffer, 1, storage_size, F) + != storage_size) { + ret = PICOQUIC_ERROR_INVALID_FILE; + } + else { + size_t consumed = 0; + ret = picoquic_deserialize_ticket(&next, buffer, storage_size, &consumed); + + if (ret == 0 && (consumed != storage_size || next == NULL)) { + ret = PICOQUIC_ERROR_INVALID_FILE; + } + + if (ret == 0 && next != NULL) { + if (next->time_valid_until < current_time) { + free(next); + next = NULL; + } + else { + next->sni = ((char*)next) + sizeof(picoquic_stored_ticket_t); + next->alpn = next->sni + next->sni_length + 1; + next->ticket = (uint8_t*)(next->alpn + next->alpn_length + 1); + next->next_ticket = NULL; + if (previous == NULL) { + *pp_first_ticket = next; + } + else { + previous->next_ticket = next; + } + + previous = next; + } + } + } + } + } + + picoquic_file_close(F); + + return ret; +} + +void picoquic_free_tickets(picoquic_stored_ticket_t** pp_first_ticket) +{ + picoquic_stored_ticket_t* next; + + while ((next = *pp_first_ticket) != NULL) { + *pp_first_ticket = next->next_ticket; + + free(next); + } +} + +int picoquic_save_session_tickets(picoquic_quic_t* quic, char const* ticket_store_filename) +{ + return picoquic_save_tickets(quic->p_first_ticket, picoquic_get_quic_time(quic), ticket_store_filename); +} + +int picoquic_load_retry_tokens(picoquic_quic_t* quic, char const* token_store_filename) +{ + return picoquic_load_tokens(&quic->p_first_token, picoquic_get_quic_time(quic), token_store_filename); +} + +int picoquic_save_retry_tokens(picoquic_quic_t* quic, char const* ticket_store_filename) +{ + return picoquic_save_tokens(quic->p_first_token, picoquic_get_quic_time(quic), ticket_store_filename); +} \ No newline at end of file diff --git a/deps/picoquic/tls_api.c b/deps/picoquic/tls_api.c new file mode 100644 index 0000000..aaf37d0 --- /dev/null +++ b/deps/picoquic/tls_api.c @@ -0,0 +1,3312 @@ +/* +* Author: Christian Huitema +* Copyright (c) 2017, Private Octopus, Inc. +* All rights reserved. +* +* Permission to use, copy, modify, and distribute this software for any +* purpose with or without fee is hereby granted, provided that the above +* copyright notice and this permission notice appear in all copies. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL Private Octopus, Inc. BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + +/* The tls_api.c file provides the glue between the QUIC protocol code and the + * implementation of TLS 1.3 in picotls. That glue code has two main components: + * + * - matching TLS concepts such as handshake events with QUIC protocol events. + * - providing implementation of cryptographic algorithms. + * + * The bulk of the code corresponds to the first objective, the implementation + * of generic interactions with picotls. But this implementation relies on + * implementation of cryptographic primitives. The initial version of + * picoquic relies on OpenSSL for the implementation of these primitives, + * which limits portability of Picoquic to platforms that support OpenSSL. + * + * Picotls includes API for providing a variety of implementations of the + * cryptographic algorithms, linking with external libraries like OpenSSL or + * minimal implementations like "minicrypto". Our goal there is to provide a + * "crypto-provider" API so that applications can decide which provider they + * prefer. + * + * As an intermediate state towards that goal, we isolate the dependencies on + * OpenSSL in a small set of function calls. + */ + +#ifdef _WINDOWS +#include "wincompat.h" +#endif +#include +#include "picotls.h" +#include "picoquic_internal.h" +#include "picotls/openssl.h" +#if !defined(_WINDOWS) || defined(_WINDOWS64) +#include "picotls/fusion.h" +#endif +#if 0 +#include "picotls/ffx.h" +#endif +#include "tls_api.h" +#include +#include +#include +#include +#include +#include +#include +#include "picoquic_unified_log.h" + +#define container_of(ptr, type, member) ((type *)((char *)(ptr) - offsetof(type, member))) + +#ifndef UNREFERENCED_PARAMETER +#define UNREFERENCED_PARAMETER(x) (void)(x) +#endif + + +#define PICOQUIC_TRANSPORT_PARAMETERS_TLS_EXTENSION 0xFFA5 +#define PICOQUIC_TRANSPORT_PARAMETERS_MAX_SIZE 2048 + +#ifdef PTLS_ESNI_NONCE_SIZE +#define PICOQUIC_ESNI_NONCE_SIZE PTLS_ESNI_NONCE_SIZE +#else +#define PICOQUIC_ESNI_NONCE_SIZE 16 +#endif + + +typedef struct st_picoquic_tls_ctx_t { + ptls_t* tls; + picoquic_cnx_t* cnx; + int client_mode; + ptls_raw_extension_t ext[2]; + ptls_handshake_properties_t handshake_properties; + ptls_iovec_t alpn_vec[PICOQUIC_ALPN_NUMBER_MAX]; + int alpn_count; + uint8_t ext_data[PICOQUIC_TRANSPORT_PARAMETERS_MAX_SIZE]; + uint8_t ext_received[PICOQUIC_TRANSPORT_PARAMETERS_MAX_SIZE]; + size_t ext_received_length; + int ext_received_return; + uint16_t esni_version; + uint8_t esni_nonce[PICOQUIC_ESNI_NONCE_SIZE]; + uint8_t app_secret_enc[PTLS_MAX_DIGEST_SIZE]; + uint8_t app_secret_dec[PTLS_MAX_DIGEST_SIZE]; +} picoquic_tls_ctx_t; + +struct st_picoquic_log_event_t { + ptls_log_event_t super; + FILE* fp; +}; + + +/* This first part of this file provides a set of function for accessing + * the cryptographic libraries. + * The explicit calls to + */ +#define CRYPTO_PROVIDERS_REGION 1 + +#ifdef CRYPTO_PROVIDERS_REGION + + /* + * Make sure that openssl is properly initialized. + * + * The OpenSSL resources are allocated on first use, and not released until the end of the + * process. The only problem is when use memory leak tracers such as valgrind. The OpenSSL + * allocations will create a large number of issues, which may hide the actual leaks that + * should be fixed. To alleviate that, the application may use an explicit call to + * a global destructor like OPENSSL_cleanup(), but normally the OpenSSL stack does it + * during the process exit. + */ +static int openssl_is_init = 0; + +static void picoquic_init_openssl() +{ + if (openssl_is_init == 0) { + openssl_is_init = 1; + ERR_load_crypto_strings(); + OpenSSL_add_all_algorithms(); +#if !defined(OPENSSL_NO_ENGINE) + /* Load all compiled-in ENGINEs */ + ENGINE_load_builtin_engines(); + ENGINE_register_all_ciphers(); + ENGINE_register_all_digests(); +#endif + } +} + +ptls_cipher_suite_t* picoquic_cipher_suites[] = { + &ptls_openssl_aes128gcmsha256, + &ptls_openssl_aes256gcmsha384, +#ifdef PTLS_OPENSSL_HAVE_CHACHA20_POLY1305 + & ptls_openssl_chacha20poly1305sha256, +#else + /* No support for ChaCha 20 */ +#endif + NULL }; + +#if !defined(_WINDOWS) || defined(_WINDOWS64) +/* Definition of fusion versions of AESGCM */ +ptls_cipher_suite_t picoquic_fusion_aes128gcmsha256 = { PTLS_CIPHER_SUITE_AES_128_GCM_SHA256, &ptls_fusion_aes128gcm, + &ptls_openssl_sha256 }; +ptls_cipher_suite_t picoquic_fusion_aes256gcmsha384 = { PTLS_CIPHER_SUITE_AES_256_GCM_SHA384, &ptls_fusion_aes256gcm, + &ptls_openssl_sha384 }; +#endif + +/* Setting of cipher suites. This is provisional code, + using the most performant functions from openssl or fusion */ + +static int picoquic_set_cipher_suite_list(ptls_cipher_suite_t** selected_suites, int cipher_suite_id) +{ + int nb_suites = 0; + /* Check first if fusion is enabled */ +#if !defined(_WINDOWS) || defined(_WINDOWS64) + if (ptls_fusion_is_supported_by_cpu()) { + if (cipher_suite_id == 0 || cipher_suite_id == 128) { + selected_suites[nb_suites++] = &picoquic_fusion_aes128gcmsha256; + } + + if (cipher_suite_id == 0 || cipher_suite_id == 256) { + selected_suites[nb_suites++] = &picoquic_fusion_aes256gcmsha384; + } + } +#endif + if (nb_suites == 0) { + /* Fallback to openssl if fusion is not supported */ + if (cipher_suite_id == 0 || cipher_suite_id == 128) { + selected_suites[nb_suites++] = &ptls_openssl_aes128gcmsha256; + } + + if (cipher_suite_id == 0 || cipher_suite_id == 256) { + selected_suites[nb_suites++] = &ptls_openssl_aes256gcmsha384; + } + } +#ifdef PTLS_OPENSSL_HAVE_CHACHA20_POLY1305 + if (cipher_suite_id == 0 || cipher_suite_id == 20) { + selected_suites[nb_suites++] = &ptls_openssl_chacha20poly1305sha256; + } +#else + /* Consider getting ChaCha20 from mini crypto, despite poor performance */ +#endif + return nb_suites; +} + +static int picoquic_set_cipher_suite_in_ctx(ptls_context_t* ctx, int cipher_suite_id) +{ + ptls_cipher_suite_t** selected_suites = (ptls_cipher_suite_t**)malloc(sizeof(ptls_cipher_suite_t*) * 4); + int nb_suites = 0; + int ret = 0; + + if (ctx == NULL || selected_suites == NULL) { + ret = -1; + } + else { + nb_suites = picoquic_set_cipher_suite_list(selected_suites, cipher_suite_id); + + if (nb_suites == 0) { + ctx->cipher_suites = NULL; + ret = -1; + } + else { + while (nb_suites < 4) { + selected_suites[nb_suites++] = NULL; + } + ctx->cipher_suites = selected_suites; + } + } + + if (ret != 0 && selected_suites != NULL) { + free((void*)selected_suites); + } + return ret; +} + +/* Obtain an AES128 ECB cipher using openSSL */ +void* picoquic_aes128_ecb_openssl_create(int is_enc, const void* ecb_key) +{ + return (void*)ptls_cipher_new(&ptls_openssl_aes128ecb, is_enc, ecb_key); +} + +/* Obtain AES128GCM SHA256, AES256GCM_SHA384 or CHACHA20 suite according to current provider */ +ptls_cipher_suite_t* picoquic_get_selected_cipher_suite_by_id(int cipher_suite_id) +{ + ptls_cipher_suite_t* selected_suites[4]; + ptls_cipher_suite_t* cipher; + int nb_suites = picoquic_set_cipher_suite_list(selected_suites, cipher_suite_id); + if (nb_suites <= 0) { + cipher = NULL; + } + else { + cipher = selected_suites[0]; + } + + return cipher; +} + +/* Obtain the SHA256 hash algorithm used to create secrets + */ +ptls_hash_algorithm_t* picoquic_get_openssl_sha256() +{ + return &ptls_openssl_sha256; +} + +/* Setting the supported key exchange algorithms, + using definitions in openSSL */ + +ptls_key_exchange_algorithm_t* picoquic_key_exchanges[] = { &ptls_openssl_secp256r1, +#ifdef PTLS_OPENSSL_HAVE_CHACHA20_POLY1305 + & ptls_openssl_x25519, +#endif + NULL }; + +ptls_key_exchange_algorithm_t* picoquic_key_secp256r1[] = { &ptls_openssl_secp256r1, NULL }; + +#ifdef PTLS_OPENSSL_HAVE_CHACHA20_POLY1305 +ptls_key_exchange_algorithm_t* picoquic_key_x25519[] = { &ptls_openssl_x25519, NULL }; +#endif + + +static int picoquic_openssl_set_key_exchange_in_ctx(ptls_context_t* ctx, int key_exchange_id) +{ + int ret = 0; + + if (ctx == NULL) { + ret = -1; + } + else { + switch (key_exchange_id) { + case 0: + ctx->key_exchanges = picoquic_key_exchanges; + break; + case 20: +#ifdef PTLS_OPENSSL_HAVE_CHACHA20_POLY1305 + ctx->key_exchanges = picoquic_key_x25519; + break; +#else + ret = -1; + break; +#endif + case 128: + ctx->key_exchanges = picoquic_key_secp256r1; + break; + case 256: + ctx->key_exchanges = picoquic_key_secp256r1; + break; + default: + ret = -1; + break; + } + } + return ret; +} + +/* This function is only used as part of the ESNI code. + * The purpose is to obtain an "exchange context" that matches the type of key found in the + * file defining the ESNI key. It does not really belong in the basic "crypto provider" + * API, but rather in the API that handles private keys, etc. + * TODO: rewrite this as a function that uses generic APIs. + */ +static int picoquic_openssl_exchange_context_from_file(char const* esni_key_file_name, + ptls_key_exchange_context_t** p_exchange_ctx) +{ + int ret = 0; + EVP_PKEY* pkey = NULL; + BIO* bio = BIO_new_file(esni_key_file_name, "rb"); + pkey = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL); + if (pkey == NULL) { + DBG_PRINTF("%s", "failed to load private key"); + ret = PICOQUIC_ERROR_INVALID_FILE; + } + else { + *p_exchange_ctx = (ptls_key_exchange_context_t*)malloc(sizeof(ptls_key_exchange_context_t)); + if (*p_exchange_ctx == NULL) { + DBG_PRINTF("%s", "no memory for ESNI private key\n"); + ret = PICOQUIC_ERROR_MEMORY; + } + else if ((ret = ptls_openssl_create_key_exchange(p_exchange_ctx, pkey)) != 0) { + DBG_PRINTF("failed to load private key from file:%s:picotls-error:%d", esni_key_file_name, ret); + ret = PICOQUIC_ERROR_INVALID_FILE; + free(*p_exchange_ctx); + *p_exchange_ctx = NULL; + } + EVP_PKEY_free(pkey); + } + BIO_free(bio); + return ret; +} + + +/* Provide a certificate signature function, based on the implementation in openssl. + */ +static int set_openssl_sign_certificate_from_key(EVP_PKEY* pkey, ptls_context_t* ctx) +{ + int ret = 0; + ptls_openssl_sign_certificate_t* signer; + + signer = (ptls_openssl_sign_certificate_t*)malloc(sizeof(ptls_openssl_sign_certificate_t)); + + if (signer == NULL || pkey == NULL) { + ret = -1; + } + else { + ret = ptls_openssl_init_sign_certificate(signer, pkey); + ctx->sign_certificate = &signer->super; + } + + if (pkey != NULL) { + EVP_PKEY_free(pkey); + } + + if (ret != 0 && signer != NULL) { + free(signer); + } + + return ret; +} + +int picoquic_set_tls_key_openssl(ptls_context_t* ctx, const uint8_t* data, size_t len) +{ + if (ctx->sign_certificate != NULL) { + ptls_openssl_dispose_sign_certificate((ptls_openssl_sign_certificate_t*)ctx->sign_certificate); + ctx->sign_certificate = NULL; + } + + return set_openssl_sign_certificate_from_key(d2i_AutoPrivateKey(NULL, &data, (long)len), ctx); +} + +/* Read a private key from file using openSSL */ +static uint8_t* get_openssl_private_key_from_key_file(char const* file_name, int * key_length) +{ + unsigned char* key_der; + unsigned char* tmp; + int length; + + BIO* bio_key = BIO_new_file(file_name, "rb"); + /* Load key and convert to DER */ + EVP_PKEY* key = PEM_read_bio_PrivateKey(bio_key, NULL, NULL, NULL); + length = i2d_PrivateKey(key, NULL); + key_der = (unsigned char*)malloc(length); + tmp = key_der; + i2d_PrivateKey(key, &tmp); + EVP_PKEY_free(key); + BIO_free(bio_key); + + *key_length = length; + return key_der; +} + +/* Set the certificate signature function and context using openSSL + */ + +static int set_openssl_private_key_from_key_file(char const* keypem, ptls_context_t* ctx) +{ + int ret = 0; + BIO* bio = BIO_new_file(keypem, "rb"); + EVP_PKEY* pkey = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL); + if (pkey == NULL) { + DBG_PRINTF("%s", "failed to load private key"); + ret = -1; + } + else { + ret = set_openssl_sign_certificate_from_key(pkey, ctx); + } + BIO_free(bio); + return ret; +} + +/* Read certificates from a file using openSSL functions + * TODO: what if we need to read multiple certificates for the chain? + */ +static ptls_iovec_t* picoquic_openssl_get_certs_from_file(char const * file_name, size_t * count) +{ + BIO* bio_key = BIO_new_file(file_name, "rb"); + /* Load cert and convert to DER */ + X509* cert = PEM_read_bio_X509(bio_key, NULL, NULL, NULL); + int length = i2d_X509(cert, NULL); + unsigned char* cert_der = (unsigned char*)malloc(length); + unsigned char* tmp = cert_der; + i2d_X509(cert, &tmp); + X509_free(cert); + BIO_free(bio_key); + + ptls_iovec_t* chain = malloc(sizeof(ptls_iovec_t)); + if (chain == NULL) { + *count = 0; + } else { + *count = 1; + chain[0] = ptls_iovec_init(cert_der, length); + } + return chain; +} + +/* Clear certificate objects allocated via openssl for a certificate + */ +static void picoquic_openssl_dispose_sign_certificate(ptls_sign_certificate_t* cert) +{ + ptls_openssl_dispose_sign_certificate((ptls_openssl_sign_certificate_t*)cert); +} + +/* Use openssl functions to create a certficate verifier */ +ptls_openssl_verify_certificate_t* picoquic_openssl_get_certificate_verifier(char const * cert_root_file_name, + unsigned int * is_cert_store_not_empty) +{ + ptls_openssl_verify_certificate_t * verifier = (ptls_openssl_verify_certificate_t*)malloc(sizeof(ptls_openssl_verify_certificate_t)); + if (verifier != NULL) { + X509_STORE* store = X509_STORE_new(); + + if (cert_root_file_name != NULL && store != NULL) { + int file_ret = 0; + X509_LOOKUP* lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file()); + if ((file_ret = X509_LOOKUP_load_file(lookup, cert_root_file_name, X509_FILETYPE_PEM)) != 1) { + DBG_PRINTF("Cannot load X509 store (%s), ret = %d\n", + cert_root_file_name, file_ret); + } + else { + *is_cert_store_not_empty = 1; + } + } + + ptls_openssl_init_verify_certificate(verifier, store); + + // If we created an instance of the store, release our reference after giving it to the verify_certificate callback. + // The callback internally increased the reference counter by one. +#if OPENSSL_VERSION_NUMBER > 0x10100000L + if (store != NULL) { + X509_STORE_free(store); + } +#endif + } + return verifier; +} + +/* Set the list of root certificates used by the client. + * This implementation is specific to OpenSSL, because it is tied to the + * implementation of the verify certificate function. */ + +int picoquic_openssl_set_tls_root_certificates(picoquic_quic_t* quic, ptls_iovec_t* certs, size_t count) +{ + ptls_context_t* ctx = (ptls_context_t*)quic->tls_master_ctx; + ptls_openssl_verify_certificate_t* verify_ctx = (ptls_openssl_verify_certificate_t*)ctx->verify_certificate; + + for (size_t i = 0; i < count; ++i) { + X509* cert = d2i_X509(NULL, (const uint8_t**)&certs[i].base, (long)certs[i].len); + + if (cert == NULL) { + return -1; + } + + if (X509_STORE_add_cert(verify_ctx->cert_store, cert) == 0) { + X509_free(cert); + return -2; + } + + quic->is_cert_store_not_empty = 1; + + X509_free(cert); + } + + return 0; +} + +/* Explain OPENSSL errors */ +int picoquic_open_ssl_explain_crypto_error(char const** err_file, int* err_line) +{ + return ERR_get_error_line(err_file, err_line); +} + +/* Clear the recorded errors in the crypto stack, e.g. before + * processing a new message. + */ +void picoquic_openssl_clear_crypto_errors() +{ + ERR_clear_error(); +} +#endif /* CRYPTO_PROVIDERS_REGION */ + +#define CRYPTO_PROVIDERS_API_REGION 1 + +#ifdef CRYPTO_PROVIDERS_API_REGION +/* Implementation of generic setup functions using the default present + * in this file. These functions may be declared in tls_api.h. + */ + +void picoquic_init_crypto_provider() +{ + picoquic_init_openssl(); +} + +/* Set the cryptographic random provider */ +static void picoquic_set_random_provider_in_ctx(ptls_context_t* ctx) +{ + ctx->random_bytes = ptls_openssl_random_bytes; +} + +/* Set the cipher suites */ +int picoquic_set_cipher_suite(picoquic_quic_t* quic, int cipher_suite_id) +{ + ptls_context_t* ctx = (ptls_context_t*)quic->tls_master_ctx; + return (picoquic_set_cipher_suite_in_ctx(ctx, cipher_suite_id)); +} + +static ptls_cipher_suite_t* picoquic_get_cipher_suite_by_id(int cipher_suite_id) +{ + return picoquic_get_selected_cipher_suite_by_id(cipher_suite_id); +} + +/* Set the supported key exchange in the TLS context + * Supported algorithms are defined by keyexchange_id + * - 0: set all supported algorithms + * - 20: x25519 + * - 128 or 256: secp256r1 + */ + +static int picoquic_set_key_exchange_in_ctx(ptls_context_t* ctx, int key_exchange_id) +{ + return picoquic_openssl_set_key_exchange_in_ctx(ctx, key_exchange_id); +} + +/* Set a key exchange from a file containing a private key. + * This is used for the implementation of ESNI. + * TODO: rewrite the ESNI code to use generic APIs. + */ +int picoquic_exchange_context_from_file(char const* key_file_name, + ptls_key_exchange_context_t** p_exchange_ctx) +{ + return picoquic_openssl_exchange_context_from_file(key_file_name, p_exchange_ctx); +} + +/* Obtain an AES128 ECB cipher, which is required for CID encryption + * according the CID for load balancer specification. + * TODO: rewrite this as a call to the generic "get cipher suite" API, + * then derive the ECB function from the selection of the AEAD function. + * This will obviate the need of providing a specific API. + */ +void* picoquic_aes128_ecb_create(int is_enc, const void* ecb_key) +{ + return (void*)picoquic_aes128_ecb_openssl_create(is_enc, ecb_key); +} + +/* Export hash functions so applications do not need to access picotls. + * It is not clear that these functions are actually used by applications. + * TODO: maybe reuse the "cipher suite" API, and just obtain the hash + * function in cipher suite 128 (SHA256) or 256 (SHA384). + */ + +void* picoquic_hash_create(char const* algorithm_name) { + ptls_hash_context_t* ctx; + + if (strcmp(algorithm_name, "SHA256") == 0) { + ctx = ptls_openssl_sha256.create(); + } + else if (strcmp(algorithm_name, "SHA384") == 0) { + ctx = ptls_openssl_sha384.create(); + } + else { + ctx = NULL; + } + + return (void*)ctx; +} + +size_t picoquic_hash_get_length(char const* algorithm_name) { + size_t len; + + if (strcmp(algorithm_name, "SHA256") == 0) { + len = ptls_openssl_sha256.digest_size; + } + else if (strcmp(algorithm_name, "SHA384") == 0) { + len = ptls_openssl_sha384.digest_size; + } + else { + len = 0; + } + + return len; +} + +/* Obtain the SHA256 hash, used to derive some secrets + */ +ptls_hash_algorithm_t* picoquic_get_sha256() +{ + return picoquic_get_openssl_sha256(); +} + +void* picoquic_get_sha256_v() +{ + return (void*)picoquic_get_sha256(); +} + +/* Get private key from current crypto processor */ +uint8_t* picoquic_get_private_key_from_key_file(char const* file_name, int* key_length) +{ + return get_openssl_private_key_from_key_file(file_name, key_length); +} + +/* Set the certificate signing function in the context */ +static int set_private_key_from_key_file(char const* keypem, ptls_context_t* ctx) +{ + return set_openssl_private_key_from_key_file(keypem, ctx); +} + +/* Read certificates from a file + */ +ptls_iovec_t* picoquic_get_certs_from_file(char const* file_name, size_t * count) +{ + return picoquic_openssl_get_certs_from_file(file_name, count); +} + +/* Clear certificate objects allocated by the crypto stack for a certficate + */ +void picoquic_dispose_sign_certificate(ptls_sign_certificate_t* cert) +{ + picoquic_openssl_dispose_sign_certificate(cert); +} + +/* Set the list of root certificates used by the client. */ +int picoquic_set_tls_root_certificates(picoquic_quic_t* quic, ptls_iovec_t* certs, size_t count) +{ + return picoquic_openssl_set_tls_root_certificates(quic, certs, count); +} + +/* Set the TLS Key */ +int picoquic_set_tls_key(picoquic_quic_t* quic, const uint8_t* data, size_t len) +{ + ptls_context_t* ctx = (ptls_context_t*)quic->tls_master_ctx; + return picoquic_set_tls_key_openssl(ctx, data, len); +} + +/* Return the certificate verifier callback provided by the crypto stack */ +ptls_verify_certificate_t* picoquic_get_certificate_verifier(char const* cert_root_file_name, + unsigned int* is_cert_store_not_empty) +{ + ptls_openssl_verify_certificate_t* verifier = picoquic_openssl_get_certificate_verifier(cert_root_file_name, + is_cert_store_not_empty); + return (verifier == NULL) ? NULL : &verifier->super; +} + + +/* Release a verify certificate callback function. + * TODO: there should be a delete function documented at the same time the + * callback is installed, to allow replacing one type of callback by another. + */ + +void picoquic_dispose_certificate_verifier(ptls_verify_certificate_t* verifier) { + ptls_openssl_dispose_verify_certificate((ptls_openssl_verify_certificate_t*)verifier); +} + +/* Provide a crypto provider independent interface to crypto errors. + * Can be called repeatedly until no error needs to be signalled. + */ + +int picoquic_explain_crypto_error(char const** err_file, int* err_line) +{ + return picoquic_open_ssl_explain_crypto_error(err_file, err_line); +} + +/* Clear the recorded errors in the crypto stack, e.g. before + * processing a new message. + */ +void picoquic_clear_crypto_errors() +{ + picoquic_openssl_clear_crypto_errors(); +} + +#endif /* CRYPTO_PROVIDERS_API_REGION */ + +#define CRYPTO_PROVIDERS_GENERIC_REGION 1 + +#ifdef CRYPTO_PROVIDERS_GENERIC_REGION +/* Generic APIs, derived from the APi to crypto providers */ + + +/* Get the AES128GCM+SHA256 cipher suite required for Initial packets */ +static ptls_cipher_suite_t* picoquic_get_aes128gcm_sha256() +{ + return picoquic_get_cipher_suite_by_id(128); +} + +void* picoquic_get_aes128gcm_sha256_v() +{ + return (void*)picoquic_get_aes128gcm_sha256(); +} + +void* picoquic_get_aes128gcm_v() +{ + void* aead = NULL; + ptls_cipher_suite_t* cipher = picoquic_get_aes128gcm_sha256(); + + if (cipher != NULL) { + aead = (void*)(cipher->aead); + } + return aead; +} + +void* picoquic_get_cipher_suite_by_id_v(int cipher_suite_id) +{ + return (void*)picoquic_get_cipher_suite_by_id(cipher_suite_id); +} + +void picoquic_hash_update(uint8_t* input, size_t input_length, void* hash_context) { + ((ptls_hash_context_t*)hash_context)->update((ptls_hash_context_t*)hash_context, input, input_length); +} + +void picoquic_hash_finalize(uint8_t* output, void* hash_context) { + ((ptls_hash_context_t*)hash_context)->final((ptls_hash_context_t*)hash_context, output, PTLS_HASH_FINAL_MODE_FREE); +} + +#endif /* CRYPTO_PROVIDERS_GENERIC_REGION */ + +static void picoquic_setup_cleartext_aead_salt(size_t version_index, ptls_iovec_t* salt); + +static void picoquic_free_log_event(picoquic_quic_t* quic); + +void picoquic_log_crypto_errors(picoquic_cnx_t* cnx, int ret) +{ + unsigned long openssl_err; + char const* err_file = NULL; + int err_line = 0; + + while ((openssl_err = picoquic_explain_crypto_error(&err_file, &err_line)) != 0) { + picoquic_log_app_message(cnx, "OpenSSL error: %lu, file %s, line %d", openssl_err, + (err_file == NULL) ? "?" : err_file, err_line); + } + + picoquic_log_app_message(cnx, "Picotls returns error: %d (0x%x)", ret, ret); +} + + +int picoquic_server_setup_ticket_aead_contexts(picoquic_quic_t* quic, + ptls_context_t* tls_ctx, + const uint8_t* secret, size_t secret_length); + +/* + * Provide access to transport received transport extension for + * logging purpose. + */ +void picoquic_provide_received_transport_extensions(picoquic_cnx_t* cnx, + uint8_t** ext_received, + size_t* ext_received_length, + int* ext_received_return, + int* client_mode) +{ + picoquic_tls_ctx_t* ctx = (picoquic_tls_ctx_t*)cnx->tls_ctx; + + *ext_received = ctx->ext_received; + *ext_received_length = ctx->ext_received_length; + *ext_received_return = ctx->ext_received_return; + *client_mode = ctx->client_mode; +} + +/* Crypto random number generator */ + +void picoquic_crypto_random(picoquic_quic_t* quic, void* buf, size_t len) +{ + ptls_context_t* ctx = (ptls_context_t*)quic->tls_master_ctx; + + ctx->random_bytes(buf, len); +} + +uint64_t picoquic_crypto_uniform_random(picoquic_quic_t* quic, uint64_t rnd_max) +{ + uint64_t rnd; + uint64_t rnd_min = ((uint64_t)((int64_t)-1)) % rnd_max; + + do { + picoquic_crypto_random(quic, &rnd, sizeof(rnd)); + } while (rnd < rnd_min); + + return rnd % rnd_max; +} + +/* + * Non crypto public random generator. This is meant to provide good enough randomness + * without disclosing the state of the crypto random number generator. This is + * adequate for non critical random numbers, such as sequence numbers or padding. + * + * The following is an implementation of xorshift1024* suggested by Sebastiano Vigna, + * following the general xorshift design by George Marsaglia. + * The state must be seeded so that it is not everywhere zero. + * + * The seed operation gets 64 bits from the crypto random generator. We then run the + * generator 16 times to mix that input into the 1024 bits of seed[16]. + * + * In order to provide a minimum of protection against casual analysis, we run + * an obfuscation step before providing the result. The obfuscation involves + * multiply by a constant modulo, then XOR the result with obfuscator again. + * The obfuscator changes each time the random generator is seeded. + * + * If we were really paranoid, we would want to break possible discovery by passing + * the seeding bits from the crypto random generator through SHA256 or something + * similar, so there would be really no way to get at the state of crypto random + * generator. The 16 rounds of the xorshift process give a pretty good hash, but + * that can probably be broken by linear analysis. Or at least we have no proof + * that it cannot be broken. + */ + +static uint64_t public_random_seed[16] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 }; +static int public_random_index = 0; +static const uint64_t public_random_multiplier = 1181783497276652981ull; +static uint64_t public_random_obfuscator = 0x5555555555555555ull; + +static uint64_t picoquic_public_random_step(void) +{ + uint64_t s1; + const uint64_t s0 = public_random_seed[public_random_index++]; + public_random_index &= 15; + s1 = public_random_seed[public_random_index]; + s1 ^= (s1 << 31); // a + s1 ^= (s1 >> 11); // b + s1 ^= (s0 ^ (s0 >> 30)); // c + public_random_seed[public_random_index] = s1; + return s1; +} + +uint64_t picoquic_public_random_64(void) +{ + uint64_t s1 = picoquic_public_random_step(); + s1 *= public_random_multiplier; + s1 ^= public_random_obfuscator; + return s1; +} + +void picoquic_public_random_seed_64(uint64_t seed, int reset) +{ + if (reset) { + public_random_index = 0; + for (uint64_t i = 0; i < 16; i++) { + public_random_seed[i] = i + 1u; + } + public_random_obfuscator = 0x5555555555555555ull; + } + + public_random_seed[public_random_index] ^= seed; + + for (int i = 0; i < 16; i++) { + (void)picoquic_public_random_step(); + } +} + + +void picoquic_public_random_seed(picoquic_quic_t* quic) +{ + uint64_t seed[3]; + picoquic_crypto_random(quic, &seed, sizeof(seed)); + + picoquic_public_random_seed_64(seed[0], 0); + public_random_obfuscator = seed[1]; +} + +void picoquic_public_random(void* buf, size_t len) +{ + uint8_t* x = buf; + + while (len > 0) { + uint64_t y = picoquic_public_random_64(); + for (int i = 0; i < 8 && len > 0; i++) { + *x++ = (uint8_t)(y & 255); + y >>= 8; + len--; + } + } +} + +uint64_t picoquic_public_uniform_random(uint64_t rnd_max) +{ + uint64_t rnd; + uint64_t rnd_min = UINT64_MAX % rnd_max; + + do { + rnd = picoquic_public_random_64(); + } while (rnd < rnd_min); + + return rnd % rnd_max; +} + +/* + * The collect extensions call back is called by the picotls stack upon + * reception of a handshake message containing extensions. It should return true (1) + * if the stack can process the extension, false (0) otherwise. + */ + +int picoquic_tls_collect_extensions_cb(ptls_t* tls, struct st_ptls_handshake_properties_t* properties, uint16_t type) +{ +#ifdef _WINDOWS + UNREFERENCED_PARAMETER(tls); + UNREFERENCED_PARAMETER(properties); +#endif + return type == PICOQUIC_TRANSPORT_PARAMETERS_TLS_EXTENSION; +} + +void picoquic_tls_set_extensions(picoquic_cnx_t* cnx, picoquic_tls_ctx_t* tls_ctx) +{ + size_t consumed; + int ret = picoquic_prepare_transport_extensions(cnx, (tls_ctx->client_mode) ? 0 : 1, + tls_ctx->ext_data, sizeof(tls_ctx->ext_data), &consumed); + + if (ret == 0) { + tls_ctx->ext[0].type = PICOQUIC_TRANSPORT_PARAMETERS_TLS_EXTENSION; + tls_ctx->ext[0].data.base = tls_ctx->ext_data; + tls_ctx->ext[0].data.len = consumed; + tls_ctx->ext[1].type = 0xFFFF; + tls_ctx->ext[1].data.base = NULL; + tls_ctx->ext[1].data.len = 0; + } else { + tls_ctx->ext[0].type = 0xFFFF; + tls_ctx->ext[0].data.base = NULL; + tls_ctx->ext[0].data.len = 0; + } + + tls_ctx->handshake_properties.additional_extensions = tls_ctx->ext; +} + +/* + * The collected extensions call back is called by the stack upon + * reception of a handshake message containing supported extensions. + */ + +int picoquic_tls_collected_extensions_cb(ptls_t* tls, ptls_handshake_properties_t* properties, + ptls_raw_extension_t* slots) +{ +#ifdef _WINDOWS + UNREFERENCED_PARAMETER(tls); +#endif + int ret = 0; + size_t consumed = 0; + /* Find the context from the TLS context */ + picoquic_tls_ctx_t* ctx = (picoquic_tls_ctx_t*)((char*)properties - offsetof(struct st_picoquic_tls_ctx_t, handshake_properties)); + + for (int i_slot = 0; slots[i_slot].type != 0xFFFF; i_slot++) { + if (slots[i_slot].type == PICOQUIC_TRANSPORT_PARAMETERS_TLS_EXTENSION) { + size_t copied_length = sizeof(ctx->ext_received); + + /* Retrieve the transport parameters */ + ret = picoquic_receive_transport_extensions(ctx->cnx, (ctx->client_mode) ? 1 : 0, + slots[i_slot].data.base, slots[i_slot].data.len, &consumed); + + /* Copy the extensions in the local context for further debugging */ + ctx->ext_received_length = slots[i_slot].data.len; + if (copied_length > ctx->ext_received_length) + copied_length = ctx->ext_received_length; + memcpy(ctx->ext_received, slots[i_slot].data.base, copied_length); + ctx->ext_received_return = ret; + /* For now, override the value in case of default */ + ret = 0; + + /* In server mode, only compose the extensions if properly received from client */ + if (ctx->client_mode == 0) { + picoquic_tls_set_extensions(ctx->cnx, ctx); + } + } + } + + return ret; +} + +/* + * The Hello Call Back is called on the server side upon reception of the + * Client Hello. The picotls code will parse the client hello and retrieve + * parameters such as SNI and proposed ALPN. + * TODO: check the SNI in case several are supported. + * TODO: check the ALPN in case several are supported. + */ + +int picoquic_client_hello_call_back(ptls_on_client_hello_t* on_hello_cb_ctx, + ptls_t* tls, ptls_on_client_hello_parameters_t *params) +{ + const uint8_t * alpn_found = 0; + size_t alpn_found_length = 0; + int ret = 0; + picoquic_quic_t** ppquic = (picoquic_quic_t**)(((char*)on_hello_cb_ctx) + sizeof(ptls_on_client_hello_t)); + picoquic_quic_t* quic = *ppquic; + + /* Save the server name */ + ptls_set_server_name(tls, (const char *)params->server_name.base, params->server_name.len); + +#ifdef PTLS_ESNI_NONCE_SIZE + if (params->esni && quic->cnx_in_progress != NULL) { + /* Find the ESNI secret if any, and copy key values to picoquic tls context */ + picoquic_tls_ctx_t* tls_ctx = (picoquic_tls_ctx_t*)quic->cnx_in_progress->tls_ctx; + struct st_ptls_esni_secret_t * esni = ptls_get_esni_secret(tls_ctx->tls); + if (esni != NULL) { + tls_ctx->esni_version = esni->version; + memcpy(tls_ctx->esni_nonce, esni->nonce, PTLS_ESNI_NONCE_SIZE); + } + } +#endif + + /* Check if the client is proposing the expected ALPN */ + if (quic->default_alpn != NULL) { + size_t len = strlen(quic->default_alpn); + + for (size_t i = 0; i < params->negotiated_protocols.count; i++) { + if (params->negotiated_protocols.list[i].len == len && memcmp(params->negotiated_protocols.list[i].base, quic->default_alpn, len) == 0) { + if (quic->cnx_in_progress != NULL) { + picoquic_log_app_message(quic->cnx_in_progress, "ALPN[%d] matches default alpn (%s)", (int)i, quic->default_alpn); + } + alpn_found = (const uint8_t *)quic->default_alpn; + alpn_found_length = len; + ptls_set_negotiated_protocol(tls, quic->default_alpn, len); + break; + } + } + } + else if (quic->alpn_select_fn != NULL) { + size_t selected = quic->alpn_select_fn(quic, params->negotiated_protocols.list, params->negotiated_protocols.count); + + if (selected < params->negotiated_protocols.count) { + alpn_found = params->negotiated_protocols.list[selected].base; + alpn_found_length = params->negotiated_protocols.list[selected].len; + ptls_set_negotiated_protocol(tls, (const char *)params->negotiated_protocols.list[selected].base, params->negotiated_protocols.list[selected].len); + } + } + + if (quic->cnx_in_progress != NULL) { + picoquic_log_negotiated_alpn(quic->cnx_in_progress, + 0, params->server_name.base, params->server_name.len, alpn_found, alpn_found_length, + params->negotiated_protocols.list, params->negotiated_protocols.count); + } + + /* ALPN is mandatory in Quic. Return an error if no match found. */ + if (alpn_found == NULL) { + ret = PTLS_ALERT_NO_APPLICATION_PROTOCOL; + } + + if (ret != 0 && quic->cnx_in_progress != NULL) { + picoquic_log_app_message(quic->cnx_in_progress, "Client Hello call back returns %d (0x%x)", ret, ret); + } + + return ret; +} + +/* + * The server will generate session tickets if some parameters are set in the server + * TLS context, including: + * - the session ticket encryption callback, defined per the "encrypt_ticket" member of the context. + * - the session ticket lifetime, defined per the "ticket_life_time" member of the context. + * The encrypt call back is called on the server side when a session resume ticket is ready. + * The call is: + * cb(tls->ctx->encrypt_ticket, tls, 1, sendbuf, + * ptls_iovec_init(session_id.base, session_id.off)) + * The call to decrypt is: + * tls->ctx->encrypt_ticket->cb(tls->ctx->encrypt_ticket, tls, 0, &decbuf, identity->identity) + * Should return 0 if the ticket is good, etc. + */ + +int picoquic_server_encrypt_ticket_call_back(ptls_encrypt_ticket_t* encrypt_ticket_ctx, + ptls_t* tls, int is_encrypt, ptls_buffer_t* dst, ptls_iovec_t src) +{ +#ifdef _WINDOWS + UNREFERENCED_PARAMETER(tls); +#endif + + /* Assume that the keys are in the quic context + * The tickets are composed of a 64 bit "sequence number" + * followed by the result of the clear text encryption. + */ + int ret = 0; + picoquic_quic_t** ppquic = (picoquic_quic_t**)(((char*)encrypt_ticket_ctx) + sizeof(ptls_encrypt_ticket_t)); + picoquic_quic_t* quic = *ppquic; + + if (is_encrypt != 0) { + ptls_aead_context_t* aead_enc = (ptls_aead_context_t*)quic->aead_encrypt_ticket_ctx; + /* Encoding*/ + if (aead_enc == NULL) { + ret = -1; + } else if ((ret = ptls_buffer_reserve(dst, 8 + src.len + aead_enc->algo->tag_size)) == 0) { + /* Create and store the ticket sequence number */ + uint64_t seq_num = picoquic_public_random_64(); + picoformat_64(dst->base + dst->off, seq_num); + dst->off += 8; + /* Run AEAD encryption */ + dst->off += ptls_aead_encrypt(aead_enc, dst->base + dst->off, + src.base, src.len, seq_num, NULL, 0); + } + } else { + ptls_aead_context_t* aead_dec = (ptls_aead_context_t*)quic->aead_decrypt_ticket_ctx; + /* Encoding*/ + if (aead_dec == NULL) { + ret = -1; + } else if (src.len < 8 + aead_dec->algo->tag_size) { + ret = -1; + } else if ((ret = ptls_buffer_reserve(dst, src.len)) == 0) { + /* Decode the ticket sequence number */ + uint64_t seq_num = PICOPARSE_64(src.base); + /* Decrypt */ + size_t decrypted = ptls_aead_decrypt(aead_dec, dst->base + dst->off, + src.base + 8, src.len - 8, seq_num, NULL, 0); + + if (decrypted > src.len - 8) { + /* decryption error */ + ret = -1; + if (quic->F_log != NULL) { + picoquic_log_app_message(quic->cnx_in_progress, "%s", + "Session ticket could not be decrypted"); + } + } else { + dst->off += decrypted; + if (quic->F_log != NULL) { + picoquic_log_app_message(quic->cnx_in_progress, "%s", + "Session ticket properly decrypted"); + } + } + } + } + + return ret; +} + +/* + * The client signals its willingness to receive session resume tickets by providing + * the "save ticket" callback in the client's quic context. + */ + +int picoquic_client_save_ticket_call_back(ptls_save_ticket_t* save_ticket_ctx, + ptls_t* tls, ptls_iovec_t input) +{ + int ret = 0; + picoquic_quic_t* quic = *((picoquic_quic_t**)(((char*)save_ticket_ctx) + sizeof(ptls_save_ticket_t))); + const char* sni = ptls_get_server_name(tls); + const char* alpn = ptls_get_negotiated_protocol(tls); + picoquic_cnx_t * cnx = (picoquic_cnx_t *)*ptls_get_data_ptr(tls); + + if (alpn == NULL && quic != NULL) { + alpn = quic->default_alpn; + } + + if (sni != NULL && alpn != NULL) { + ret = picoquic_store_ticket(&quic->p_first_ticket, 0, sni, (uint16_t)strlen(sni), + alpn, (uint16_t)strlen(alpn), input.base, (uint16_t)input.len, &cnx->remote_parameters); + } else { + DBG_PRINTF("Received incorrect session resume ticket, sni = %s, alpn = %s, length = %d\n", + (sni == NULL) ? "NULL" : sni, (alpn == NULL) ? "NULL" : alpn, (int)input.len); + } + + return ret; +} + +/* + * Time get callback + */ +uint64_t picoquic_get_simulated_time_cb(ptls_get_time_t* self) +{ + uint64_t** pp_simulated_time = (uint64_t**)(((char*)self) + sizeof(ptls_get_time_t)); + return ((**pp_simulated_time) / 1000); +} + +/* + * Verify certificate + */ +typedef struct { + ptls_verify_certificate_t cb; + picoquic_quic_t *quic; +} picoquic_verify_certificate_t; + +typedef struct { + /* The pointer to the overlying `verify_ctx` */ + void *verify_ctx; + int (*verify_sign)(void *verify_ctx, ptls_iovec_t data, ptls_iovec_t sign); +} picoquic_verify_ctx_t; + +static int verify_sign_callback(void *verify_ctx, ptls_iovec_t data, ptls_iovec_t sign) +{ + picoquic_verify_ctx_t* ctx = (picoquic_verify_ctx_t*)verify_ctx; + int ret = 0; + + ret = ctx->verify_sign(ctx->verify_ctx, data, sign); + + free(ctx); + + return ret; +} + +static int verify_certificate_callback(ptls_verify_certificate_t* _self, ptls_t* tls, + int (**verify_sign)(void *verify_ctx, ptls_iovec_t data, ptls_iovec_t sign), + void **verify_data, + ptls_iovec_t *certs, + size_t num_certs) +{ + picoquic_verify_certificate_t *self = container_of(_self, picoquic_verify_certificate_t, cb); + picoquic_cnx_t* cnx = (picoquic_cnx_t*)*ptls_get_data_ptr(tls); + int ret = 0; + void *verify_ctx = NULL; + picoquic_verify_sign_cb_fn verify_sign_fn = NULL; + + ret = (self->quic->verify_certificate_callback_fn)(self->quic->verify_certificate_ctx, cnx, + certs, num_certs, &verify_sign_fn, &verify_ctx); + + if (ret == 0) { + *verify_sign = verify_sign_callback; + *verify_data = malloc(sizeof(picoquic_verify_ctx_t)); + if (*verify_data != NULL) { + ((picoquic_verify_ctx_t*)*verify_data)->verify_ctx = verify_ctx; + ((picoquic_verify_ctx_t*)*verify_data)->verify_sign = verify_sign_fn; + } + } + + return ret; +} + +int picoquic_enable_custom_verify_certificate_callback(picoquic_quic_t* quic) { + picoquic_verify_certificate_t* verifier = NULL; + ptls_context_t* ctx = (ptls_context_t*)quic->tls_master_ctx; + + assert(quic->verify_certificate_callback_fn != NULL); + + verifier = (picoquic_verify_certificate_t*)malloc(sizeof(picoquic_verify_certificate_t)); + if (verifier == NULL) { + return PICOQUIC_ERROR_MEMORY; + } else { + verifier->quic = quic; + verifier->cb.cb = verify_certificate_callback; + ctx->verify_certificate = &verifier->cb; + quic->is_cert_store_not_empty = 1; + + return 0; + } +} + + +void picoquic_dispose_verify_certificate_callback(picoquic_quic_t* quic, int custom) { + ptls_context_t* ctx = (ptls_context_t*)quic->tls_master_ctx; + + if (ctx->verify_certificate == NULL) { + return; + } + + if (custom == 0) { + picoquic_dispose_certificate_verifier(ctx->verify_certificate); + } + free(ctx->verify_certificate); + + ctx->verify_certificate = NULL; +} + +/* set key from secret: this is used to create AEAD contexts and PN encoding contexts + * after a key update callback, and also to create the initial keys from a locally + * computed secret + */ +static int picoquic_set_aead_from_secret(void ** v_aead,ptls_cipher_suite_t * cipher, int is_enc, const void *secret) +{ + int ret = 0; + + if (*v_aead != NULL) { + ptls_aead_free((ptls_aead_context_t*)*v_aead); + } + + if ((*v_aead = ptls_aead_new(cipher->aead, cipher->hash, is_enc, secret, PICOQUIC_LABEL_QUIC_KEY_BASE)) == NULL) { + ret = PTLS_ERROR_NO_MEMORY; + } + + return ret; +} + +static int picoquic_set_pn_enc_from_secret(void ** v_pn_enc, ptls_cipher_suite_t * cipher, int is_enc, const void *secret) +{ + uint8_t pnekey[PTLS_MAX_SECRET_SIZE]; + int ret; + + if (*v_pn_enc != NULL) { + ptls_cipher_free((ptls_cipher_context_t *)*v_pn_enc); + *v_pn_enc = NULL; + } + + if ((ret = ptls_hkdf_expand_label(cipher->hash, pnekey, + cipher->aead->ctr_cipher->key_size, ptls_iovec_init(secret, cipher->hash->digest_size), + PICOQUIC_LABEL_HP, ptls_iovec_init(NULL, 0), PICOQUIC_LABEL_QUIC_KEY_BASE)) == 0) { + if ((*v_pn_enc = ptls_cipher_new(cipher->aead->ctr_cipher, is_enc, pnekey)) == NULL) { + ret = PTLS_ERROR_NO_MEMORY; + } + } + + return ret; +} + +void picoquic_aes128_ecb_free(void * v_aesecb) +{ + ptls_cipher_free((ptls_cipher_context_t *)v_aesecb); +} + +void picoquic_aes128_ecb_encrypt(void* v_aesecb, uint8_t * output, const uint8_t * input, size_t len) +{ + ptls_cipher_encrypt((ptls_cipher_context_t*)v_aesecb, output, input, len); +} + +static int picoquic_set_key_from_secret(ptls_cipher_suite_t * cipher, int is_enc, int is_rotation, picoquic_crypto_context_t * ctx, const void *secret) +{ + int ret = 0; + + if (is_enc != 0) { + ret = picoquic_set_aead_from_secret(&ctx->aead_encrypt, cipher, is_enc, secret); + + if (ret == 0 && !is_rotation) { + ret = picoquic_set_pn_enc_from_secret(&ctx->pn_enc, cipher, is_enc, secret); + } + } else { + ret = picoquic_set_aead_from_secret(&ctx->aead_decrypt, cipher, is_enc, secret); + + if (ret == 0 && !is_rotation) { + ret = picoquic_set_pn_enc_from_secret(&ctx->pn_dec, cipher, is_enc, secret); + } + } + + return ret; +} + + +/* Key update callback: this is called by TLS whenever the session key has changed, + * from the function "setup_traffic_protection" in picotls.c. + * + * The macro generated callback struct is: + * typedef struct st_ptls_update_traffic_key_t { + * ret (*cb)(struct st_ptls_update_traffic_key_t * self, ptls_t *tls, int is_enc, size_t epoch, const void *secret); + * } ptls_update_traffic_key_t; + * + * The parameters are defined as: + * - self -- classic callback structure in picotls, can be remapped to hold additional arguments. + * - tls -- the tls context of the connection + * - is_enc -- 0: decryption key, 1: decryption key + * - epoch -- 1: "c e traffic" + * -- 2: "s hs traffic" + * -- 2: "c hs traffic" + * -- 3: "s ap traffic" + * -- 3: "c ap traffic" + * - secret -- the expansion of the master secret with the label specific to the key epoch + * and client or server mode. + */ + +typedef struct st_picoquic_update_traffic_key_t { + int(*cb)(struct st_ptls_update_traffic_key_t * self, ptls_t *tls, int is_enc, size_t epoch, const void *secret); + picoquic_cnx_t *cnx; +} picoquic_update_traffic_key_t; + +static int picoquic_update_traffic_key_callback(ptls_update_traffic_key_t * self, ptls_t *tls, int is_enc, size_t epoch, const void *secret) +{ + picoquic_cnx_t* cnx = (picoquic_cnx_t*)*ptls_get_data_ptr(tls); + picoquic_tls_ctx_t * tls_ctx = (picoquic_tls_ctx_t *)cnx->tls_ctx; + ptls_context_t* ctx = (ptls_context_t*)cnx->quic->tls_master_ctx; + ptls_cipher_suite_t * cipher = ptls_get_cipher(tls); + UNREFERENCED_PARAMETER(self); + + int ret = picoquic_set_key_from_secret(cipher, is_enc, 0, &cnx->crypto_context[epoch], secret); + if (cnx->cnx_state < picoquic_state_ready) { + cnx->recycle_sooner_needed = 1; + } + + if (ret == 0 && epoch == 3) { + memcpy((is_enc) ? tls_ctx->app_secret_enc : tls_ctx->app_secret_dec, secret, cipher->hash->digest_size); + } + + if (ctx->log_event != NULL) { + char hexbuf[PTLS_MAX_DIGEST_SIZE * 2 + 1]; + static const char *log_labels[2][4] = { + {NULL, "CLIENT_EARLY_TRAFFIC_SECRET", "CLIENT_HANDSHAKE_TRAFFIC_SECRET", "CLIENT_TRAFFIC_SECRET_0"}, + {NULL, NULL, "SERVER_HANDSHAKE_TRAFFIC_SECRET", "SERVER_TRAFFIC_SECRET_0"}}; + const char *secret_label = log_labels[ptls_is_server(tls) == is_enc][epoch]; + ptls_hexdump(hexbuf, secret, cipher->hash->digest_size); + ctx->log_event->cb(ctx->log_event, tls, secret_label, "%s", hexbuf); + } + + return ret; +} + +ptls_update_traffic_key_t * picoquic_set_update_traffic_key_callback() { + ptls_update_traffic_key_t * cb_st = (ptls_update_traffic_key_t *)malloc(sizeof(ptls_update_traffic_key_t)); + + if (cb_st != NULL) { + memset(cb_st, 0, sizeof(ptls_update_traffic_key_t)); + cb_st->cb = picoquic_update_traffic_key_callback; + } + + return cb_st; +} + +int picoquic_setup_initial_master_secret( + ptls_cipher_suite_t * cipher, + ptls_iovec_t salt, + picoquic_connection_id_t initial_cnxid, + uint8_t * master_secret) +{ + int ret = 0; + ptls_iovec_t ikm; + uint8_t cnx_id_serialized[PICOQUIC_CONNECTION_ID_MAX_SIZE]; + + ikm.len = picoquic_format_connection_id(cnx_id_serialized, PICOQUIC_CONNECTION_ID_MAX_SIZE, + initial_cnxid); + ikm.base = cnx_id_serialized; + + /* Extract the master key -- key length will be 32 per SHA256 */ + ret = ptls_hkdf_extract(cipher->hash, master_secret, salt, ikm); + + return ret; +} + +int picoquic_setup_initial_secrets( + ptls_cipher_suite_t * cipher, + uint8_t * master_secret, + uint8_t * client_secret, + uint8_t * server_secret) +{ + int ret = 0; + ptls_iovec_t prk; + + prk.base = master_secret; + prk.len = cipher->hash->digest_size; + + /* Get the client secret */ + ret = ptls_hkdf_expand_label(cipher->hash, client_secret, cipher->hash->digest_size, + prk, PICOQUIC_LABEL_INITIAL_CLIENT, ptls_iovec_init(NULL, 0), NULL); + + if (ret == 0) { + /* Get the server secret */ + ret = ptls_hkdf_expand_label(cipher->hash, server_secret, cipher->hash->digest_size, + prk, PICOQUIC_LABEL_INITIAL_SERVER, ptls_iovec_init(NULL, 0), NULL); + } + + return ret; +} + +int picoquic_setup_initial_traffic_keys(picoquic_cnx_t* cnx) +{ + int ret = 0; + uint8_t master_secret[256]; /* secret_max */ + ptls_cipher_suite_t * cipher = picoquic_get_aes128gcm_sha256(); + ptls_iovec_t salt; + uint8_t client_secret[256]; + uint8_t server_secret[256]; + uint8_t *secret1, *secret2; + + if (cipher == NULL) { + ret = -1; + } + else { + picoquic_setup_cleartext_aead_salt(cnx->version_index, &salt); + + /* Extract the master key -- key length will be 32 per SHA256 */ + ret = picoquic_setup_initial_master_secret(cipher, salt, cnx->initial_cnxid, master_secret); + } + + /* set up client and server secrets */ + if (ret == 0) { + ret = picoquic_setup_initial_secrets(cipher, master_secret, client_secret, server_secret); + } + + /* derive the initial keys */ + if (ret == 0) { + if (!cnx->client_mode) { + secret1 = server_secret; + secret2 = client_secret; + } + else { + secret1 = client_secret; + secret2 = server_secret; + } + + ret = picoquic_set_key_from_secret(cipher, 1, 0, &cnx->crypto_context[0], secret1); + + if (ret == 0) { + ret = picoquic_set_key_from_secret(cipher, 0, 0, &cnx->crypto_context[0], secret2); + } + } + + return ret; +} + +/* + * Key rotation. + * + * The old keys get moved to the old crypto context. + * The secrets are rotated. + * The new context gets informed. + * + * The key update is defined in RFC 8446 section 7.2 as: + * application_traffic_secret_N+1 = + * HKDF-Expand-Label(application_traffic_secret_N, + * "quic ku", "", Hash.length) + * Label: PICOQUIC_LABEL_TRAFFIC_UPDATE + */ +int picoquic_rotate_app_secret(ptls_cipher_suite_t * cipher, uint8_t * secret) +{ + int ret = 0; + uint8_t new_secret[PTLS_MAX_DIGEST_SIZE]; + + ret = ptls_hkdf_expand_label(cipher->hash, new_secret, + cipher->hash->digest_size, ptls_iovec_init(secret, cipher->hash->digest_size), PICOQUIC_LABEL_TRAFFIC_UPDATE, + ptls_iovec_init(NULL, 0), PICOQUIC_LABEL_QUIC_BASE); + if (ret == 0) { + memcpy(secret, new_secret, cipher->hash->digest_size); + } + + return ret; +} + + +uint8_t * picoquic_get_app_secret(picoquic_cnx_t* cnx, int is_enc) +{ + picoquic_tls_ctx_t * tls_ctx = (picoquic_tls_ctx_t *)cnx->tls_ctx; + + return (is_enc) ?tls_ctx->app_secret_enc:tls_ctx->app_secret_dec; +} + +size_t picoquic_get_app_secret_size(picoquic_cnx_t* cnx) +{ + picoquic_tls_ctx_t * tls_ctx = (picoquic_tls_ctx_t *)cnx->tls_ctx; + + ptls_cipher_suite_t * cipher = ptls_get_cipher(tls_ctx->tls); + + return (cipher->hash->digest_size); +} + +int picoquic_compute_new_rotated_keys(picoquic_cnx_t * cnx) +{ + int ret = 0; + picoquic_tls_ctx_t * tls_ctx = (picoquic_tls_ctx_t *)cnx->tls_ctx; + ptls_cipher_suite_t * cipher = ptls_get_cipher(tls_ctx->tls); + + /* Verify that the previous transition is complete */ + if (cnx->crypto_context_new.aead_decrypt != NULL || + cnx->crypto_context_new.aead_encrypt != NULL) { + if (cnx->crypto_context_new.aead_decrypt == NULL || + cnx->crypto_context_new.aead_encrypt == NULL) { + ret = PICOQUIC_ERROR_CANNOT_COMPUTE_KEY; + } + else { + /* already computed */ + return 0; + } + } + + /* Recompute the secrets */ + if (ret == 0) { + ret = picoquic_rotate_app_secret(cipher, tls_ctx->app_secret_enc); +#ifdef _DEBUG + if (ret == 0) { + DBG_PRINTF("Rotated Encryption Secret (%d):\n", (int)cipher->hash->digest_size); + debug_dump(tls_ctx->app_secret_enc, (int)cipher->hash->digest_size); + } + else { + DBG_PRINTF("Encryption secret rotation fails, ret=%x\n", ret); + } +#endif + } + + if (ret == 0) { + ret = picoquic_set_key_from_secret(cipher, 1, 1, &cnx->crypto_context_new, tls_ctx->app_secret_enc); + } + + if (ret == 0) { + ret = picoquic_rotate_app_secret(cipher, tls_ctx->app_secret_dec); +#ifdef _DEBUG + if (ret == 0) { + DBG_PRINTF("Rotated Decryption Secret (%d):\n", (int)cipher->hash->digest_size); + debug_dump(tls_ctx->app_secret_dec, (int)cipher->hash->digest_size); + } + else { + DBG_PRINTF("Decryption secret rotation fails, ret=%x\n", ret); + } +#endif + + } + + if (ret == 0) { + ret = picoquic_set_key_from_secret(cipher, 0, 1, &cnx->crypto_context_new, tls_ctx->app_secret_dec); + } + + return (ret == 0)?0: PICOQUIC_ERROR_CANNOT_COMPUTE_KEY; +} + +void picoquic_apply_rotated_keys(picoquic_cnx_t * cnx, int is_enc) +{ + if (is_enc) { + if (cnx->crypto_context[3].aead_encrypt != NULL) { + ptls_aead_free((ptls_aead_context_t *)cnx->crypto_context[3].aead_encrypt); + } + + cnx->crypto_context[3].aead_encrypt = cnx->crypto_context_new.aead_encrypt; + cnx->crypto_context_new.aead_encrypt = NULL; + + cnx->key_phase_enc ^= 1; + picoquic_log_pn_dec_trial(cnx); + } + else { + if (cnx->crypto_context_old.aead_decrypt != NULL) { + ptls_aead_free((ptls_aead_context_t *)cnx->crypto_context_old.aead_decrypt); + } + + cnx->crypto_context_old.aead_decrypt = cnx->crypto_context[3].aead_decrypt; + cnx->crypto_context[3].aead_decrypt = cnx->crypto_context_new.aead_decrypt; + cnx->crypto_context_new.aead_decrypt = NULL; + + cnx->key_phase_dec ^= 1; + } +} + +/* + * Release the crypto context, and the associated keys. + */ + +void picoquic_crypto_context_free(picoquic_crypto_context_t * ctx) +{ + if (ctx->aead_encrypt != NULL) { + ptls_aead_free((ptls_aead_context_t *)ctx->aead_encrypt); + ctx->aead_encrypt = NULL; + } + + if (ctx->aead_decrypt != NULL) { + ptls_aead_free((ptls_aead_context_t *)ctx->aead_decrypt); + ctx->aead_decrypt = NULL; + } + + if (ctx->pn_enc != NULL) { + ptls_cipher_free((ptls_cipher_context_t *)ctx->pn_enc); + ctx->pn_enc = NULL; + } + + if (ctx->pn_dec != NULL) { + ptls_cipher_free((ptls_cipher_context_t *)ctx->pn_dec); + ctx->pn_dec = NULL; + } +} + +/* + * Setting the master TLS context. + * On servers, this implies setting the "on hello" call back + */ + +int picoquic_master_tlscontext(picoquic_quic_t* quic, + char const* cert_file_name, char const* key_file_name, const char * cert_root_file_name, + const uint8_t* ticket_key, size_t ticket_key_length) +{ + /* Create a client context or a server context */ + int ret = 0; + ptls_context_t* ctx; + ptls_on_client_hello_t* och = NULL; + ptls_encrypt_ticket_t* encrypt_ticket = NULL; + ptls_save_ticket_t* save_ticket = NULL; + unsigned int is_cert_store_not_empty = 0; + + picoquic_init_crypto_provider(); /* For example, init openSSL if in use. */ + + ctx = (ptls_context_t*)malloc(sizeof(ptls_context_t)); + + if (ctx == NULL) { + ret = -1; + } + else { + memset(ctx, 0, sizeof(ptls_context_t)); + picoquic_set_random_provider_in_ctx(ctx); + + ret = picoquic_set_key_exchange_in_ctx(ctx, 0); /* was: ctx->key_exchanges = picoquic_key_exchanges; */ + + if (ret == 0) { + ret = picoquic_set_cipher_suite_in_ctx(ctx, 0); /* was: ptls_openssl_cipher_suites; */ + } + + if (ret == 0) { + ctx->send_change_cipher_spec = 0; + + ctx->hkdf_label_prefix__obsolete = NULL; + ctx->update_traffic_key = picoquic_set_update_traffic_key_callback(); + + if (quic->p_simulated_time == NULL) { + ctx->get_time = &ptls_get_time; + } + else { + ptls_get_time_t* time_getter = (ptls_get_time_t*)malloc(sizeof(ptls_get_time_t) + sizeof(uint64_t*)); + if (time_getter == NULL) { + ret = PICOQUIC_ERROR_MEMORY; + } + else { + uint64_t** pp_simulated_time = (uint64_t**)(((char*)time_getter) + sizeof(ptls_get_time_t)); + + time_getter->cb = picoquic_get_simulated_time_cb; + *pp_simulated_time = quic->p_simulated_time; + ctx->get_time = time_getter; + } + } + + if (cert_file_name != NULL && key_file_name != NULL) { + /* Read the certificate file */ + if (ptls_load_certificates(ctx, (char*)cert_file_name) != 0) { + ret = -1; + } + else { + ret = set_private_key_from_key_file(key_file_name, ctx); + } + } + } + + if (ret == 0) { + och = (ptls_on_client_hello_t*)malloc(sizeof(ptls_on_client_hello_t) + sizeof(picoquic_quic_t*)); + if (och != NULL) { + picoquic_quic_t** ppquic = (picoquic_quic_t**)(((char*)och) + sizeof(ptls_on_client_hello_t)); + + och->cb = picoquic_client_hello_call_back; + ctx->on_client_hello = och; + *ppquic = quic; + } else { + ret = PICOQUIC_ERROR_MEMORY; + } + } + + if (ret == 0) { + ret = picoquic_server_setup_ticket_aead_contexts(quic, ctx, ticket_key, ticket_key_length); + } + + if (ret == 0) { + encrypt_ticket = (ptls_encrypt_ticket_t*)malloc(sizeof(ptls_encrypt_ticket_t) + sizeof(picoquic_quic_t*)); + if (encrypt_ticket == NULL) { + ret = PICOQUIC_ERROR_MEMORY; + } else { + picoquic_quic_t** ppquic = (picoquic_quic_t**)(((char*)encrypt_ticket) + sizeof(ptls_encrypt_ticket_t)); + + encrypt_ticket->cb = picoquic_server_encrypt_ticket_call_back; + *ppquic = quic; + + ctx->encrypt_ticket = encrypt_ticket; + ctx->ticket_lifetime = 100000; /* 100,000 seconds, a bit more than one day */ + ctx->require_dhe_on_psk = 1; + ctx->max_early_data_size = 0xFFFFFFFF; + } + } + + ctx->verify_certificate = picoquic_get_certificate_verifier(cert_root_file_name, &is_cert_store_not_empty); + quic->is_cert_store_not_empty = is_cert_store_not_empty; + + if (quic->ticket_file_name != NULL) { + save_ticket = (ptls_save_ticket_t*)malloc(sizeof(ptls_save_ticket_t) + sizeof(picoquic_quic_t*)); + if (save_ticket != NULL) { + picoquic_quic_t** ppquic = (picoquic_quic_t**)(((char*)save_ticket) + sizeof(ptls_save_ticket_t)); + + save_ticket->cb = picoquic_client_save_ticket_call_back; + ctx->save_ticket = save_ticket; + *ppquic = quic; + } + } + + if (ret == 0) { + /* Tell Picotls to not require EOED messages during handshake */ + ctx->omit_end_of_early_data = 1; + } + + if (ret == 0) { + quic->tls_master_ctx = ctx; + picoquic_public_random_seed(quic); + } else { + free(ctx); + } + } + + return ret; +} + +static void free_certificates_list(ptls_iovec_t* certs, size_t len) { + if (certs == NULL) { + return; + } + + for (size_t i = 0; i < len; ++i) { + free(certs[i].base); + } + free(certs); +} + + +int picoquic_set_key_exchange(picoquic_quic_t* quic, int key_exchange_id) +{ + int ret = 0; + ptls_context_t* ctx = (ptls_context_t*)quic->tls_master_ctx; + + ret = picoquic_set_key_exchange_in_ctx(ctx, key_exchange_id); + return ret; +} + +void picoquic_master_tlscontext_free(picoquic_quic_t* quic) +{ + if (quic->tls_master_ctx != NULL) { + ptls_context_t* ctx = (ptls_context_t*)quic->tls_master_ctx; + + if (quic->p_simulated_time != NULL && ctx->get_time != NULL) { + free(ctx->get_time); + ctx->get_time = NULL; + } + + free_certificates_list(ctx->certificates.list, ctx->certificates.count); + + if (ctx->sign_certificate != NULL) { + picoquic_dispose_sign_certificate(ctx->sign_certificate); + free(ctx->sign_certificate); + ctx->sign_certificate = NULL; + } + + picoquic_dispose_verify_certificate_callback(quic, 0); + + if (ctx->on_client_hello != NULL) { + free(ctx->on_client_hello); + } + + if (ctx->encrypt_ticket != NULL) { + free(ctx->encrypt_ticket); + } + + if (ctx->update_traffic_key != NULL) { + free(ctx->update_traffic_key); + } + + /* Need to be tested */ + if (ctx->save_ticket != NULL) { + free(ctx->save_ticket); + } + + if (ctx->cipher_suites != NULL) { + free((void*)ctx->cipher_suites); + } + + picoquic_free_log_event(quic); + } +} + +/* Return the virtual time seen by tls */ +uint64_t picoquic_get_tls_time(picoquic_quic_t* quic) +{ + ptls_context_t* ctx = (ptls_context_t*)quic->tls_master_ctx; + uint64_t now = ctx->get_time->cb(ctx->get_time); + + return now; +} + +/* + * Creation of a TLS context. + * This includes setting the handshake properties that will later be + * used during the TLS handshake. + */ +int picoquic_tlscontext_create(picoquic_quic_t* quic, picoquic_cnx_t* cnx, uint64_t current_time) +{ + int ret = 0; + /* allocate a context structure */ + picoquic_tls_ctx_t* ctx = (picoquic_tls_ctx_t*)malloc(sizeof(picoquic_tls_ctx_t)); + + /* Create the TLS context */ + if (ctx == NULL) { + ret = -1; + } else { + memset(ctx, 0, sizeof(picoquic_tls_ctx_t)); + + ctx->cnx = cnx; + + ctx->handshake_properties.collect_extension = picoquic_tls_collect_extensions_cb; + ctx->handshake_properties.collected_extensions = picoquic_tls_collected_extensions_cb; + ctx->client_mode = cnx->client_mode; + + ctx->tls = ptls_new((ptls_context_t*)quic->tls_master_ctx, + (ctx->client_mode) ? 0 : 1); + *ptls_get_data_ptr(ctx->tls) = cnx; + + if (ctx->tls == NULL) { + free(ctx); + ctx = NULL; + ret = -1; + } else if (!ctx->client_mode) { + /* A server side connection, but no cert/key where given for the master context */ + if (((ptls_context_t*)quic->tls_master_ctx)->encrypt_ticket == NULL) { + ret = PICOQUIC_ERROR_TLS_SERVER_CON_WITHOUT_CERT; + picoquic_tlscontext_free(ctx); + ctx = NULL; + } + + if (ctx != NULL) { + /* The server should never attempt a stateless retry */ + ctx->handshake_properties.server.enforce_retry = 0; + ctx->handshake_properties.server.retry_uses_cookie = 0; + ctx->handshake_properties.server.cookie.key = NULL; + ctx->handshake_properties.server.cookie.additional_data.base = NULL; + ctx->handshake_properties.server.cookie.additional_data.len = 0; + } + } + } + + if (cnx->tls_ctx != NULL) { + picoquic_tlscontext_free(cnx->tls_ctx); + } + + cnx->tls_ctx = (void*)ctx; + + return ret; +} + +/* Set the log event to record keys for use by Wireshark. + */ + +static void picoquic_log_event_call_back(ptls_log_event_t *_self, ptls_t *tls, const char *type, const char *fmt, ...) +{ + struct st_picoquic_log_event_t *self = (struct st_picoquic_log_event_t*)_self; + char randomhex[PTLS_HELLO_RANDOM_SIZE * 2 + 1]; + va_list args; + + if (self->fp != NULL) { + ptls_hexdump(randomhex, ptls_get_client_random(tls).base, PTLS_HELLO_RANDOM_SIZE); + fprintf(self->fp, "%s %s ", type, randomhex); + + va_start(args, fmt); + vfprintf(self->fp, fmt, args); + va_end(args); + + fprintf(self->fp, "\n"); + fflush(self->fp); + } +} + +/** + * Free the log-event call back, either when the TLS master context is freed, + * or when the key log file is reset. + */ +static void picoquic_free_log_event(picoquic_quic_t* quic) +{ + ptls_context_t* ctx = (ptls_context_t*)quic->tls_master_ctx; + + if (ctx->log_event != NULL) { + struct st_picoquic_log_event_t* picoquic_log_event = (struct st_picoquic_log_event_t*)ctx->log_event; + if (picoquic_log_event != NULL && picoquic_log_event->fp != NULL) { + picoquic_file_close(picoquic_log_event->fp); + } + free(ctx->log_event); + ctx->log_event = NULL; + } +} + + +/** + * Sets the output file handle for writing traffic secrets in a format that can + * be recognized by Wireshark. + */ +void picoquic_set_key_log_file(picoquic_quic_t *quic, char const * keylog_filename) +{ + ptls_context_t* ctx = (ptls_context_t*)quic->tls_master_ctx; + struct st_picoquic_log_event_t* log_event = (struct st_picoquic_log_event_t*)ctx->log_event; + + if (log_event == NULL) { + log_event = (struct st_picoquic_log_event_t*)malloc(sizeof(struct st_picoquic_log_event_t)); + if (log_event != NULL) { + log_event->super.cb = picoquic_log_event_call_back; + } + } + else { + if (log_event->fp != NULL) { + picoquic_file_close(log_event->fp); + log_event->fp = NULL; + } + } + + if (log_event != NULL) { + log_event->fp = picoquic_file_open(keylog_filename, "a"); + log_event->super.cb = picoquic_log_event_call_back; + ctx->log_event = (ptls_log_event_t*)log_event; + } + + ctx->log_event = (ptls_log_event_t*)log_event; +} + +/* +Check whether the ticket that was received, or used, authorizes 0-RTT data. + +From TLS 1.3 spec: +struct { +uint32 ticket_lifetime; +uint32 ticket_age_add; +opaque ticket_nonce<0..255>; +opaque ticket<1..2^16-1>; +Extension extensions<0..2^16-2>; +} NewSessionTicket; + +struct { +ExtensionType extension_type; +opaque extension_data<0..2^16-1>; +} Extension; +*/ + +int picoquic_does_tls_ticket_allow_early_data(uint8_t* ticket, uint16_t ticket_length) +{ + uint8_t nonce_length = 0; + uint16_t ticket_val_length = 0; + uint16_t extension_length = 0; + uint8_t* extension_ptr = NULL; + uint16_t byte_index = 0; + uint16_t min_length = 4 + 4 + 1 + 2 + 2; + int ret = 0; + + if (ticket_length >= min_length) { + byte_index += 4; /* Skip lifetime */ + byte_index += 4; /* Skip age add */ + nonce_length = ticket[byte_index++]; + min_length += nonce_length; + if (ticket_length >= min_length) { + byte_index += nonce_length; + + ticket_val_length = PICOPARSE_16(ticket + byte_index); + byte_index += 2; + min_length += ticket_val_length; + if (ticket_length >= min_length) { + byte_index += ticket_val_length; + + extension_length = PICOPARSE_16(ticket + byte_index); + byte_index += 2; + min_length += extension_length; + if (ticket_length >= min_length) { + extension_ptr = &ticket[byte_index]; + } + } + } + } + + if (extension_ptr != NULL) { + uint16_t x_index = 0; + + while (x_index + 4 < extension_length) { + uint16_t x_type = PICOPARSE_16(extension_ptr + x_index); + uint16_t x_len = PICOPARSE_16(extension_ptr + x_index + 2); + x_index += 4 + x_len; + + if (x_type == 42 && x_len == 4) { + uint32_t ed_len = PICOPARSE_32(extension_ptr + x_index - 4); + if (ed_len == 0xFFFFFFFF) { + ret = 1; + } + break; + } + } + } + + return ret; +} + +/* +* Creation of a TLS context. +* This includes setting the handshake properties that will later be +* used during the TLS handshake. +*/ +void picoquic_tlscontext_remove_ticket(picoquic_cnx_t* cnx) +{ + /* allocate a context structure */ + picoquic_tls_ctx_t* ctx = (picoquic_tls_ctx_t*)(cnx->tls_ctx); + + ctx->handshake_properties.client.session_ticket.base = NULL; + ctx->handshake_properties.client.session_ticket.len = 0; +} + +void picoquic_tlscontext_free(void* vctx) +{ + picoquic_tls_ctx_t* ctx = (picoquic_tls_ctx_t*)vctx; + if (ctx->tls != NULL) { + ptls_free((ptls_t*)ctx->tls); + ctx->tls = NULL; + } + free(ctx); +} + +char const* picoquic_tls_get_negotiated_alpn(picoquic_cnx_t* cnx) +{ + picoquic_tls_ctx_t* ctx = (picoquic_tls_ctx_t*)cnx->tls_ctx; + + return ptls_get_negotiated_protocol(ctx->tls); +} + +char const* picoquic_tls_get_sni(picoquic_cnx_t* cnx) +{ + picoquic_tls_ctx_t* ctx = (picoquic_tls_ctx_t*)cnx->tls_ctx; + + return ptls_get_server_name(ctx->tls); +} + +int picoquic_tls_is_psk_handshake(picoquic_cnx_t* cnx) +{ + /* int ret = cnx->is_psk_handshake; */ + int ret = ptls_is_psk_handshake(((picoquic_tls_ctx_t*)(cnx->tls_ctx))->tls); + return ret; +} + + +/* +* Sending data on the crypto stream. +*/ + +static int picoquic_add_to_tls_stream(picoquic_cnx_t* cnx, const uint8_t* data, size_t length, int epoch) +{ + int ret = 0; + picoquic_stream_head_t* stream = &cnx->tls_stream[epoch]; + + if (length > 0) { + picoquic_stream_data_node_t* stream_data = (picoquic_stream_data_node_t*)malloc(sizeof(picoquic_stream_data_node_t)); + + if (stream_data == 0) { + ret = -1; + } + else { + stream_data->bytes = (uint8_t*)malloc(length); + + if (stream_data->bytes == NULL) { + free(stream_data); + stream_data = NULL; + ret = -1; + } + else { + picoquic_stream_data_node_t** pprevious = &stream->send_queue; + picoquic_stream_data_node_t* next = stream->send_queue; + + memcpy(stream_data->bytes, data, length); + stream_data->length = length; + stream_data->offset = 0; + stream_data->next_stream_data = NULL; + + while (next != NULL) { + pprevious = &next->next_stream_data; + next = next->next_stream_data; + } + + *pprevious = stream_data; + } + } + } + + return ret; +} + +/* Add a supported ALPN context */ +int picoquic_add_proposed_alpn(void* tls_context, const char* alpn) +{ + int ret = 0; + picoquic_tls_ctx_t* ctx = (picoquic_tls_ctx_t*)tls_context; + if (ctx == NULL) { + ret = PICOQUIC_ERROR_UNEXPECTED_ERROR; + } + else if (ctx->alpn_count >= PICOQUIC_ALPN_NUMBER_MAX) { + ret = PICOQUIC_ERROR_SEND_BUFFER_TOO_SMALL; + } else { + ctx->alpn_vec[ctx->alpn_count].base = (uint8_t*)alpn; + ctx->alpn_vec[ctx->alpn_count].len = strlen(alpn); + ctx->alpn_count++; + } + + return ret; +} + +/* Prepare the initial message when starting a connection. + */ + +int picoquic_initialize_tls_stream(picoquic_cnx_t* cnx, uint64_t current_time) +{ + int ret = 0; + struct st_ptls_buffer_t sendbuf; + picoquic_tls_ctx_t* ctx = (picoquic_tls_ctx_t*)cnx->tls_ctx; + size_t epoch_offsets[PICOQUIC_NUMBER_OF_EPOCH_OFFSETS] = { 0, 0, 0, 0, 0 }; + + if (cnx->sni != NULL) { + ptls_set_server_name(ctx->tls, cnx->sni, strlen(cnx->sni)); + } + + if (cnx->alpn != NULL) { + ctx->alpn_vec[0].base = (uint8_t*)cnx->alpn; + ctx->alpn_vec[0].len = strlen(cnx->alpn); + ctx->handshake_properties.client.negotiated_protocols.count = 1; + ctx->handshake_properties.client.negotiated_protocols.list = ctx->alpn_vec; + } + else if (cnx->callback_fn != NULL) { + /* Get the default ALPN list for the callback function */ + ret = cnx->callback_fn(cnx, 0, (uint8_t*)ctx, 0, picoquic_callback_request_alpn_list, cnx->callback_ctx, NULL); + + ctx->handshake_properties.client.negotiated_protocols.count = ctx->alpn_count; + ctx->handshake_properties.client.negotiated_protocols.list = ctx->alpn_vec; + + if (ret != 0) { + DBG_PRINTF("ALPN list callback returns 0x%x", ret); + } + } + + /* ALPN is mandatory, there should be at least one */ + if (ret == 0 && ctx->handshake_properties.client.negotiated_protocols.count == 0) { + ret = PICOQUIC_ERROR_NO_ALPN_PROVIDED; + DBG_PRINTF("No ALPN provided, error 0x%x", ret); + } + + picoquic_log_negotiated_alpn(cnx, + 1, (const uint8_t *)cnx->sni, (cnx->sni == NULL)?0:strlen(cnx->sni), NULL, 0, + ctx->handshake_properties.client.negotiated_protocols.list, + ctx->handshake_properties.client.negotiated_protocols.count); + + /* No resumption if no alpn specified upfront, because it would make the negotiation and + * the handling of 0-RTT way too messy */ + if (cnx->sni != NULL && cnx->alpn != NULL && !cnx->quic->client_zero_share) { + uint8_t* ticket = NULL; + uint16_t ticket_length = 0; + + if (picoquic_get_ticket(cnx->quic->p_first_ticket, current_time, + cnx->sni, (uint16_t)strlen(cnx->sni), cnx->alpn, (uint16_t)strlen(cnx->alpn), + &ticket, &ticket_length, &cnx->remote_parameters, 1) + == 0) { + ctx->handshake_properties.client.session_ticket.base = ticket; + ctx->handshake_properties.client.session_ticket.len = ticket_length; + + ctx->handshake_properties.client.max_early_data_size = &cnx->max_early_data_size; + + cnx->psk_cipher_suite_id = PICOPARSE_16(ticket + 8); + } + } + + if (cnx->quic->client_zero_share && + cnx->cnx_state == picoquic_state_client_init) + { + ctx->handshake_properties.client.negotiate_before_key_exchange = 1; + } + else + { + ctx->handshake_properties.client.negotiate_before_key_exchange = 0; + } + + if (ret != 0) { + DBG_PRINTF("Could not set up TLS parameters, error 0x%x, abandoning connection", ret); + cnx->cnx_state = picoquic_state_disconnected; + } else { + picoquic_tls_set_extensions(cnx, ctx); + + ptls_buffer_init(&sendbuf, "", 0); + + /* Clearing the global error state of the crypto provider before calling handle message. + * This allows detection of errors during processing. */ + picoquic_clear_crypto_errors(); + ret = ptls_handle_message(ctx->tls, &sendbuf, epoch_offsets, 0, NULL, 0, &ctx->handshake_properties); + + /* assume that all the data goes to epoch 0, initial */ + if ((ret == 0 || ret == PTLS_ERROR_IN_PROGRESS)) { +#ifdef PTLS_ESNI_NONCE_SIZE + /* Find the ESNI secret if any, and copy key values to picoquic tls context */ + struct st_ptls_esni_secret_t* esni = ptls_get_esni_secret(ctx->tls); + if (esni != NULL) { + ctx->esni_version = esni->version; + memcpy(ctx->esni_nonce, esni->nonce, PTLS_ESNI_NONCE_SIZE); + } +#endif + if (sendbuf.off > 0) { + ret = picoquic_add_to_tls_stream(cnx, sendbuf.base, sendbuf.off, 0); + } + else { + ret = 0; + } + } + else { + picoquic_log_crypto_errors(cnx, ret); + ret = -1; + } + ptls_buffer_dispose(&sendbuf); + } + + return ret; +} + +/* + * Packet number encryption and decryption utilities + */ + +void * picoquic_pn_enc_create_for_test(const uint8_t * secret) +{ + ptls_cipher_suite_t *cipher = picoquic_get_aes128gcm_sha256(); + void *v_pn_enc = NULL; + + (void)picoquic_set_pn_enc_from_secret(&v_pn_enc, cipher, 1, secret); + + return v_pn_enc; +} + +size_t picoquic_pn_iv_size(void *pn_enc) +{ + return ((ptls_cipher_context_t *)pn_enc)->algo->iv_size; +} + +void picoquic_pn_encrypt(void *pn_enc, const void * iv, void *output, const void *input, size_t len) +{ + ptls_cipher_init((ptls_cipher_context_t *) pn_enc, iv); + ptls_cipher_encrypt((ptls_cipher_context_t *) pn_enc, output, input, len); +} + +/* Utility functions, so applications do not have to load picotls.h */ + +void picoquic_aead_free(void* aead_context) +{ + ptls_aead_free((ptls_aead_context_t*)aead_context); +} + +size_t picoquic_aead_get_checksum_length(void* aead_context) +{ + size_t tag_size = ((ptls_aead_context_t*)aead_context)->algo->tag_size; + /* TODO: remove this temporary fix to deal with Feb 2019 change in picotls */ + if (tag_size > 16) { + tag_size = 16; + } + return tag_size; +} + +/* Setting of encryption contexts for test */ +void * picoquic_setup_test_aead_context(int is_encrypt, const uint8_t * secret) +{ + void * v_aead = NULL; + ptls_cipher_suite_t* cipher = picoquic_get_aes128gcm_sha256(); + + (void)picoquic_set_aead_from_secret(&v_aead, cipher, is_encrypt, secret); + + return v_aead; +} + +int picoquic_server_setup_ticket_aead_contexts(picoquic_quic_t* quic, + ptls_context_t* tls_ctx, + const uint8_t* secret, size_t secret_length) +{ + int ret = 0; + uint8_t temp_secret[256]; /* secret_max */ + ptls_cipher_suite_t *cipher = picoquic_get_aes128gcm_sha256(); + + if (cipher->hash->digest_size > sizeof(temp_secret)) { + ret = PICOQUIC_ERROR_UNEXPECTED_ERROR; + } else { + if (secret != NULL && secret_length > 0) { + memset(temp_secret, 0, cipher->hash->digest_size); + memcpy(temp_secret, secret, (secret_length > cipher->hash->digest_size) ? cipher->hash->digest_size : secret_length); + } else { + tls_ctx->random_bytes(temp_secret, cipher->hash->digest_size); + } + + /* Create the AEAD contexts */ + ret = picoquic_set_aead_from_secret(&quic->aead_encrypt_ticket_ctx, cipher, 1, temp_secret); + if (ret == 0) { + ret = picoquic_set_aead_from_secret(&quic->aead_decrypt_ticket_ctx, cipher, 0, temp_secret); + } + + /* erase the temporary secret */ + ptls_clear_memory(temp_secret, cipher->hash->digest_size); + } + return ret; +} + +/* Access integrity limit for AEAD */ +uint64_t picoquic_aead_integrity_limit(void* aead_ctx) +{ + return ((ptls_aead_context_t*)aead_ctx)->algo->integrity_limit; +} + +/* Access confidentiality limit for AEAD */ +uint64_t picoquic_aead_confidentiality_limit(void* aead_ctx) +{ + return ((ptls_aead_context_t*)aead_ctx)->algo->confidentiality_limit; +} + +/* AEAD encrypt/decrypt routines */ +size_t picoquic_aead_decrypt_generic(uint8_t* output, const uint8_t* input, size_t input_length, + uint64_t seq_num, const uint8_t* auth_data, size_t auth_data_length, void* aead_ctx) +{ + size_t decrypted = 0; + + if (aead_ctx == NULL) { + decrypted = SIZE_MAX; + } else { + decrypted = ptls_aead_decrypt((ptls_aead_context_t*)aead_ctx, + (void*)output, (const void*)input, input_length, seq_num, + (void*)auth_data, auth_data_length); + } + + return decrypted; +} + +size_t picoquic_aead_encrypt_generic(uint8_t* output, const uint8_t* input, size_t input_length, + uint64_t seq_num, const uint8_t* auth_data, size_t auth_data_length, void* aead_context) +{ + size_t encrypted = 0; + + encrypted = ptls_aead_encrypt((ptls_aead_context_t*)aead_context, + (void*)output, (const void*)input, input_length, seq_num, + (void*)auth_data, auth_data_length); + + return encrypted; +} + +/* management of version specific salt, for initial packet encryption. + */ + +uint8_t picoquic_cleartext_null_salt[] = { + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0 +}; + +static void picoquic_setup_cleartext_aead_salt(size_t version_index, ptls_iovec_t* salt) +{ + if (picoquic_supported_versions[version_index].version_aead_key != NULL && picoquic_supported_versions[version_index].version_aead_key_length > 0) { + salt->base = picoquic_supported_versions[version_index].version_aead_key; + salt->len = picoquic_supported_versions[version_index].version_aead_key_length; + } else { + salt->base = picoquic_cleartext_null_salt; + salt->len = sizeof(picoquic_cleartext_null_salt); + } +} + +#if 0 +/* TODO: find a replacement for this test. */ +/* Compare AEAD context parameters. This is done just by comparing the IV, + * which is accessible in the context */ +int picoquic_compare_cleartext_aead_contexts(picoquic_cnx_t* cnx1, picoquic_cnx_t* cnx2) +{ + int ret = 0; + ptls_aead_context_t * aead_enc = (ptls_aead_context_t *)cnx1->crypto_context[0].aead_encrypt; + ptls_aead_context_t * aead_dec = (ptls_aead_context_t *)cnx2->crypto_context[0].aead_decrypt; + + if (aead_enc == NULL ) + { + DBG_PRINTF("%s", "Missing aead encoding context\n"); + ret = -1; + } + else if (aead_dec == NULL) + { + DBG_PRINTF("%s", "Missing aead decoding context\n"); + ret = -1; + } + else if (memcmp(aead_enc->static_iv, aead_dec->static_iv, 16) != 0){ + DBG_PRINTF("%s", "Encoding IV does not match decoding IV\n"); + ret = -1; + } + + return ret; +} +#endif + +/* Input stream zero data to TLS context. + * + * Processing depends on the "epoch" in which packets have been received. That + * epoch is be passed through the ptls_handle_message() API. + * The API has an "epoch offset" parameter that documents how many bytes of the + * should be sent at each epoch. + */ + +int picoquic_tls_stream_process(picoquic_cnx_t* cnx, int * data_consumed) +{ + int ret = 0; + picoquic_tls_ctx_t* ctx = (picoquic_tls_ctx_t*)cnx->tls_ctx; + size_t next_epoch = 0; + + /* Provide indication of current connection for later callbacks */ + cnx->quic->cnx_in_progress = cnx; + + for (size_t epoch = 0; epoch < PICOQUIC_NUMBER_OF_EPOCHS && ret == 0; epoch++) { + picoquic_stream_head_t* stream = &cnx->tls_stream[epoch]; + picoquic_stream_data_node_t* data = (picoquic_stream_data_node_t*)picosplay_first(&stream->stream_data_tree); + size_t processed = 0; + int data_pushed = 0; + + next_epoch = ptls_get_read_epoch(ctx->tls); + + if (epoch != next_epoch) { + if (epoch > next_epoch) { + break; + } else { + if (data != NULL && data->offset > stream->consumed_offset) { + /* Protocol error: data received that could not be read */ +#ifdef _DEBUG + DBG_PRINTF("Connection error - TLS data at epoch %d, expected %d.\n", + epoch, next_epoch); +#endif + ret = picoquic_connection_error(cnx, + PICOQUIC_TRANSPORT_PROTOCOL_VIOLATION, 0); + } + continue; + } + } + + while ((ret == 0 || ret == PTLS_ERROR_IN_PROGRESS) && + data != NULL && data->offset <= stream->consumed_offset) { + struct st_ptls_buffer_t sendbuf; + size_t start = (size_t)(stream->consumed_offset - data->offset); + size_t epoch_data = data->length - start; + size_t send_offset[PICOQUIC_NUMBER_OF_EPOCH_OFFSETS] = { 0, 0, 0, 0, 0 }; + + if (data_consumed != NULL) { + *data_consumed = 1; + } + + ptls_buffer_init(&sendbuf, "", 0); + + /* Clearing the global error state of the crypto provider before calling handle message. + * This allows detection of errors during processing. */ + picoquic_clear_crypto_errors(); + + ret = ptls_handle_message(ctx->tls, &sendbuf, send_offset, epoch, + data->bytes + start, epoch_data, &ctx->handshake_properties); + + if ((ret == 0 || ret == PTLS_ERROR_IN_PROGRESS || + ret == PTLS_ERROR_STATELESS_RETRY)) { + for (int i = 0; i < PICOQUIC_NUMBER_OF_EPOCHS; i++) { + if (send_offset[i] < send_offset[i + 1]) { + data_pushed = 1; + ret = picoquic_add_to_tls_stream(cnx, + sendbuf.base + send_offset[i], send_offset[i + 1] - send_offset[i], i); + } + } + if (cnx->client_mode) { + if (cnx->alpn == NULL) { + const char* alpn = ptls_get_negotiated_protocol(ctx->tls); + + if (alpn != NULL){ + cnx->alpn = picoquic_string_duplicate(alpn); + + picoquic_log_negotiated_alpn(cnx, 0, NULL, 0, (const uint8_t*)alpn, strlen(alpn), NULL, 0); + + if (cnx->callback_fn != NULL) { + cnx->callback_fn(cnx, 0, (uint8_t*)alpn, 0, picoquic_callback_set_alpn, cnx->callback_ctx, NULL); + } + else { + DBG_PRINTF("Negotiated ALPN: %s", alpn); + } + } + } + switch (ctx->handshake_properties.client.early_data_acceptance) { + case PTLS_EARLY_DATA_REJECTED: + cnx->zero_rtt_data_accepted = 0; + break; + case PTLS_EARLY_DATA_ACCEPTED: + cnx->zero_rtt_data_accepted = 1; + break; + default: + break; + } + } + } + else { + picoquic_log_crypto_errors(cnx, ret); + } + + stream->consumed_offset += epoch_data; + processed += epoch_data; + + if (start + epoch_data >= data->length) { + picosplay_delete_hint(&cnx->tls_stream[epoch].stream_data_tree, &data->stream_data_node); + data = (picoquic_stream_data_node_t*)picosplay_first(&cnx->tls_stream[epoch].stream_data_tree); + } + + ptls_buffer_dispose(&sendbuf); + } + + if (processed > 0) { + if (ret == 0) { + switch (cnx->cnx_state) { + case picoquic_state_client_retry_received: + /* This is not supposed to happen -- HRR should generate "error in progress" */ + break; + case picoquic_state_client_init: + case picoquic_state_client_init_sent: + case picoquic_state_client_renegotiate: + case picoquic_state_client_init_resent: + case picoquic_state_client_handshake_start: + if (ptls_handshake_is_complete(ctx->tls)) { + if (cnx->remote_parameters_received == 0) { + +#ifdef _DEBUG + DBG_PRINTF("%s", "Connection error - no transport parameter received.\n"); +#endif + ret = picoquic_connection_error(cnx, + PICOQUIC_TRANSPORT_PARAMETER_ERROR, 0); + } + else { + if (cnx->crypto_context[3].aead_encrypt != NULL) { + cnx->cnx_state = picoquic_state_client_almost_ready; + } + } + } + break; + case picoquic_state_server_init: + case picoquic_state_server_handshake: + /* If client authentication is activated, the client sends the certificates with its `Finished` packet. + The server does not send any further packets, so, we can switch into false start state here. + */ + if (data_pushed == 0 && ((ptls_context_t*)cnx->quic->tls_master_ctx)->require_client_authentication == 1) { + cnx->cnx_state = picoquic_state_server_false_start; + + /* On a server that does address validation, send a NEW TOKEN frame */ + if (!cnx->client_mode && (cnx->quic->check_token||cnx->quic->provide_token)) { + uint8_t token_buffer[256]; + size_t token_size; + picoquic_connection_id_t n_cid = picoquic_null_connection_id; + + if (picoquic_prepare_retry_token(cnx->quic, (struct sockaddr *)&cnx->path[0]->peer_addr, + picoquic_get_quic_time(cnx->quic) + PICOQUIC_TOKEN_DELAY_LONG, &n_cid, &n_cid, 0, + token_buffer, sizeof(token_buffer), &token_size) == 0) { + if (picoquic_queue_new_token_frame(cnx, token_buffer, token_size) != 0) { + picoquic_connection_error(cnx, PICOQUIC_TRANSPORT_INTERNAL_ERROR, picoquic_frame_type_new_token); + } + } + } + } + else { + if (cnx->crypto_context[3].aead_encrypt != NULL) { + cnx->cnx_state = picoquic_state_server_almost_ready; + } + } + break; + case picoquic_state_client_almost_ready: + case picoquic_state_handshake_failure: + case picoquic_state_handshake_failure_resend: + case picoquic_state_client_ready_start: + case picoquic_state_server_almost_ready: + case picoquic_state_server_false_start: + case picoquic_state_ready: + case picoquic_state_disconnecting: + case picoquic_state_closing_received: + case picoquic_state_closing: + case picoquic_state_draining: + case picoquic_state_disconnected: + break; + default: + DBG_PRINTF("Unexpected connection state: %d\n", cnx->cnx_state); + break; + } + } + else if (ret == PTLS_ERROR_IN_PROGRESS && (cnx->cnx_state == picoquic_state_client_init || cnx->cnx_state == picoquic_state_client_init_sent || cnx->cnx_state == picoquic_state_client_init_resent)) { + /* Extract and install the client 0-RTT key */ +#ifdef _DEBUG + DBG_PRINTF("%s", "Handshake not yet complete.\n"); +#endif + } + else if (ret == PTLS_ERROR_IN_PROGRESS && + (cnx->cnx_state == picoquic_state_server_init || + cnx->cnx_state == picoquic_state_server_handshake)) + { + if (ptls_handshake_is_complete(ctx->tls)) + { + cnx->cnx_state = picoquic_state_server_almost_ready; + } + } + + if ((ret == 0 || ret == PTLS_ERROR_IN_PROGRESS || ret == PTLS_ERROR_STATELESS_RETRY)) { + ret = 0; + } + else { + uint16_t error_code = PICOQUIC_TRANSPORT_INTERNAL_ERROR; + + if (PTLS_ERROR_GET_CLASS(ret) == PTLS_ERROR_CLASS_SELF_ALERT) { + error_code = PICOQUIC_TRANSPORT_CRYPTO_ERROR(ret); + } +#ifdef _DEBUG + DBG_PRINTF("Handshake failed, ret = 0x%x.\n", ret); +#endif + (void)picoquic_connection_error(cnx, error_code, 0); + ret = 0; + } + } + } + + + /* Reset indication of current connection */ + cnx->quic->cnx_in_progress = NULL; + + return ret; +} + +/* + * Test whether the TLS handshake is complete according to TLS stack + */ +int picoquic_is_tls_complete(picoquic_cnx_t* cnx) +{ + picoquic_tls_ctx_t* ctx = (picoquic_tls_ctx_t*)cnx->tls_ctx; + return ptls_handshake_is_complete(ctx->tls); +} + +/* + * Compute the 16 byte reset secret associated with a connection ID. + * We implement it as the hash of a secret seed maintained per QUIC context + * and the 8 bytes connection ID. + * This is written portable hash APIs. + */ + +int picoquic_create_cnxid_reset_secret(picoquic_quic_t* quic, picoquic_connection_id_t * cnx_id, + uint8_t reset_secret[PICOQUIC_RESET_SECRET_SIZE]) +{ + int ret = 0; + ptls_hash_algorithm_t* algo = picoquic_get_sha256(); + + if (algo == NULL) { + ret = -1; + } + else { + ptls_hash_context_t* hash_ctx = algo->create(); + uint8_t final_hash[PTLS_MAX_DIGEST_SIZE]; + + if (hash_ctx == NULL) { + ret = -1; + memset(reset_secret, 0, PICOQUIC_RESET_SECRET_SIZE); + } + else { + hash_ctx->update(hash_ctx, quic->reset_seed, sizeof(quic->reset_seed)); + hash_ctx->update(hash_ctx, cnx_id, sizeof(picoquic_connection_id_t)); + hash_ctx->final(hash_ctx, final_hash, PTLS_HASH_FINAL_MODE_FREE); + memcpy(reset_secret, final_hash, PICOQUIC_RESET_SECRET_SIZE); + } + } + + return (ret); +} + +void picoquic_set_tls_certificate_chain(picoquic_quic_t* quic, ptls_iovec_t* certs, size_t count) +{ + ptls_context_t* ctx = (ptls_context_t*)quic->tls_master_ctx; + + free_certificates_list(ctx->certificates.list, ctx->certificates.count); + + ctx->certificates.list = certs; + ctx->certificates.count = count; +} + +void picoquic_tls_set_client_authentication(picoquic_quic_t* quic, int client_authentication) { + ((ptls_context_t*)quic->tls_master_ctx)->require_client_authentication = client_authentication; +} + +int picoquic_tls_client_authentication_activated(picoquic_quic_t* quic) { + return ((ptls_context_t*)quic->tls_master_ctx)->require_client_authentication; +} + +/* + * Create or verify a token. Tokens are tied to an IP address and a time of + * issue, and come in two variations: + * - specific tokens are tied to an Original DCID. + * - generic tokens work with a zero length DCID. + * The structure of the token is: + * - time valid until: uint64_t + * - ODCID length, one byte + * - ODCID, length bytes + * This is encrypted using the same AEAD contexts as the encryption of session tickets. + * The encrypted structure is: + * - 64 bit random sequence number. + * - Encrypted value of the token. + * - AEAD checksum. + * The most significant bit of the random number is set to 1 (0x80) for a "new token", + * and to zero for a "retry token". + * When invoking AEAD, the sequence number is used to update the IV, and the IP address + * is passed as "authenticated" data. The 64 bit random number alleviates the concern of + * reusing the same AEAD key twice. The authenticated data ensures that if the token is + * used from a different address, the decryption will fail. + */ + +static int picoquic_server_encrypt_retry_token(picoquic_quic_t * quic, const struct sockaddr * addr_peer, + int is_new_token, + uint8_t * token, size_t * token_length, size_t token_max, const uint8_t * text, size_t text_length) +{ + int ret = 0; + uint64_t sequence; + uint8_t* auth_data; + size_t auth_data_length; + + if (text_length + 1u + 16u > token_max) { + ret = -1; + *token_length = 0; + } + else { + + if (addr_peer->sa_family == AF_INET) { + auth_data = (uint8_t*)&((struct sockaddr_in*)addr_peer)->sin_addr; + auth_data_length = 4; + } + else { + auth_data = (uint8_t*)&((struct sockaddr_in6*)addr_peer)->sin6_addr; + auth_data_length = 16; + } + picoquic_crypto_random(quic, token, 8); + if (is_new_token) { + token[0] |= 0x80; + } + else { + token[0] &= 0x7F; + } + sequence = PICOPARSE_64(token); + + *token_length = (size_t)8u + picoquic_aead_encrypt_generic(token + 8, text, text_length, + sequence, auth_data, auth_data_length, quic->aead_encrypt_ticket_ctx); + } + + return ret; +} + +int picoquic_server_decrypt_retry_token(picoquic_quic_t* quic, const struct sockaddr * addr_peer, + int * is_new_token, const uint8_t * token, size_t token_length, uint8_t * text, size_t *text_length) +{ + int ret = 0; + uint64_t sequence; + uint8_t* auth_data; + size_t auth_data_length; + + if (addr_peer->sa_family == AF_INET) { + auth_data = (uint8_t*)&((struct sockaddr_in *)addr_peer)->sin_addr; + auth_data_length = 4; + } + else { + auth_data = (uint8_t*)&((struct sockaddr_in6 *)addr_peer)->sin6_addr; + auth_data_length = 16; + } + + if (token_length < 8) { + *is_new_token = 0; + ret = -1; + } + else { + *is_new_token = ((token[0] & 0x80) == 0) ? 0: 1; + sequence = PICOPARSE_64(token); + + *text_length = picoquic_aead_decrypt_generic(text, token+8, token_length-8, + sequence, auth_data, auth_data_length, quic->aead_decrypt_ticket_ctx); + if (*text_length >= token_length - 8) { + ret = -1; + } + } + + return ret; +} + +int picoquic_prepare_retry_token(picoquic_quic_t* quic, const struct sockaddr* addr_peer, + uint64_t current_time, const picoquic_connection_id_t* odcid, const picoquic_connection_id_t* rcid, + uint32_t initial_pn, + uint8_t* token, size_t token_max, size_t* token_size) +{ + int ret = 0; + uint8_t text[128]; + uint64_t token_time = current_time; + uint8_t* bytes = text; + uint8_t* bytes_max = text + sizeof(text); + + /* set a short life time for short lived tokens, 24 hours otherwise */ + if (odcid->id_len == 0) { + token_time += 24ull * 3600ull * 1000000ull; + } + else { + token_time += 4000000ull; + } + /* serialize the token components */ + if ((bytes = picoquic_frames_uint64_encode(bytes, bytes_max, token_time)) != NULL && + (bytes = picoquic_frames_cid_encode(bytes, bytes_max, odcid)) != NULL && + (bytes = picoquic_frames_cid_encode(bytes, bytes_max, rcid)) != NULL && + (bytes = picoquic_frames_varint_encode(bytes, bytes_max, initial_pn)) != NULL) { + /* Pad to min token size */ + while (bytes < text + PICOQUIC_RETRY_TOKEN_PAD_SIZE) { + *bytes++ = 0; + } + /* Encode the clear text components */ + ret = picoquic_server_encrypt_retry_token(quic, addr_peer, odcid->id_len == 0, + token, token_size, token_max, text, bytes - text); + } + else { + ret = -1; + } + + return ret; +} + +int picoquic_verify_retry_token(picoquic_quic_t* quic, const struct sockaddr * addr_peer, + uint64_t current_time, int * is_new_token, picoquic_connection_id_t * odcid, const picoquic_connection_id_t* rcid, + uint32_t initial_pn, + const uint8_t * token, size_t token_size, int new_context_created) +{ + int ret = 0; + uint8_t text[128]; + size_t text_len = 0; + picoquic_connection_id_t cid; + uint64_t token_pn; + + odcid->id_len = 0; + + /* decode the encrypted token */ + ret = picoquic_server_decrypt_retry_token(quic, addr_peer, is_new_token, token, token_size, + text, &text_len); + + if (ret == 0) { + /* Decode the clear text components */ + const uint8_t* bytes = text; + const uint8_t* bytes_max = text + text_len; + uint64_t token_time = PICOPARSE_64(text); + + if ((bytes = picoquic_frames_uint64_decode(bytes, bytes_max, &token_time)) != NULL && + (bytes = picoquic_frames_cid_decode(bytes, bytes_max, odcid)) != NULL && + (bytes = picoquic_frames_cid_decode(bytes, bytes_max, &cid)) != NULL && + (bytes = picoquic_frames_varint_decode(bytes, bytes_max, &token_pn)) != NULL) { + if (token_time < current_time) { + /* Invalid token, too old */ + ret = -1; + } + else if (odcid->id_len > 0 && token_pn >= initial_pn) { + /* Invalid PN number */ + ret = -1; + } + else { + /* Remove old tickets before testing this one. */ + picoquic_registered_token_clear(quic, current_time); + if (new_context_created && (ret = picoquic_registered_token_check_reuse(quic, token, token_size, token_time)) != 0) { + picoquic_log_context_free_app_message(quic, rcid, "Duplicate token test returns %d", ret); + } + else if (odcid->id_len > 0 && + picoquic_compare_connection_id(rcid, &cid) != 0) { + /* Invalid token, bad rcid */ + ret = -1; + } + } + } + else { + *odcid = picoquic_null_connection_id; + } + } + + return ret; +} + +/* + * Encryption functions for CID encryption + */ + +void picoquic_cid_free_under_mask_ctx(void * v_cid_enc) +{ + if (v_cid_enc != NULL) { + ptls_cipher_free((ptls_cipher_context_t *)v_cid_enc); + } +} + +int picoquic_cid_get_under_mask_ctx(void ** v_cid_enc, const void *secret) +{ + uint8_t cidkey[PTLS_MAX_SECRET_SIZE]; + uint8_t long_secret[PTLS_MAX_DIGEST_SIZE]; + ptls_cipher_suite_t * cipher = picoquic_get_aes128gcm_sha256(); + int ret; + + picoquic_cid_free_under_mask_ctx(*v_cid_enc); + *v_cid_enc = NULL; + /* Secret is only guaranteed to be 16 bytes long. Avoid excess length issues */ + memset(long_secret, 0, sizeof(long_secret)); + memcpy(long_secret, secret, 16); + + if ((ret = ptls_hkdf_expand_label(cipher->hash, cidkey, + cipher->aead->ctr_cipher->key_size, ptls_iovec_init(long_secret, cipher->hash->digest_size), + PICOQUIC_LABEL_CID, ptls_iovec_init(NULL, 0), PICOQUIC_LABEL_QUIC_KEY_BASE)) == 0) { +#ifdef _DEBUG + DBG_PRINTF("CID Encryption key (%d):\n", (int)cipher->aead->ctr_cipher->key_size); + debug_dump(cidkey, (int)cipher->aead->ctr_cipher->key_size); +#endif + if ((*v_cid_enc = ptls_cipher_new(cipher->aead->ctr_cipher, 1, cidkey)) == NULL) { + ret = PTLS_ERROR_NO_MEMORY; + } + } + + return ret; +} + +void picoquic_cid_encrypt_under_mask(void *cid_enc, const picoquic_connection_id_t * cid_in, const picoquic_connection_id_t * mask, + picoquic_connection_id_t * cid_out) +{ + uint8_t unmasked[18]; + uint8_t val[18]; + + memset(unmasked, 0, 18); + memset(val, 0, 18); + + for (uint8_t i = 0; i < cid_in->id_len; i++) { + /* retain only the random bits */ + unmasked[i] = cid_in->id[i] & mask->id[i]; + } + + ptls_cipher_init((ptls_cipher_context_t *)cid_enc, unmasked); + ptls_cipher_encrypt((ptls_cipher_context_t *)cid_enc, val, val, cid_in->id_len); + + for (uint8_t i = 0; i < cid_in->id_len; i++) { + /* randomize the unmasked bits */ + cid_out->id[i] = cid_in->id[i]^(val[i] & ~mask->id[i]); + } + cid_out->id_len = cid_in->id_len; + if (cid_out->id_len < 18) { + memset(cid_out->id + cid_out->id_len, 0, 18 - cid_out->id_len); + } +} + +void picoquic_cid_decrypt_under_mask(void *cid_enc, const picoquic_connection_id_t * cid_in, const picoquic_connection_id_t * mask, + picoquic_connection_id_t * cid_out) +{ + picoquic_cid_encrypt_under_mask(cid_enc, cid_in, mask, cid_out); +} +#if 0 +void picoquic_cid_free_encrypt_global_ctx(void ** v_cid_enc) +{ + if (v_cid_enc != NULL) { + ptls_cipher_free((ptls_cipher_context_t *)v_cid_enc); + } +} +#endif + +/* Support for encrypted SNI (ESNI). + */ + +/* Load the ESNI exchange keys. This function must be called at least once + * before setting up ESNI for the server. + * esni_key_elements should be declared as an array of ptls_key_exchange_context_t* + * of size less than *esni_key_count. + * The last element in the array should be a NULL pointer. + */ + +int picoquic_esni_load_key(picoquic_quic_t * quic, char const * esni_key_file_name) +{ + int ret = 0; + size_t esni_key_exchange_count = 0; + + while (esni_key_exchange_count < 15 && + quic->esni_key_exchange[esni_key_exchange_count] != 0) + esni_key_exchange_count++; + + if (quic->esni_key_exchange[esni_key_exchange_count] != 0) { + DBG_PRINTF("Too many ESNI private key file, %zu already\n", esni_key_exchange_count); + ret = PICOQUIC_ERROR_UNEXPECTED_ERROR; + } else { + ret = picoquic_exchange_context_from_file(esni_key_file_name, &quic->esni_key_exchange[esni_key_exchange_count]); + } + + return ret; +} + +int picoquic_esni_load_rr(char const * esni_rr_file_name, uint8_t *esnikeys, size_t esnikeys_max, size_t *esnikeys_len) +{ + FILE * fp = NULL; + int ret = 0; + + *esnikeys_len = 0; + + if ((fp = picoquic_file_open(esni_rr_file_name, "rb")) == NULL) { + fprintf(stderr, "failed to open file:%s\n", esni_rr_file_name); + ret = PICOQUIC_ERROR_INVALID_FILE; + } + else { + *esnikeys_len = fread(esnikeys, 1, esnikeys_max, fp); + if (*esnikeys_len == 0 || !feof(fp)) { + DBG_PRINTF("failed to load ESNI data from file:%s\n", esni_rr_file_name); + } + (void)picoquic_file_close(fp); + } + + return ret; +} + +/* Setup ESNI by providing a set of RRDATA for the specified context. + * The "esni_rr_file_name" file contains the RRDATA of _ESNI RR published for the service in the DNS. + * The "esni_key_elements" contains a null terminated array of ptls_key_exchange_context_t* + * TODO: in theory, it should be possible to support multuple ESNI contexts in a single server. + * This would require repeated calls to the "init context" API, and an esni vector + * containing a longer null terminated list of esni pointers. + */ + +static const size_t picoquic_esnikeys_max_rr = 65536; + +int picoquic_esni_server_setup(picoquic_quic_t * quic, char const * esni_rr_file_name) +{ + uint8_t * esnikeys = malloc(picoquic_esnikeys_max_rr); + size_t esnikeys_len; + ptls_context_t *ctx = (ptls_context_t *)quic->tls_master_ctx; + int ret = 0; + + if (esnikeys != NULL) { + /* read esnikeys */ + ret = picoquic_esni_load_rr(esni_rr_file_name, esnikeys, picoquic_esnikeys_max_rr, &esnikeys_len); + + /* Install the ESNI data in the server context */ + if (ret == 0) { + ctx->esni = malloc(sizeof(*ctx->esni) * 2); + + if (ctx->esni != NULL) { + ctx->esni[1] = NULL; + ctx->esni[0] = malloc(sizeof(**ctx->esni)); + } + + if (ctx->esni == NULL || ctx->esni[0] == NULL) { + DBG_PRINTF("%s", "no memory for SNI allocation.\n"); + ret = PICOQUIC_ERROR_MEMORY; + } + else { + if ((ret = ptls_esni_init_context(ctx, ctx->esni[0], ptls_iovec_init(esnikeys, esnikeys_len), quic->esni_key_exchange)) != 0) { + DBG_PRINTF("failed to parse ESNI data of file:%s:error=%d\n", esni_rr_file_name, ret); + } + } + } + free(esnikeys); + } + else { + DBG_PRINTF("failed to allocate memory(%zu) for parsing esni record\n", picoquic_esnikeys_max_rr); + ret = PICOQUIC_ERROR_MEMORY; + } + + return ret; +} + +/* Setup the client side ESNI record, prior to using ESNI in a connection attempt. + */ +int picoquic_esni_client_from_file(picoquic_cnx_t * cnx, char const * esni_rr_file_name) +{ + int ret = 0; + picoquic_tls_ctx_t* ctx = (picoquic_tls_ctx_t*)cnx->tls_ctx; + uint8_t* esnikeys = malloc(picoquic_esnikeys_max_rr); + size_t esnikeys_len; + + if (esnikeys != NULL) { + /* read esnikeys */ + ret = picoquic_esni_load_rr(esni_rr_file_name, esnikeys, picoquic_esnikeys_max_rr, &esnikeys_len); + + if (ret == 0 && esnikeys_len > 0 && esnikeys_len <= picoquic_esnikeys_max_rr){ + ctx->handshake_properties.client.esni_keys.base = (uint8_t*)malloc(esnikeys_len); + if (ctx->handshake_properties.client.esni_keys.base == NULL) { + ctx->handshake_properties.client.esni_keys.len = 0; + DBG_PRINTF("%s", "no memory for SNI allocation.\n"); + ret = PICOQUIC_ERROR_MEMORY; + } + else { + ctx->handshake_properties.client.esni_keys.len = esnikeys_len; + memcpy(ctx->handshake_properties.client.esni_keys.base, esnikeys, esnikeys_len); + } + } + else { + ctx->handshake_properties.client.esni_keys.len = 0; + ctx->handshake_properties.client.esni_keys.base = NULL; + if (ret != 0) { + ret = PICOQUIC_ERROR_INVALID_FILE; + } + } + free(esnikeys); + } + else { + DBG_PRINTF("failed to allocate memory(%zu) for parsing esni record\n", picoquic_esnikeys_max_rr); + ret = PICOQUIC_ERROR_MEMORY; + } + + return ret; +} + +/** + * Access to ESNI data for tests + */ + +uint16_t picoquic_esni_version(picoquic_cnx_t * cnx) +{ + return(((picoquic_tls_ctx_t*)cnx->tls_ctx)->esni_version); +} + +uint8_t * picoquic_esni_nonce(picoquic_cnx_t * cnx) +{ + return(((picoquic_tls_ctx_t*)cnx->tls_ctx)->esni_nonce); +} + +/* Retry Packet Protection. + * This is done by applying AES-GCM128 with a constant key and a NULL nonce, + * using an extension of the retry packet as authenticated data and a zero + * length content, computing a 16 bytes checksum. Or verifying it in the + * other direction. + * + * The retry protection key is stored in the Quic context. It is created on + * first use, and deleted when the context is deleted. + */ + +void * picoquic_create_retry_protection_context(int is_enc, uint8_t * key) +{ + return (void *)picoquic_setup_test_aead_context(is_enc, key); +} + +void * picoquic_find_retry_protection_context(picoquic_cnx_t * cnx, int sending) +{ + void * aead_ctx = NULL; + void ** aead_vector = (sending) ? cnx->quic->retry_integrity_sign_ctx : cnx->quic->retry_integrity_verify_ctx; + + if (picoquic_supported_versions[cnx->version_index].version_retry_key != NULL) { + if (aead_vector == NULL) { + if (sending) { + cnx->quic->retry_integrity_sign_ctx = (void**)malloc(sizeof(void*)*picoquic_nb_supported_versions); + aead_vector = cnx->quic->retry_integrity_sign_ctx; + } + else { + cnx->quic->retry_integrity_verify_ctx = (void**)malloc(sizeof(void*)*picoquic_nb_supported_versions); + aead_vector = cnx->quic->retry_integrity_verify_ctx; + } + if (aead_vector != NULL) { + memset(aead_vector, 0, sizeof(void*)*picoquic_nb_supported_versions); + } + } + + if (aead_vector != NULL) { + aead_ctx = aead_vector[cnx->version_index]; + if (aead_ctx == NULL) { + aead_ctx = picoquic_create_retry_protection_context(sending, picoquic_supported_versions[cnx->version_index].version_retry_key); + aead_vector[cnx->version_index] = aead_ctx; + } + } + } + + return aead_ctx; +} + +static void ** picoquic_delete_one_retry_protection_context(void ** ctx) +{ + if (ctx != NULL) { + for (size_t i = 0; i < picoquic_nb_supported_versions; i++) { + if (ctx[i] != NULL) { + picoquic_aead_free(ctx[i]); + } + } + free(ctx); + } + return NULL; +} + +void picoquic_delete_retry_protection_contexts(picoquic_quic_t * quic) +{ + quic->retry_integrity_sign_ctx = picoquic_delete_one_retry_protection_context(quic->retry_integrity_sign_ctx); + quic->retry_integrity_verify_ctx = picoquic_delete_one_retry_protection_context(quic->retry_integrity_verify_ctx); +} + +static size_t picoquic_format_retry_protection_pseudo_packet(uint8_t * pseudo_packet, uint8_t * bytes, size_t byte_index, const picoquic_connection_id_t * odcid) +{ + size_t pseudo_index = 0; + + if (byte_index + odcid->id_len + 1 < PICOQUIC_MAX_PACKET_SIZE) { + pseudo_packet[pseudo_index++] = odcid->id_len; + memcpy(&pseudo_packet[pseudo_index], odcid->id, odcid->id_len); + pseudo_index += odcid->id_len; + memcpy(&pseudo_packet[pseudo_index], bytes, byte_index); + pseudo_index += byte_index; + } + + return pseudo_index; +} + +size_t picoquic_encode_retry_protection(void * integrity_aead, uint8_t * bytes, size_t bytes_max, size_t byte_index, const picoquic_connection_id_t * odcid) +{ + size_t pseudo_index; + uint8_t pseudo_packet[PICOQUIC_MAX_PACKET_SIZE]; + + if (integrity_aead != NULL && byte_index + picoquic_aead_get_checksum_length(integrity_aead) < bytes_max && + (pseudo_index = picoquic_format_retry_protection_pseudo_packet(pseudo_packet, bytes, byte_index, odcid)) > 0){ + byte_index += picoquic_aead_encrypt_generic(bytes+byte_index, bytes+byte_index, 0, 0, pseudo_packet, pseudo_index, integrity_aead); + } + + return byte_index; +} + +int picoquic_verify_retry_protection(void * integrity_aead, uint8_t * bytes, size_t * length, size_t byte_index, const picoquic_connection_id_t * odcid) +{ + int ret = PICOQUIC_ERROR_AEAD_CHECK; + size_t pseudo_index; + uint8_t pseudo_packet[PICOQUIC_MAX_PACKET_SIZE]; + uint8_t decoded[PICOQUIC_MAX_PACKET_SIZE]; + size_t checksum_length = picoquic_aead_get_checksum_length(integrity_aead); + + if (byte_index + checksum_length < *length) { + *length -= checksum_length; + if ((pseudo_index = picoquic_format_retry_protection_pseudo_packet(pseudo_packet, bytes, *length, odcid)) > 0 && + picoquic_aead_decrypt_generic(decoded, bytes + *length, checksum_length, 0, pseudo_packet, pseudo_index, integrity_aead) == 0) { + ret = 0; + } + } + + return ret; +} diff --git a/deps/picoquic/tls_api.h b/deps/picoquic/tls_api.h new file mode 100644 index 0000000..ab6f1c4 --- /dev/null +++ b/deps/picoquic/tls_api.h @@ -0,0 +1,199 @@ +/* +* Author: Christian Huitema +* Copyright (c) 2017, Private Octopus, Inc. +* All rights reserved. +* +* Permission to use, copy, modify, and distribute this software for any +* purpose with or without fee is hereby granted, provided that the above +* copyright notice and this permission notice appear in all copies. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL Private Octopus, Inc. BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef TLS_API_H +#define TLS_API_H +#include "picoquic_internal.h" + +#define PICOQUIC_LABEL_INITIAL_CLIENT "client in" +#define PICOQUIC_LABEL_INITIAL_SERVER "server in" + +#define PICOQUIC_LABEL_TRAFFIC_UPDATE "quic ku" +#define PICOQUIC_LABEL_TRAFFIC_UPDATE_23 "traffic upd" + +#define PICOQUIC_LABEL_KEY "key" +#define PICOQUIC_LABEL_IV "iv" +#define PICOQUIC_LABEL_HP "hp" +#define PICOQUIC_LABEL_CID "cid" +#define PICOQUIC_LABEL_CID_GLOBAL "cid global" +#define PICOQUIC_LABEL_CID_GLOBAL_ROUNDS 4 + +#define PICOQUIC_LABEL_QUIC_BASE NULL +#define PICOQUIC_LABEL_QUIC_KEY_BASE "tls13 quic " + +int picoquic_master_tlscontext(picoquic_quic_t* quic, char const* cert_file_name, char const* key_file_name, + char const * cert_root_file_name, const uint8_t* ticket_key, size_t ticket_key_length); + +void picoquic_master_tlscontext_free(picoquic_quic_t* quic); + +int picoquic_tlscontext_create(picoquic_quic_t* quic, picoquic_cnx_t* cnx, uint64_t current_time); + +void picoquic_tlscontext_free(void* ctx); + +void picoquic_tlscontext_remove_ticket(picoquic_cnx_t* cnx); + +int picoquic_tls_stream_process(picoquic_cnx_t* cnx, int* data_consumed); +int picoquic_is_tls_complete(picoquic_cnx_t* cnx); + +int picoquic_initialize_tls_stream(picoquic_cnx_t* cnx, uint64_t current_time); + +uint64_t picoquic_get_tls_time(picoquic_quic_t* quic); + +void picoquic_crypto_random(picoquic_quic_t* quic, void* buf, size_t len); +uint64_t picoquic_crypto_uniform_random(picoquic_quic_t* quic, uint64_t rnd_max); + +uint64_t picoquic_public_random_64(void); +void picoquic_public_random_seed_64(uint64_t seed, int reset); +void picoquic_public_random_seed(picoquic_quic_t* quic); +void picoquic_public_random(void* buf, size_t len); +uint64_t picoquic_public_uniform_random(uint64_t rnd_max); + +size_t picoquic_aead_get_checksum_length(void* aead_context); + +size_t picoquic_aead_encrypt_generic(uint8_t* output, const uint8_t* input, size_t input_length, + uint64_t seq_num, const uint8_t* auth_data, size_t auth_data_length, void* aead_context); + +size_t picoquic_aead_decrypt_generic(uint8_t* output, const uint8_t* input, size_t input_length, + uint64_t seq_num, const uint8_t* auth_data, size_t auth_data_length, void* aead_ctx); + +uint64_t picoquic_aead_integrity_limit(void* aead_ctx); +uint64_t picoquic_aead_confidentiality_limit(void* aead_ctx); + +void picoquic_aead_free(void* aead_context); + +size_t picoquic_pn_iv_size(void *pn_enc); + +void picoquic_pn_encrypt(void *pn_enc, const void * iv, void *output, const void *input, size_t len); + +typedef const struct st_ptls_cipher_suite_t ptls_cipher_suite_t; + +int picoquic_setup_initial_master_secret( + ptls_cipher_suite_t * cipher, + ptls_iovec_t salt, + picoquic_connection_id_t initial_cnxid, + uint8_t * master_secret); + +int picoquic_setup_initial_secrets( + ptls_cipher_suite_t * cipher, + uint8_t * master_secret, + uint8_t * client_secret, + uint8_t * server_secret); + +int picoquic_setup_initial_traffic_keys(picoquic_cnx_t* cnx); + +uint8_t * picoquic_get_app_secret(picoquic_cnx_t* cnx, int is_enc); +size_t picoquic_get_app_secret_size(picoquic_cnx_t* cnx); +int picoquic_compute_new_rotated_keys(picoquic_cnx_t * cnx); +void picoquic_apply_rotated_keys(picoquic_cnx_t * cnx, int is_enc); +int picoquic_rotate_app_secret(ptls_cipher_suite_t * cipher, uint8_t * secret); + +void picoquic_crypto_context_free(picoquic_crypto_context_t * ctx); + +void * picoquic_setup_test_aead_context(int is_encrypt, const uint8_t * secret); +void * picoquic_pn_enc_create_for_test(const uint8_t * secret); + +#if 0 +/* TODO: find replacement for this test */ +int picoquic_compare_cleartext_aead_contexts(picoquic_cnx_t* cnx1, picoquic_cnx_t* cnx2); +#endif + +int picoquic_create_cnxid_reset_secret(picoquic_quic_t* quic, picoquic_connection_id_t * cnx_id, + uint8_t reset_secret[PICOQUIC_RESET_SECRET_SIZE]); + +void picoquic_provide_received_transport_extensions(picoquic_cnx_t* cnx, + uint8_t** ext_received, + size_t* ext_received_length, + int* ext_received_return, + int* client_mode); + +char const* picoquic_tls_get_negotiated_alpn(picoquic_cnx_t* cnx); +char const* picoquic_tls_get_sni(picoquic_cnx_t* cnx); + +int picoquic_enable_custom_verify_certificate_callback(picoquic_quic_t* quic); + +void picoquic_dispose_verify_certificate_callback(picoquic_quic_t* quic, int custom); + +void picoquic_tls_set_client_authentication(picoquic_quic_t* quic, int client_authentication); + +int picoquic_tls_client_authentication_activated(picoquic_quic_t* quic); + +int picoquic_server_decrypt_retry_token(picoquic_quic_t* quic, const struct sockaddr* addr_peer, + int * is_new_token, const uint8_t* token, size_t token_length, uint8_t* text, size_t* text_length); + +int picoquic_prepare_retry_token(picoquic_quic_t* quic, const struct sockaddr * addr_peer, + uint64_t current_time, const picoquic_connection_id_t * odcid, const picoquic_connection_id_t* rcid, + uint32_t initial_pn, + uint8_t * token, size_t token_max, size_t * token_size); + +int picoquic_verify_retry_token(picoquic_quic_t* quic, const struct sockaddr * addr_peer, + uint64_t current_time, int * is_new_token, picoquic_connection_id_t * odcid, + const picoquic_connection_id_t* rcid, uint32_t initial_pn, + const uint8_t * token, size_t token_size, int new_context_created); + +void picoquic_cid_free_under_mask_ctx(void * v_pn_enc); +int picoquic_cid_get_under_mask_ctx(void ** v_pn_enc, const void * secret); +void picoquic_cid_encrypt_under_mask(void * cid_enc, const picoquic_connection_id_t * cid_in, const picoquic_connection_id_t * mask, picoquic_connection_id_t * cid_out); +void picoquic_cid_decrypt_under_mask(void * cid_enc, const picoquic_connection_id_t * cid_in, const picoquic_connection_id_t * mask, picoquic_connection_id_t * cid_out); + +void picoquic_cid_free_encrypt_global_ctx(void ** v_cid_enc); + +int picoquic_esni_load_rr(char const * esni_rr_file_name, uint8_t *esnikeys, size_t esnikeys_max, size_t *esnikeys_len); +struct st_ptls_esni_secret_t * picoquic_esni_secret(picoquic_cnx_t * cnx); + +uint16_t picoquic_esni_version(picoquic_cnx_t * cnx); +uint8_t * picoquic_esni_nonce(picoquic_cnx_t * cnx); + +/* Define hash functions here so applications don't need to directly interface picotls */ +#define PICOQUIC_HASH_SIZE_MAX 64 +void * picoquic_hash_create(char const * algorithm_name); +#if 0 +size_t picoquic_hash_get_length(char const* algorithm_name); +#endif +void picoquic_hash_update(uint8_t* input, size_t input_length, void* hash_context); +void picoquic_hash_finalize(uint8_t* output, void* hash_context); + +uint8_t* picoquic_get_private_key_from_key_file(char const* file_name, int* key_length); +ptls_iovec_t* picoquic_get_certs_from_file(char const* file_name, size_t * count); + + +/* Special AEAD context definition functions used for stateless retry integrity protection */ +void * picoquic_create_retry_protection_context(int is_enc, uint8_t * key); +void * picoquic_find_retry_protection_context(picoquic_cnx_t * cnx, int sending); +void picoquic_delete_retry_protection_contexts(picoquic_quic_t * quic); +size_t picoquic_encode_retry_protection(void * integrity_aead, uint8_t * bytes, size_t bytes_max, size_t byte_index, const picoquic_connection_id_t * odcid); +int picoquic_verify_retry_protection(void * integrity_aead, uint8_t * bytes, size_t * length, size_t byte_index, const picoquic_connection_id_t * odcid); + +/* Exportable definition of ciphersuites */ +void* picoquic_get_cipher_suite_by_id_v(int cipher_suite_id); + +/* Exportable version of ciphersuite definition for AES128GCM SHA256 ciphersuite */ +void* picoquic_get_aes128gcm_sha256_v(); + +void* picoquic_get_aes128gcm_v(); + +/* AES ECB function used for CID encryption */ +void* picoquic_aes128_ecb_create(int is_enc, const void* ecb_key); + +void picoquic_aes128_ecb_free(void* v_aesecb); + +void picoquic_aes128_ecb_encrypt(void* v_aesecb, uint8_t* output, const uint8_t* input, size_t len); + +#endif /* TLS_API_H */ \ No newline at end of file diff --git a/deps/picoquic/token_store.c b/deps/picoquic/token_store.c new file mode 100644 index 0000000..b9e657c --- /dev/null +++ b/deps/picoquic/token_store.c @@ -0,0 +1,354 @@ +/* +* Author: Christian Huitema +* Copyright (c) 2019, Private Octopus, Inc. +* All rights reserved. +* +* Permission to use, copy, modify, and distribute this software for any +* purpose with or without fee is hereby granted, provided that the above +* copyright notice and this permission notice appear in all copies. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL Private Octopus, Inc. BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "picoquic_internal.h" +#include +#include +#include +#include + +picoquic_stored_token_t* picoquic_format_token(uint64_t time_valid_until, + char const* sni, uint16_t sni_length, uint8_t const* ip_addr, uint8_t ip_addr_length, + uint8_t const* token, uint16_t token_length) +{ + size_t token_size = sizeof(picoquic_stored_token_t) + sni_length + 1 + ip_addr_length + 1 + token_length; + picoquic_stored_token_t* stored = (picoquic_stored_token_t*)malloc(token_size); + + if (stored != NULL) { + uint8_t* next_p = ((uint8_t*)stored) + sizeof(picoquic_stored_token_t); + + memset(stored, 0, token_size); + stored->time_valid_until = time_valid_until; + stored->sni = (char const *)next_p; + stored->sni_length = sni_length; + memcpy(next_p, sni, sni_length); + next_p += sni_length; + *next_p++ = 0; + + stored->ip_addr = next_p; + stored->ip_addr_length = ip_addr_length; + memcpy(next_p, ip_addr, ip_addr_length); + next_p += ip_addr_length; + *next_p++ = 0; + + stored->token = next_p; + stored->token_length = token_length; + memcpy(next_p, token, token_length); + } + + return stored; +} + +int picoquic_serialize_token(const picoquic_stored_token_t * token, uint8_t * bytes, size_t bytes_max, size_t * consumed) +{ + int ret = 0; + size_t byte_index = 0; + size_t required_length; + + /* Compute serialized length */ + required_length = (size_t)(8 + 2 + 2 + 2) + token->sni_length + token->ip_addr_length + token->token_length; + /* Serialize */ + if (required_length > bytes_max) { + ret = PICOQUIC_ERROR_FRAME_BUFFER_TOO_SMALL; + *consumed = 0; + } else { + picoformat_64(bytes + byte_index, token->time_valid_until); + byte_index += 8; + + picoformat_16(bytes + byte_index, token->sni_length); + byte_index += 2; + memcpy(bytes + byte_index, token->sni, token->sni_length); + byte_index += token->sni_length; + + picoformat_16(bytes + byte_index, token->ip_addr_length); + byte_index += 2; + memcpy(bytes + byte_index, token->ip_addr, token->ip_addr_length); + byte_index += token->ip_addr_length; + + picoformat_16(bytes + byte_index, token->token_length); + byte_index += 2; + memcpy(bytes + byte_index, token->token, token->token_length); + byte_index += token->token_length; + + *consumed = byte_index; + } + + return ret; +} + +int picoquic_deserialize_token(picoquic_stored_token_t ** token, uint8_t * bytes, size_t bytes_max, size_t * consumed) +{ + int ret = 0; + uint64_t time_valid_until = 0; + size_t required_length = 8 + 2 + 2 + 2; + size_t byte_index = 0; + size_t sni_index = 0; + size_t ip_addr_index = 0; + size_t token_index = 0; + uint16_t sni_length = 0; + uint8_t ip_addr_length = 0; + uint16_t token_length = 0; + + + *consumed = 0; + *token = NULL; + + if (required_length < bytes_max) { + time_valid_until = PICOPARSE_64(bytes); + byte_index = 8; + sni_length = PICOPARSE_16(bytes + byte_index); + byte_index += 2; + sni_index = byte_index; + required_length += sni_length; + byte_index += sni_length; + } + + if (required_length < bytes_max) { + ip_addr_length = PICOPARSE_16(bytes + byte_index); + byte_index += 2; + ip_addr_index = byte_index; + required_length += ip_addr_length; + byte_index += ip_addr_length; + } + + if (required_length < bytes_max) { + token_length = PICOPARSE_16(bytes + byte_index); + byte_index += 2; + token_index = byte_index; + required_length += token_length; + } + + if (required_length > bytes_max) { + *token = NULL; + ret = PICOQUIC_ERROR_INVALID_TOKEN; + } else { + *token = picoquic_format_token(time_valid_until, (const char *)(bytes + sni_index), sni_length, + bytes + ip_addr_index, ip_addr_length, bytes + token_index, token_length); + if (*token == NULL) { + ret = PICOQUIC_ERROR_MEMORY; + } + else { + *consumed = required_length; + } + } + + return ret; +} + +int picoquic_store_token(picoquic_stored_token_t** pp_first_token, + uint64_t current_time, + char const* sni, uint16_t sni_length, + uint8_t const* ip_addr, uint8_t ip_addr_length, + uint8_t const* token, uint16_t token_length) +{ + int ret = 0; + + if (token_length < 1 || sni == NULL || sni_length == 0) { + ret = PICOQUIC_ERROR_INVALID_TOKEN; + } + else { + /* There is no explicit TTL for tokens. We assume they are OK for 24 hours */ + uint64_t time_valid_until = current_time + ((uint64_t)24 * 3600) * ((uint64_t)1000000); + picoquic_stored_token_t* stored = picoquic_format_token(time_valid_until, sni, sni_length, + ip_addr, ip_addr_length, token, token_length); + if (stored == NULL) { + ret = PICOQUIC_ERROR_MEMORY; + } + else { + picoquic_stored_token_t* next; + picoquic_stored_token_t** pprevious; + + stored->next_token = next = *pp_first_token; + *pp_first_token = stored; + pprevious = &stored->next_token; + + /* Now remove the old tokens for that SNI & ip_addr */ + while (next != NULL) { + if (next->time_valid_until <= stored->time_valid_until && next->sni_length == sni_length && next->ip_addr_length == ip_addr_length && memcmp(next->sni, sni, sni_length) == 0 && memcmp(next->ip_addr, ip_addr, ip_addr_length) == 0) { + picoquic_stored_token_t* deleted = next; + next = next->next_token; + *pprevious = next; + memset((uint8_t*)&deleted->token, 0, deleted->token_length); + free(deleted); + } + else { + pprevious = &next->next_token; + next = next->next_token; + } + } + } + } + + return ret; +} + +int picoquic_get_token(picoquic_stored_token_t* p_first_token, + uint64_t current_time, + char const* sni, uint16_t sni_length, + uint8_t const* ip_addr, uint8_t ip_addr_length, + uint8_t** token, uint16_t* token_length, int mark_used) +{ + int ret = 0; + picoquic_stored_token_t* next = p_first_token; + picoquic_stored_token_t* best_match = NULL; + + while (next != NULL) { + if (next->time_valid_until > current_time && next->sni_length == sni_length && memcmp(next->sni, sni, sni_length) == 0 && next->was_used == 0){ + if (ip_addr_length > 0) { + if (next->ip_addr_length == ip_addr_length && memcmp(next->ip_addr, ip_addr, ip_addr_length) == 0) { + best_match = next; + break; + } + } + else { + if (best_match == NULL || next->time_valid_until > best_match->time_valid_until) { + best_match = next; + } + } + } + next = next->next_token; + } + + if (best_match == NULL || best_match->token_length == 0 || (*token = (uint8_t *)malloc(best_match->token_length)) == NULL) { + *token = NULL; + *token_length = 0; + ret = -1; + } else { + *token_length = best_match->token_length; + memcpy(*token, (uint8_t*)best_match->token, best_match->token_length); + best_match->was_used = mark_used; + } + + return ret; +} + +int picoquic_save_tokens(const picoquic_stored_token_t* first_token, + uint64_t current_time, + char const* token_file_name) +{ + int ret = 0; + FILE* F = NULL; + const picoquic_stored_token_t* next = first_token; + + if ((F = picoquic_file_open(token_file_name, "wb")) == NULL) { + ret = -1; + } else { + while (ret == 0 && next != NULL) { + /* Only store the tokens that are valid going forward */ + if (next->time_valid_until > current_time && next->was_used == 0) { + /* Compute the serialized size */ + uint8_t buffer[2048]; + size_t record_size; + + ret = picoquic_serialize_token(next, buffer, sizeof(buffer), &record_size); + + if (ret == 0) { + if (fwrite(&record_size, 4, 1, F) != 1 || fwrite(buffer, 1, record_size, F) != record_size) { + ret = PICOQUIC_ERROR_INVALID_FILE; + break; + } + } + } + next = next->next_token; + } + (void)picoquic_file_close(F); + } + + return ret; +} + +int picoquic_load_tokens(picoquic_stored_token_t** pp_first_token, + uint64_t current_time, char const* token_file_name) +{ + int ret = 0; + int file_ret = 0; + FILE* F = NULL; + picoquic_stored_token_t* previous = NULL; + picoquic_stored_token_t* next = NULL; + uint32_t record_size; + uint32_t storage_size; + + if ((F = picoquic_file_open_ex(token_file_name, "rb", &file_ret)) == NULL) { + ret = (file_ret == ENOENT) ? PICOQUIC_ERROR_NO_SUCH_FILE : -1; + } + + while (ret == 0) { + if (fread(&storage_size, 4, 1, F) != 1) { + /* end of file */ + break; + } + else if (storage_size > 2048 || + (record_size = storage_size + offsetof(struct st_picoquic_stored_token_t, time_valid_until)) > 2048) { + ret = PICOQUIC_ERROR_INVALID_FILE; + break; + } + else { + uint8_t buffer[2048]; + if (fread(buffer, 1, storage_size, F) != storage_size) { + ret = PICOQUIC_ERROR_INVALID_FILE; + } + else { + size_t consumed = 0; + ret = picoquic_deserialize_token(&next, buffer, storage_size, &consumed); + + if (ret == 0 && (consumed != storage_size || next == NULL)) { + ret = PICOQUIC_ERROR_INVALID_FILE; + } + + if (ret == 0 && next != NULL) { + if (next->time_valid_until < current_time) { + free(next); + next = NULL; + } + else { + next->sni = ((char*)next) + sizeof(picoquic_stored_token_t); + next->ip_addr = ((uint8_t*)next->sni) + next->sni_length + 1; + next->token = (uint8_t*)(next->ip_addr + next->ip_addr_length + 1); + next->next_token = NULL; + if (previous == NULL) { + *pp_first_token = next; + } + else { + previous->next_token = next; + } + + previous = next; + } + } + } + } + } + + (void)picoquic_file_close(F); + + return ret; +} + +void picoquic_free_tokens(picoquic_stored_token_t** pp_first_token) +{ + picoquic_stored_token_t* next; + + while ((next = *pp_first_token) != NULL) { + *pp_first_token = next->next_token; + + free(next); + } +} diff --git a/deps/picoquic/transport.c b/deps/picoquic/transport.c new file mode 100644 index 0000000..52488bb --- /dev/null +++ b/deps/picoquic/transport.c @@ -0,0 +1,825 @@ +/* +* Author: Christian Huitema +* Copyright (c) 2017, Private Octopus, Inc. +* All rights reserved. +* +* Permission to use, copy, modify, and distribute this software for any +* purpose with or without fee is hereby granted, provided that the above +* copyright notice and this permission notice appear in all copies. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL Private Octopus, Inc. BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/* + * Management of transport parameters for PicoQUIC. + */ + +#include "picoquic_internal.h" +#include "picoquic_unified_log.h" +#include "tls_api.h" +#include + +uint8_t* picoquic_transport_param_varint_encode_old(uint8_t* bytes, const uint8_t* bytes_max, uint64_t n64) +{ + if (bytes + 2 > bytes_max) { + bytes = NULL; + } + else { + uint8_t * byte_l; + size_t l; + + *bytes++ = 0; + byte_l = bytes; + *bytes++ = 0; + l = picoquic_varint_encode(bytes, bytes_max - bytes, n64); + if (l == 0) { + bytes = NULL; + } + else { + *byte_l = (uint8_t) l; + bytes += l; + } + } + + return bytes; +} + +uint64_t picoquic_transport_param_varint_decode(picoquic_cnx_t * cnx, uint8_t* bytes, uint64_t extension_length, int* ret) +{ + uint64_t n64 = 0; + uint64_t l_v = picoquic_varint_decode(bytes, (size_t)extension_length, &n64); + + if (l_v == 0 || l_v != extension_length) { + *ret = picoquic_connection_error(cnx, PICOQUIC_TRANSPORT_PARAMETER_ERROR, 0); + } + + return n64; +} + +uint8_t* picoquic_transport_param_type_varint_encode_old(uint8_t* bytes, const uint8_t* bytes_max, picoquic_tp_enum tp_type, uint64_t n64) +{ + if (bytes != NULL && bytes + 2 <= bytes_max) { + picoformat_16(bytes, (uint16_t)tp_type); + bytes = picoquic_transport_param_varint_encode_old(bytes + 2, bytes_max, n64); + } + else { + bytes = NULL; + } + return bytes; +} + +uint8_t* picoquic_transport_param_varint_encode(uint8_t* bytes, const uint8_t* bytes_max, uint64_t n64) +{ + if (bytes + 1 > bytes_max) { + bytes = NULL; + } + else { + uint8_t* byte_l = bytes++; + bytes = picoquic_frames_varint_encode(bytes, bytes_max, n64); + if (bytes != NULL) { + *byte_l = (uint8_t)((bytes - byte_l) - 1); + } + } + + return bytes; +} + +uint8_t* picoquic_transport_param_type_varint_encode(uint8_t* bytes, const uint8_t* bytes_max, picoquic_tp_enum tp_type, uint64_t n64) +{ + if (bytes != NULL && + (bytes = picoquic_frames_varint_encode(bytes, bytes_max, tp_type)) != NULL) { + bytes = picoquic_transport_param_varint_encode(bytes, bytes_max, n64); + } + return bytes; +} + +uint8_t* picoquic_transport_param_type_flag_encode(uint8_t* bytes, const uint8_t* bytes_max, picoquic_tp_enum tp_type) +{ + if (bytes != NULL && + (bytes = picoquic_frames_varint_encode(bytes, bytes_max, tp_type)) != NULL) { + bytes = picoquic_frames_varint_encode(bytes, bytes_max, 0); + } + return bytes; +} + +uint8_t* picoquic_transport_param_cid_encode(uint8_t* bytes, const uint8_t* bytes_max, picoquic_tp_enum tp_type, picoquic_connection_id_t * cid) +{ + if (bytes != NULL && + (bytes = picoquic_frames_varint_encode(bytes, bytes_max, tp_type)) != NULL) { + /* frame encoding includes length and the value. */ + bytes = picoquic_frames_cid_encode(bytes, bytes_max, cid); + } + return bytes; +} + +int picoquic_transport_param_cid_decode(picoquic_cnx_t * cnx, uint8_t* bytes, uint64_t extension_length, picoquic_connection_id_t* cid) +{ + int ret = 0; + cid->id_len = (uint8_t)picoquic_parse_connection_id(bytes, (uint8_t)extension_length, cid); + if ((size_t)cid->id_len != extension_length) { + ret = picoquic_connection_error(cnx, PICOQUIC_TRANSPORT_PARAMETER_ERROR, 0); + } + + return ret; +} + +uint64_t picoquic_decode_transport_param_stream_id(uint64_t rank, int extension_mode, int stream_type) +{ + uint64_t stream_id = 0xFFFFFFFFFFFFFFFFull; + + if (rank > 0) { + stream_id = stream_type; + stream_id += extension_mode^1; + + stream_id += 4 * (rank - 1); + } + + return stream_id; +} + +uint64_t picoquic_prepare_transport_param_stream_id(uint64_t stream_id) +{ + uint64_t rank = 0; + + if (stream_id != 0xFFFFFFFFFFFFFFFFll) { + rank = (uint64_t)1 + (stream_id / 4); + } + + return rank; +} + +uint8_t* picoquic_encode_transport_param_prefered_address_old(uint8_t* bytes, uint8_t* bytes_max, + picoquic_tp_prefered_address_t* prefered_address) +{ + /* first compute the length */ + uint16_t coded_length = 4u + 2u + 16u + 2u + 1u + prefered_address->connection_id.id_len + 16u; + + if (bytes == NULL || bytes + coded_length > bytes_max) { + bytes = NULL; + } + else { + picoformat_16(bytes, picoquic_tp_server_preferred_address); + bytes += 2; + picoformat_16(bytes, coded_length); + bytes += 2; + memcpy(bytes, prefered_address->ipv4Address, 4); + bytes += 4; + picoformat_16(bytes, prefered_address->ipv4Port); + bytes += 2; + memcpy(bytes, prefered_address->ipv6Address, 16); + bytes += 16; + picoformat_16(bytes, prefered_address->ipv4Port); + bytes += 2; + *bytes++ = prefered_address->connection_id.id_len; + bytes += picoquic_format_connection_id(bytes, bytes_max - bytes, + prefered_address->connection_id); + memcpy(bytes, prefered_address->statelessResetToken, 16); + bytes += 16; + } + + return bytes; +} + +uint8_t * picoquic_encode_transport_param_prefered_address(uint8_t * bytes, uint8_t * bytes_max, + picoquic_tp_prefered_address_t * prefered_address) +{ + /* first compute the length */ + uint64_t coded_length = ((uint64_t)(4 + 2 + 16 + 2 + 1)) + prefered_address->connection_id.id_len + ((uint64_t)16); + + if (bytes != NULL && + (bytes = picoquic_frames_varint_encode(bytes, bytes_max, picoquic_tp_server_preferred_address)) != NULL && + (bytes = picoquic_frames_varint_encode(bytes, bytes_max, coded_length)) != NULL){ + if (bytes + coded_length > bytes_max) { + bytes = NULL; + } + else { + memcpy(bytes, prefered_address->ipv4Address, 4); + bytes += 4; + picoformat_16(bytes, prefered_address->ipv4Port); + bytes += 2; + memcpy(bytes, prefered_address->ipv6Address, 16); + bytes += 16; + picoformat_16(bytes, prefered_address->ipv4Port); + bytes += 2; + *bytes++ = prefered_address->connection_id.id_len; + bytes += picoquic_format_connection_id(bytes, bytes_max - bytes, + prefered_address->connection_id); + memcpy(bytes, prefered_address->statelessResetToken, 16); + bytes += 16; + } + } + + return bytes; +} + +size_t picoquic_decode_transport_param_prefered_address(uint8_t * bytes, size_t bytes_max, + picoquic_tp_prefered_address_t * prefered_address) +{ + /* first compute the minimal length */ + size_t byte_index = 0; + uint8_t cnx_id_length = 0; + size_t minimal_length = 4u + 2u + 16u + 2u + 1u /* + prefered_address->connection_id.id_len */ + 16u; + size_t ret = 0; + + if (bytes_max >= minimal_length) { + memcpy(prefered_address->ipv4Address, bytes + byte_index, 4); + byte_index += 4; + prefered_address->ipv4Port = PICOPARSE_16(bytes + byte_index); + byte_index += 2; + memcpy(prefered_address->ipv6Address, bytes + byte_index, 16); + byte_index += 16; + prefered_address->ipv6Port = PICOPARSE_16(bytes + byte_index); + byte_index += 2; + cnx_id_length = bytes[byte_index++]; + if (cnx_id_length > 0 && cnx_id_length <= PICOQUIC_CONNECTION_ID_MAX_SIZE && + byte_index + (size_t)cnx_id_length + 16u <= bytes_max && + cnx_id_length == picoquic_parse_connection_id(bytes + byte_index, cnx_id_length, + &prefered_address->connection_id)){ + byte_index += cnx_id_length; + memcpy(prefered_address->statelessResetToken, bytes + byte_index, 16); + byte_index += 16; + ret = byte_index; + prefered_address->is_defined = 1; + } + } + + return ret; +} + +int picoquic_prepare_transport_extensions(picoquic_cnx_t* cnx, int extension_mode, + uint8_t* bytes, size_t bytes_length, size_t* consumed) +{ + int ret = 0; + uint8_t* bytes_zero = bytes; + uint8_t* bytes_max = bytes + bytes_length; + + bytes = picoquic_transport_param_type_varint_encode(bytes, bytes_max, picoquic_tp_initial_max_stream_data_bidi_local, + cnx->local_parameters.initial_max_stream_data_bidi_local); + + bytes = picoquic_transport_param_type_varint_encode(bytes, bytes_max, picoquic_tp_initial_max_data, + cnx->local_parameters.initial_max_data); + + if (cnx->local_parameters.initial_max_stream_id_bidir > 0) { + bytes = picoquic_transport_param_type_varint_encode(bytes, bytes_max, picoquic_tp_initial_max_streams_bidi, + picoquic_prepare_transport_param_stream_id( + cnx->local_parameters.initial_max_stream_id_bidir)); + } + + if (cnx->local_parameters.idle_timeout > 0) { + bytes = picoquic_transport_param_type_varint_encode(bytes, bytes_max, picoquic_tp_idle_timeout, + cnx->local_parameters.idle_timeout); + } + + bytes = picoquic_transport_param_type_varint_encode(bytes, bytes_max, picoquic_tp_max_packet_size, + cnx->local_parameters.max_packet_size); + + if (cnx->local_parameters.ack_delay_exponent != 3) { + bytes = picoquic_transport_param_type_varint_encode(bytes, bytes_max, picoquic_tp_ack_delay_exponent, + cnx->local_parameters.ack_delay_exponent); + } + + if (cnx->local_parameters.initial_max_stream_id_unidir > 0) { + bytes = picoquic_transport_param_type_varint_encode(bytes, bytes_max, picoquic_tp_initial_max_streams_uni, + picoquic_prepare_transport_param_stream_id(cnx->local_parameters.initial_max_stream_id_unidir)); + } + + if (cnx->local_parameters.prefered_address.is_defined) { + bytes = picoquic_encode_transport_param_prefered_address( + bytes, bytes_max, &cnx->local_parameters.prefered_address); + } + + if (cnx->local_parameters.migration_disabled != 0 && bytes != NULL) { + bytes = picoquic_transport_param_type_flag_encode(bytes, bytes_max, picoquic_tp_disable_migration); + } + + if (cnx->local_parameters.initial_max_stream_data_bidi_remote > 0) { + bytes = picoquic_transport_param_type_varint_encode(bytes, bytes_max, picoquic_tp_initial_max_stream_data_bidi_remote, + cnx->local_parameters.initial_max_stream_data_bidi_remote); + } + + if (cnx->local_parameters.initial_max_stream_data_uni > 0) { + bytes = picoquic_transport_param_type_varint_encode(bytes, bytes_max, picoquic_tp_initial_max_stream_data_uni, + cnx->local_parameters.initial_max_stream_data_uni); + } + + if (cnx->local_parameters.active_connection_id_limit > 0) { + bytes = picoquic_transport_param_type_varint_encode(bytes, bytes_max, picoquic_tp_active_connection_id_limit, + cnx->local_parameters.active_connection_id_limit); + } + + if (cnx->local_parameters.max_ack_delay != PICOQUIC_ACK_DELAY_MAX_DEFAULT) { + bytes = picoquic_transport_param_type_varint_encode(bytes, bytes_max, picoquic_tp_max_ack_delay, + (cnx->local_parameters.max_ack_delay + 999) / 1000); /* Max ACK delay in milliseconds */ + } + bytes = picoquic_transport_param_cid_encode(bytes, bytes_max, picoquic_tp_handshake_connection_id, &cnx->path[0]->p_local_cnxid->cnx_id); + + if (extension_mode == 1){ + if (cnx->original_cnxid.id_len > 0) { + bytes = picoquic_transport_param_cid_encode(bytes, bytes_max, picoquic_tp_original_connection_id, &cnx->original_cnxid); + bytes = picoquic_transport_param_cid_encode(bytes, bytes_max, picoquic_tp_retry_connection_id, &cnx->initial_cnxid); + } + else if (cnx->is_hcid_verified) { + bytes = picoquic_transport_param_cid_encode(bytes, bytes_max, picoquic_tp_original_connection_id, &cnx->initial_cnxid); + } + } + + if (extension_mode == 1) { + if (bytes != NULL && + (bytes = picoquic_frames_varint_encode(bytes, bytes_max, picoquic_tp_stateless_reset_token)) != NULL && + (bytes = picoquic_frames_varint_encode(bytes, bytes_max, PICOQUIC_RESET_SECRET_SIZE)) != NULL) { + if (bytes + PICOQUIC_RESET_SECRET_SIZE < bytes_max) { + (void)picoquic_create_cnxid_reset_secret(cnx->quic, &cnx->path[0]->p_local_cnxid->cnx_id, bytes); + bytes += PICOQUIC_RESET_SECRET_SIZE; + } + else { + bytes = NULL; + } + } + } + + if (!cnx->client_mode && cnx->local_parameters.max_datagram_frame_size == 0 && + cnx->remote_parameters.max_datagram_frame_size > 0) { + cnx->local_parameters.max_datagram_frame_size = PICOQUIC_MAX_PACKET_SIZE; + } + + if (cnx->local_parameters.max_datagram_frame_size > 0 && bytes != NULL) { + bytes = picoquic_transport_param_type_varint_encode(bytes, bytes_max, picoquic_tp_max_datagram_frame_size, + cnx->local_parameters.max_datagram_frame_size); + } + + if (cnx->grease_transport_parameters) { + /* Do not use a purely random value, so we can repetitive tests */ + int n = 31 * (cnx->initial_cnxid.id[0] + cnx->client_mode) + 27; + uint64_t v = cnx->initial_cnxid.id[1]; + while (n == picoquic_tp_test_large_chello || + n == picoquic_tp_enable_loss_bit_old) { + n += 31; + } + v = (v << 8) + cnx->initial_cnxid.id[2]; + bytes = picoquic_transport_param_type_varint_encode(bytes, bytes_max, n, v); + } + + if (cnx->test_large_chello && bytes != NULL && + (bytes = picoquic_frames_varint_encode(bytes, bytes_max, picoquic_tp_test_large_chello)) != NULL && + (bytes = picoquic_frames_varint_encode(bytes, bytes_max, 1200)) != NULL){ + if (bytes + 1200 > bytes_max) { + bytes = NULL; + } + else { + memset(bytes, 'Q', 1200); + bytes += 1200; + } + } + + if (cnx->local_parameters.enable_loss_bit > 0 && bytes != NULL) { + bytes = picoquic_transport_param_type_varint_encode(bytes, bytes_max, picoquic_tp_enable_loss_bit, + (cnx->local_parameters.enable_loss_bit > 1) ? 1 : 0); + } + + if (bytes != NULL && cnx->local_parameters.min_ack_delay > 0) { + bytes = picoquic_transport_param_type_varint_encode(bytes, bytes_max, picoquic_tp_min_ack_delay, + cnx->local_parameters.min_ack_delay); + } + + if (cnx->local_parameters.enable_time_stamp > 0 && bytes != NULL) { + bytes = picoquic_transport_param_type_varint_encode(bytes, bytes_max, picoquic_tp_enable_time_stamp, + cnx->local_parameters.enable_time_stamp); + } + + if (cnx->local_parameters.do_grease_quic_bit > 0 && bytes != NULL) { + bytes = picoquic_transport_param_type_flag_encode(bytes, bytes_max, picoquic_tp_grease_quic_bit); + } + + if (bytes == NULL) { + *consumed = 0; + ret = PICOQUIC_ERROR_EXTENSION_BUFFER_TOO_SMALL; + } + else { + *consumed = bytes - bytes_zero; + picoquic_log_transport_extension(cnx, 1, *consumed, bytes_zero); + } + + return ret; +} + +void picoquic_clear_transport_extensions(picoquic_cnx_t* cnx) +{ + cnx->remote_parameters.initial_max_stream_data_bidi_local = 0; + picoquic_update_stream_initial_remote(cnx); + cnx->remote_parameters.initial_max_stream_data_bidi_remote = 0; + picoquic_update_stream_initial_remote(cnx); + cnx->remote_parameters.initial_max_stream_data_uni = 0; + picoquic_update_stream_initial_remote(cnx); + cnx->remote_parameters.initial_max_data = 0; + cnx->maxdata_remote = cnx->remote_parameters.initial_max_data; + cnx->remote_parameters.initial_max_stream_id_bidir = 0; + cnx->max_stream_id_bidir_remote = 0; + cnx->remote_parameters.idle_timeout = 0; + cnx->remote_parameters.max_packet_size = 1500; + cnx->remote_parameters.ack_delay_exponent = 3; + cnx->remote_parameters.initial_max_stream_id_unidir = 0; + cnx->max_stream_id_unidir_remote = 0; + cnx->remote_parameters.migration_disabled = 0; + cnx->remote_parameters.max_ack_delay = PICOQUIC_ACK_DELAY_MAX_DEFAULT; + cnx->remote_parameters.max_datagram_frame_size = 0; + cnx->remote_parameters.active_connection_id_limit = 0; + cnx->remote_parameters.enable_loss_bit = 0; + cnx->remote_parameters.enable_time_stamp = 0; + cnx->remote_parameters.min_ack_delay = 0; + cnx->remote_parameters.do_grease_quic_bit = 0; +} + +int picoquic_receive_transport_extensions(picoquic_cnx_t* cnx, int extension_mode, + uint8_t* bytes, size_t bytes_max, size_t* consumed) +{ + int ret = 0; + size_t byte_index = 0; + uint64_t present_flag = 0; + picoquic_connection_id_t original_connection_id = picoquic_null_connection_id; + picoquic_connection_id_t handshake_connection_id = picoquic_null_connection_id; + picoquic_connection_id_t retry_connection_id = picoquic_null_connection_id; + + cnx->remote_parameters_received = 1; + picoquic_clear_transport_extensions(cnx); + + picoquic_log_transport_extension(cnx, 0, bytes_max, bytes); + + /* Set the parameters to default value zero */ + memset(&cnx->remote_parameters, 0, sizeof(picoquic_tp_t)); + /* Except for ack_delay_exponent, whose default is 3 */ + cnx->remote_parameters.ack_delay_exponent = 3; + + while (ret == 0 && byte_index < bytes_max) { + size_t ll_type = 0; + size_t ll_length = 0; + uint64_t extension_type = UINT64_MAX; + uint64_t extension_length = 0; + + if (byte_index + 2 > bytes_max) { + ret = picoquic_connection_error(cnx, PICOQUIC_TRANSPORT_PARAMETER_ERROR, 0); + } + else { + ll_type = picoquic_varint_decode(bytes + byte_index, bytes_max - byte_index, &extension_type); + byte_index += ll_type; + ll_length = picoquic_varint_decode(bytes + byte_index, bytes_max - byte_index, &extension_length); + byte_index += ll_length; + + if (ll_type == 0 || ll_length == 0 || byte_index + extension_length > bytes_max) { + ret = picoquic_connection_error(cnx, PICOQUIC_TRANSPORT_PARAMETER_ERROR, 0); + } + else { + if (extension_type < 64) { + if ((present_flag & (1ull << extension_type)) != 0) { + /* Malformed, already present */ + ret = picoquic_connection_error(cnx, PICOQUIC_TRANSPORT_PARAMETER_ERROR, 0); + } + else { + present_flag |= (1ull << extension_type); + } + } + + switch (extension_type) { + case picoquic_tp_initial_max_stream_data_bidi_local: + cnx->remote_parameters.initial_max_stream_data_bidi_local = + picoquic_transport_param_varint_decode(cnx, bytes + byte_index, extension_length, &ret); + + /* If we sent zero rtt data, the streams were created with the + * old value of the remote parameter. We need to update that. + */ + picoquic_update_stream_initial_remote(cnx); + break; + case picoquic_tp_initial_max_stream_data_bidi_remote: + cnx->remote_parameters.initial_max_stream_data_bidi_remote = + picoquic_transport_param_varint_decode(cnx, bytes + byte_index, extension_length, &ret); + /* If we sent zero rtt data, the streams were created with the + * old value of the remote parameter. We need to update that. + */ + picoquic_update_stream_initial_remote(cnx); + break; + case picoquic_tp_initial_max_stream_data_uni: + cnx->remote_parameters.initial_max_stream_data_uni = + picoquic_transport_param_varint_decode(cnx, bytes + byte_index, extension_length, &ret); + /* If we sent zero rtt data, the streams were created with the + * old value of the remote parameter. We need to update that. + */ + picoquic_update_stream_initial_remote(cnx); + break; + case picoquic_tp_initial_max_data: + cnx->remote_parameters.initial_max_data = + picoquic_transport_param_varint_decode(cnx, bytes + byte_index, extension_length, &ret); + cnx->maxdata_remote = cnx->remote_parameters.initial_max_data; + break; + case picoquic_tp_initial_max_streams_bidi: { + uint64_t old_limit = cnx->max_stream_id_bidir_remote; + cnx->remote_parameters.initial_max_stream_id_bidir = + picoquic_decode_transport_param_stream_id( + picoquic_transport_param_varint_decode(cnx, bytes + byte_index, extension_length, &ret), extension_mode, + PICOQUIC_STREAM_ID_BIDIR); + + cnx->max_stream_id_bidir_remote = + (cnx->remote_parameters.initial_max_stream_id_bidir == 0xFFFFFFFF) ? 0 : cnx->remote_parameters.initial_max_stream_id_bidir; + + picoquic_add_output_streams(cnx, old_limit, cnx->max_stream_id_bidir_remote, 1); + break; + } + case picoquic_tp_idle_timeout: + cnx->remote_parameters.idle_timeout = (uint32_t) + picoquic_transport_param_varint_decode(cnx, bytes + byte_index, extension_length, &ret); + break; + + case picoquic_tp_max_packet_size: + cnx->remote_parameters.max_packet_size = (uint32_t) + picoquic_transport_param_varint_decode(cnx, bytes + byte_index, extension_length, &ret); + break; + case picoquic_tp_stateless_reset_token: + if (extension_mode != 1) { + ret = picoquic_connection_error(cnx, PICOQUIC_TRANSPORT_PARAMETER_ERROR, 0); + } + else if (extension_length != PICOQUIC_RESET_SECRET_SIZE) { + ret = picoquic_connection_error(cnx, PICOQUIC_TRANSPORT_PARAMETER_ERROR, 0); + } + else { + memcpy(cnx->path[0]->reset_secret, bytes + byte_index, PICOQUIC_RESET_SECRET_SIZE); + } + break; + case picoquic_tp_ack_delay_exponent: + cnx->remote_parameters.ack_delay_exponent = (uint8_t) + picoquic_transport_param_varint_decode(cnx, bytes + byte_index, extension_length, &ret); + break; + case picoquic_tp_initial_max_streams_uni: { + uint64_t old_limit = cnx->max_stream_id_unidir_remote; + cnx->remote_parameters.initial_max_stream_id_unidir = + picoquic_decode_transport_param_stream_id( + picoquic_transport_param_varint_decode(cnx, bytes + byte_index, extension_length, &ret), extension_mode, + PICOQUIC_STREAM_ID_UNIDIR); + + cnx->max_stream_id_unidir_remote = + (cnx->remote_parameters.initial_max_stream_id_unidir == 0xFFFFFFFF) ? 0 : cnx->remote_parameters.initial_max_stream_id_unidir; + picoquic_add_output_streams(cnx, old_limit, cnx->max_stream_id_unidir_remote, 0); + break; + } + case picoquic_tp_server_preferred_address: + { + uint64_t coded_length = picoquic_decode_transport_param_prefered_address( + bytes + byte_index, (size_t)extension_length, &cnx->remote_parameters.prefered_address); + + if (coded_length != extension_length) { + ret = picoquic_connection_error(cnx, PICOQUIC_TRANSPORT_PARAMETER_ERROR, 0); + } + break; + } + case picoquic_tp_disable_migration: + if (extension_length != 0) { + ret = picoquic_connection_error(cnx, PICOQUIC_TRANSPORT_PARAMETER_ERROR, 0); + } + else { + cnx->remote_parameters.migration_disabled = 1; + } + break; + case picoquic_tp_max_ack_delay: + cnx->remote_parameters.max_ack_delay = (uint32_t) + picoquic_transport_param_varint_decode(cnx, bytes + byte_index, extension_length, &ret) * 1000; + if (cnx->remote_parameters.max_ack_delay > PICOQUIC_MAX_ACK_DELAY_MAX_MS * 1000) { + ret = picoquic_connection_error(cnx, PICOQUIC_TRANSPORT_PARAMETER_ERROR, 0); + } + break; + case picoquic_tp_original_connection_id: + ret = picoquic_transport_param_cid_decode(cnx, bytes + byte_index, extension_length, &original_connection_id); + break; + case picoquic_tp_retry_connection_id: + ret = picoquic_transport_param_cid_decode(cnx, bytes + byte_index, extension_length, &retry_connection_id); + break; + case picoquic_tp_handshake_connection_id: + ret = picoquic_transport_param_cid_decode(cnx, bytes + byte_index, extension_length, &handshake_connection_id); + if (ret == 0) { + if (picoquic_compare_connection_id(&cnx->path[0]->remote_cnxid, &handshake_connection_id) != 0) { + ret = picoquic_connection_error(cnx, PICOQUIC_TRANSPORT_PARAMETER_ERROR, 0); + } + else { + cnx->is_hcid_verified = 1; + } + } + break; + case picoquic_tp_active_connection_id_limit: + cnx->remote_parameters.active_connection_id_limit = (uint32_t) + picoquic_transport_param_varint_decode(cnx, bytes + byte_index, extension_length, &ret); + /* TODO: may need to check the value, but conditions are unclear */ + break; + case picoquic_tp_max_datagram_frame_size: + cnx->remote_parameters.max_datagram_frame_size = (uint32_t) + picoquic_transport_param_varint_decode(cnx, bytes + byte_index, extension_length, &ret); + break; + case picoquic_tp_enable_loss_bit_old: + /* The old loss bit definition is obsolete */ + break; + case picoquic_tp_enable_loss_bit: { + uint64_t enabled = picoquic_transport_param_varint_decode(cnx, bytes + byte_index, extension_length, &ret); + if (ret == 0) { + if (enabled == 0) { + /* Send only variant of loss bit */ + cnx->remote_parameters.enable_loss_bit = 1; + } + else if (enabled == 1) { + /* Both send and receive are enabled */ + cnx->remote_parameters.enable_loss_bit = 2; + } + else { + /* Only values 0 and 1 are expected */ + ret = picoquic_connection_error(cnx, PICOQUIC_TRANSPORT_PARAMETER_ERROR, 0); + } + } + break; + } + case picoquic_tp_min_ack_delay: + cnx->remote_parameters.min_ack_delay = + picoquic_transport_param_varint_decode(cnx, bytes + byte_index, extension_length, &ret); + /* Values of 0 and values larger that 2^24 are not expected */ + if (ret == 0 && + (cnx->remote_parameters.min_ack_delay == 0 || + cnx->remote_parameters.min_ack_delay > PICOQUIC_ACK_DELAY_MIN_MAX_VALUE)) { + ret = picoquic_connection_error(cnx, PICOQUIC_TRANSPORT_PROTOCOL_VIOLATION, 0); + } + else { + if (cnx->local_parameters.max_ack_delay > 0) { + cnx->is_ack_frequency_negotiated = 1; + } + } + break; + case picoquic_tp_enable_time_stamp: { + uint64_t tp_time_stamp = + picoquic_transport_param_varint_decode(cnx, bytes + byte_index, extension_length, &ret); + + if (ret == 0) { + if (tp_time_stamp < 1 || tp_time_stamp > 3) { + ret = picoquic_connection_error(cnx, PICOQUIC_TRANSPORT_PARAMETER_ERROR, 0); + } + else { + cnx->remote_parameters.enable_time_stamp = (int)tp_time_stamp; + } + } + break; + } + case picoquic_tp_grease_quic_bit: + if (extension_length != 0) { + ret = picoquic_connection_error(cnx, PICOQUIC_TRANSPORT_PARAMETER_ERROR, 0); + } + else { + cnx->remote_parameters.do_grease_quic_bit = 1; + } + break; + default: + /* ignore unknown extensions */ + break; + } + + if (ret == 0) { + byte_index += (size_t)extension_length; + } + } + } + } + + /* Compute the negotiated version of the time out. + * The parameter values are expressed in milliseconds, + * but the connection context variable is in microseconds. + * If the keep alive interval was set to a too short value, + * reset it. + */ + cnx->idle_timeout = cnx->local_parameters.idle_timeout*1000ull; + if (cnx->local_parameters.idle_timeout == 0 || + (cnx->remote_parameters.idle_timeout > 0 && cnx->remote_parameters.idle_timeout < + cnx->local_parameters.idle_timeout)) { + cnx->idle_timeout = cnx->remote_parameters.idle_timeout*1000ull; + } + if (cnx->idle_timeout == 0) { + cnx->idle_timeout = UINT64_MAX; + } + else if (cnx->keep_alive_interval != 0 && + cnx->keep_alive_interval > cnx->idle_timeout / 2) { + cnx->keep_alive_interval = cnx->idle_timeout / 2; + } + + if (ret == 0 && (present_flag & (1ull << picoquic_tp_max_ack_delay)) == 0) { + cnx->remote_parameters.max_ack_delay = PICOQUIC_ACK_DELAY_MAX_DEFAULT; + } + + if (ret == 0 && (present_flag & (1ull << picoquic_tp_active_connection_id_limit)) == 0) { + if (cnx->path[0]->p_local_cnxid->cnx_id.id_len == 0) { + cnx->remote_parameters.active_connection_id_limit = 0; + } + else { + cnx->remote_parameters.active_connection_id_limit = PICOQUIC_NB_PATH_DEFAULT; + } + } + + /* Clients must not include reset token, server address, retry cid or original cid */ + + if (ret == 0 && extension_mode == 0 && + ((present_flag & (1ull << picoquic_tp_stateless_reset_token)) != 0 || + (present_flag & (1ull << picoquic_tp_server_preferred_address)) != 0 || + (present_flag & (1ull << picoquic_tp_original_connection_id)) != 0 || + (present_flag & (1ull << picoquic_tp_retry_connection_id)) != 0)) { + ret = picoquic_connection_error(cnx, PICOQUIC_TRANSPORT_PARAMETER_ERROR, 0); + } + + /* In the old versions, there was only one parameter: original CID. In the new versions, + * there are also retry CID and handshake CID, and the verification logic changed. + * If the new extensions are not used and the version is old, we support the + * old behavior. If the HCID extension is present, we support the new behavior. + * Most of the verifications happen on the client side, upon receiving server + * parameters. + * TODO: clean up when removing support for version 27. + */ + + if (ret == 0 && picoquic_supported_versions[cnx->version_index].version != PICOQUIC_SEVENTEENTH_INTEROP_VERSION && + (present_flag & (1ull << picoquic_tp_handshake_connection_id)) == 0) { + /* HCID extension becomes mandatory after draft 27 */ + ret = picoquic_connection_error(cnx, PICOQUIC_TRANSPORT_PARAMETER_ERROR, 0); + } + + if (ret == 0 && extension_mode == 1) { + /* Reeciving server parameters */ + if ((present_flag & (1ull << picoquic_tp_handshake_connection_id)) != 0) { + /* The HCID extension is present. Verify that the original and retry cnxid are as expected */ + if (cnx->original_cnxid.id_len != 0) { + /* OCID should be present and match original_cid. + * RCID should be present and match initial_cid, since token parsing + * verified that initial_cid matches source CID of retry packet. */ + if ((present_flag & (1ull << picoquic_tp_retry_connection_id)) == 0 || + (present_flag & (1ull << picoquic_tp_original_connection_id)) == 0 || + picoquic_compare_connection_id(&cnx->original_cnxid, &original_connection_id) != 0 || + picoquic_compare_connection_id(&cnx->initial_cnxid, &retry_connection_id) != 0) { + ret = picoquic_connection_error(cnx, PICOQUIC_TRANSPORT_PARAMETER_ERROR, 0); + } + } + else { + /* RCID should not be present, OCID should be present and match initial_cid */ + if ((present_flag & (1ull << picoquic_tp_retry_connection_id)) != 0 || + (present_flag & (1ull << picoquic_tp_original_connection_id)) == 0 || + picoquic_compare_connection_id(&cnx->initial_cnxid, &original_connection_id) != 0) { + ret = picoquic_connection_error(cnx, PICOQUIC_TRANSPORT_PARAMETER_ERROR, 0); + } + } + } + else if (picoquic_supported_versions[cnx->version_index].version == PICOQUIC_SEVENTEENTH_INTEROP_VERSION) { + /* Old behavior. Original CID only present if retry */ + if (cnx->original_cnxid.id_len != 0 && + ((present_flag & (1ull << picoquic_tp_original_connection_id)) == 0 || + picoquic_compare_connection_id(&cnx->original_cnxid, &original_connection_id) != 0)) { + ret = picoquic_connection_error(cnx, PICOQUIC_TRANSPORT_PARAMETER_ERROR, 0); + } + } + } + + /* Loss bit is only enabled if negotiated by both parties */ + cnx->is_loss_bit_enabled_outgoing = (cnx->local_parameters.enable_loss_bit > 1) && (cnx->remote_parameters.enable_loss_bit > 0); + cnx->is_loss_bit_enabled_incoming = (cnx->local_parameters.enable_loss_bit > 0) && (cnx->remote_parameters.enable_loss_bit > 1); + + /* One way delay and Quic_bit_grease only enabled if asked by client and accepted by server */ + if (cnx->client_mode) { + cnx->is_time_stamp_enabled = + (cnx->local_parameters.enable_time_stamp&1) && (cnx->remote_parameters.enable_time_stamp&2); + cnx->is_time_stamp_sent = + (cnx->local_parameters.enable_time_stamp & 2) && (cnx->remote_parameters.enable_time_stamp & 1); + cnx->do_grease_quic_bit = cnx->local_parameters.do_grease_quic_bit && cnx->remote_parameters.do_grease_quic_bit; + } + else + { + if (cnx->remote_parameters.enable_time_stamp) { + int v_local = 0; + if (cnx->remote_parameters.enable_time_stamp & 1) { + /* Peer wants TS. Say that we can send. */ + v_local |= 2; + cnx->is_time_stamp_sent = 1; + } + if (cnx->remote_parameters.enable_time_stamp & 2) { + /* Peer can do TS. Say that we want to receive. */ + v_local |= 1; + cnx->is_time_stamp_enabled = 1; + } + cnx->local_parameters.enable_time_stamp = v_local; + } + /* When the one way option is set, the server will grease the quic bit if the client supports that, + * but will not announce support of the grease quic bit, thus asking the client to not set it */ + cnx->local_parameters.do_grease_quic_bit = cnx->remote_parameters.do_grease_quic_bit && !cnx->quic->one_way_grease_quic_bit; + cnx->do_grease_quic_bit = cnx->remote_parameters.do_grease_quic_bit; + } + + /* ACK Frequency is only enabled on server if negotiated by client */ + if (!cnx->client_mode && !cnx->is_ack_frequency_negotiated) { + cnx->local_parameters.min_ack_delay = 0; + } + + *consumed = byte_index; + + return ret; +} diff --git a/deps/picoquic/unified_log.c b/deps/picoquic/unified_log.c new file mode 100644 index 0000000..f989d10 --- /dev/null +++ b/deps/picoquic/unified_log.c @@ -0,0 +1,237 @@ +/* +* Author: Christian Huitema +* Copyright (c) 2017, Private Octopus, Inc. +* All rights reserved. +* +* Permission to use, copy, modify, and distribute this software for any +* purpose with or without fee is hereby granted, provided that the above +* copyright notice and this permission notice appear in all copies. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL Private Octopus, Inc. BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "picoquic.h" +#include "picoquic_internal.h" +#include "tls_api.h" +#include +#include +#ifndef _WINDOWS +#include +#endif +#include "picoquic_unified_log.h" + +/* Log arrival or departure of an UDP datagram for an unknown connection */ +void picoquic_log_quic_pdu(picoquic_quic_t* quic, int receiving, uint64_t current_time, uint64_t cid64, + const struct sockaddr* addr_peer, const struct sockaddr* addr_local, size_t packet_length) +{ + if (quic->F_log != NULL) { + quic->text_log_fns->log_quic_pdu(quic, receiving, current_time, cid64, addr_peer, addr_local, packet_length); + } +} + +/* Log an event relating to a specific connection */ + +void picoquic_log_app_message(picoquic_cnx_t* cnx, const char* fmt, ...) +{ + if (cnx->quic->F_log != NULL) { + va_list args; + va_start(args, fmt); + cnx->quic->text_log_fns->log_app_message(cnx, fmt, args); + va_end(args); + } + + if (cnx->f_binlog != NULL) { + va_list args; + va_start(args, fmt); + cnx->quic->bin_log_fns->log_app_message(cnx, fmt, args); + va_end(args); + } +} + +void picoquic_log_context_free_app_message(picoquic_quic_t* quic, const picoquic_connection_id_t* cid, const char* fmt, ...) +{ + if (quic->F_log != NULL) { + va_list args; + va_start(args, fmt); + quic->text_log_fns->log_quic_app_message(quic, cid, fmt, args); + va_end(args); + } +} + +/* Log arrival or departure of an UDP datagram on a connection */ +void picoquic_log_pdu(picoquic_cnx_t* cnx, int receiving, uint64_t current_time, + const struct sockaddr* addr_peer, const struct sockaddr* addr_local, size_t packet_length) +{ + if (picoquic_cnx_is_still_logging(cnx)) { + if (cnx->quic->F_log != NULL) { + cnx->quic->text_log_fns->log_pdu(cnx, receiving, current_time, addr_peer, addr_local, packet_length); + } + + if (cnx->f_binlog != NULL) { + cnx->quic->bin_log_fns->log_pdu(cnx, receiving, current_time, addr_peer, addr_local, packet_length); + } + } +} + +/* Log a decrypted packet - receiving = 1 if arrival, = 0 if sending */ +void picoquic_log_packet(picoquic_cnx_t* cnx, int receiving, uint64_t current_time, + struct st_picoquic_packet_header_t* ph, const uint8_t* bytes, size_t bytes_max) +{ + if (picoquic_cnx_is_still_logging(cnx)) { + if (cnx->quic->F_log != NULL) { + cnx->quic->text_log_fns->log_packet(cnx, receiving, current_time, ph, bytes, bytes_max); + } + + if (cnx->f_binlog != NULL) { + cnx->quic->bin_log_fns->log_packet(cnx, receiving, current_time, ph, bytes, bytes_max); + } + } +} + +/* Report that a packet was dropped due to some error */ +void picoquic_log_dropped_packet(picoquic_cnx_t* cnx, struct st_picoquic_packet_header_t* ph, size_t packet_size, + int err, uint8_t* raw_data, uint64_t current_time) +{ + if (picoquic_cnx_is_still_logging(cnx)) { + if (cnx->quic->F_log != NULL) { + cnx->quic->text_log_fns->log_dropped_packet(cnx, ph, packet_size, err, raw_data, current_time); + } + + if (cnx->f_binlog != NULL) { + cnx->quic->bin_log_fns->log_dropped_packet(cnx, ph, packet_size, err, raw_data, current_time); + } + } +} + +/* Report that packet was buffered waiting for decryption */ +void picoquic_log_buffered_packet(picoquic_cnx_t* cnx, picoquic_packet_type_enum ptype, uint64_t current_time) +{ + if (picoquic_cnx_is_still_logging(cnx)) { + if (cnx->quic->F_log != NULL) { + cnx->quic->text_log_fns->log_buffered_packet(cnx, ptype, current_time); + } + + if (cnx->f_binlog != NULL) { + cnx->quic->bin_log_fns->log_buffered_packet(cnx, ptype, current_time); + } + } +} + +/* Log that a packet was formatted, ready to be sent. */ +void picoquic_log_outgoing_packet(picoquic_cnx_t* cnx, + uint8_t* bytes, uint64_t sequence_number, size_t pn_length, size_t length, + uint8_t* send_buffer, size_t send_length, uint64_t current_time) +{ + if (picoquic_cnx_is_still_logging(cnx)) { + if (cnx->quic->F_log != NULL) { + cnx->quic->text_log_fns->log_outgoing_packet(cnx, bytes, sequence_number, pn_length, length, + send_buffer, send_length, current_time); + } + + if (cnx->f_binlog != NULL) { + cnx->quic->bin_log_fns->log_outgoing_packet(cnx, bytes, sequence_number, pn_length, length, + send_buffer, send_length, current_time); + } + } +} + +/* Log packet lost events */ +void picoquic_log_packet_lost(picoquic_cnx_t* cnx, + picoquic_packet_type_enum ptype, uint64_t sequence_number, char const* trigger, + picoquic_connection_id_t* dcid, size_t packet_size, + uint64_t current_time) +{ + if (picoquic_cnx_is_still_logging(cnx)) { + if (cnx->quic->F_log != NULL) { + cnx->quic->text_log_fns->log_packet_lost(cnx, ptype, sequence_number, trigger, dcid, packet_size, current_time); + } + + if (cnx->f_binlog != NULL) { + cnx->quic->bin_log_fns->log_packet_lost(cnx, ptype, sequence_number, trigger, dcid, packet_size, current_time); + } + } +} + +/* Log ALPN negotiation, or results */ +void picoquic_log_negotiated_alpn(picoquic_cnx_t* cnx, int is_local, + uint8_t const* sni, size_t sni_len, uint8_t const* alpn, size_t alpn_len, + const ptls_iovec_t* alpn_list, size_t alpn_count) +{ + if (cnx->quic->F_log != NULL) { + cnx->quic->text_log_fns->log_negotiated_alpn(cnx, is_local, sni, sni_len, alpn, alpn_len, alpn_list, alpn_count); + } + + if (cnx->f_binlog != NULL) { + cnx->quic->bin_log_fns->log_negotiated_alpn(cnx, is_local, sni, sni_len, alpn, alpn_len, alpn_list, alpn_count); + } +} + +/* log transport extension -- either formatted by the loacl peer (is_local=1) or received from remote peer */ +void picoquic_log_transport_extension(picoquic_cnx_t* cnx, int is_local, + size_t param_length, uint8_t* params) +{ + if (cnx->quic->F_log != NULL) { + cnx->quic->text_log_fns->log_transport_extension(cnx, is_local, param_length, params); + } + + if (cnx->f_binlog != NULL) { + cnx->quic->bin_log_fns->log_transport_extension(cnx, is_local, param_length, params); + } +} + +/* log TLS ticket */ +void picoquic_log_tls_ticket(picoquic_cnx_t* cnx, uint8_t* ticket, uint16_t ticket_length) +{ + if (cnx->quic->F_log != NULL) { + cnx->quic->text_log_fns->log_picotls_ticket(cnx, ticket, ticket_length); + } + + if (cnx->f_binlog != NULL) { + cnx->quic->bin_log_fns->log_picotls_ticket(cnx, ticket, ticket_length); + } +} + +/* log the start of a connection */ +void picoquic_log_new_connection(picoquic_cnx_t* cnx) +{ + if (cnx->quic->F_log != NULL) { + cnx->quic->text_log_fns->log_new_connection(cnx); + } + + if (cnx->quic->bin_log_fns != NULL) { + cnx->quic->bin_log_fns->log_new_connection(cnx); + } +} +/* log the end of a connection */ +void picoquic_log_close_connection(picoquic_cnx_t* cnx) +{ + if (cnx->quic->F_log != NULL) { + cnx->quic->text_log_fns->log_close_connection(cnx); + } + + if (cnx->f_binlog != NULL) { + cnx->quic->bin_log_fns->log_close_connection(cnx); + } +} + +/* log congestion control parameters */ +void picoquic_log_cc_dump(picoquic_cnx_t* cnx, uint64_t current_time) +{ + if (picoquic_cnx_is_still_logging(cnx)) { + if (cnx->quic->F_log != NULL) { + cnx->quic->text_log_fns->log_cc_dump(cnx, current_time); + } + if (cnx->f_binlog != NULL) { + cnx->quic->bin_log_fns->log_cc_dump(cnx, current_time); + } + } +} \ No newline at end of file diff --git a/deps/picoquic/util.c b/deps/picoquic/util.c new file mode 100644 index 0000000..9ff7083 --- /dev/null +++ b/deps/picoquic/util.c @@ -0,0 +1,1132 @@ +/* +* Author: Christian Huitema +* Copyright (c) 2017, Private Octopus, Inc. +* All rights reserved. +* +* Permission to use, copy, modify, and distribute this software for any +* purpose with or without fee is hereby granted, provided that the above +* copyright notice and this permission notice appear in all copies. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL Private Octopus, Inc. BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/* clang-format off */ + +/* Simple set of utilities */ +#ifdef _WINDOWS +#define WIN32_LEAN_AND_MEAN +#include +#include +#include +#else +#include +#include +#include +#include +#include +#endif +#include "picoquic_internal.h" +#include +#include +#include +#include "picoquic_utils.h" + +/* clang-format on */ + +char* picoquic_string_create(const char* original, size_t len) +{ + size_t allocated = len + 1; + char * str = NULL; + + /* tests to protect against integer overflow */ + if (allocated > 0) { + str = (char*)malloc(allocated); + + if (str != NULL) { + if (original == NULL || len == 0) { + str[0] = 0; + } + else if (allocated > len) { + memcpy(str, original, len); + str[allocated - 1] = 0; + } + else { + /* This could happen only in case of integer overflow */ + free(str); + str = NULL; + } + } + } + + return str; +} + +char* picoquic_string_duplicate(const char* original) +{ + char* str = NULL; + + if (original != NULL) { + size_t len = strlen(original); + + str = picoquic_string_create(original, len); + } + + return str; +} + +char* picoquic_string_free(char* str) +{ + if (str != NULL) { + free(str); + } + + return NULL; +} + +char* picoquic_strip_endofline(char* buf, size_t bufmax, char const* line) +{ + for (size_t i = 0; i < bufmax; i++) { + int c = line[i]; + + if (c == 0 || c == '\r' || c == '\n') { + buf[i] = 0; + break; + } + else { + buf[i] = (char) c; + } + } + + buf[bufmax - 1] = 0; + return buf; +} + +static FILE* debug_out = NULL; +static int debug_suspended = 0; + +void debug_set_stream(FILE *F) +{ + debug_out = F; +} + +void debug_printf(const char* fmt, ...) +{ + if (debug_suspended == 0 && debug_out != NULL) { + va_list args; + va_start(args, fmt); + vfprintf(debug_out, fmt, args); + va_end(args); + } +} + +void debug_dump(const void * x, int len) +{ + if (debug_suspended == 0 && debug_out != NULL) { + uint8_t * bytes = (uint8_t *)x; + + for (int i = 0; i < len;) { + fprintf(debug_out, "%04x: ", (int)i); + + for (int j = 0; j < 16 && i < len; j++, i++) { + fprintf(debug_out, "%02x ", bytes[i]); + } + fprintf(debug_out, "\n"); + } + } +} + +void debug_printf_push_stream(FILE* f) +{ + if (debug_out) { + fprintf(stderr, "Nested err out not supported\n"); + exit(1); + } + debug_out = f; +} + +void debug_printf_pop_stream(void) +{ + if (debug_out == NULL) { + fprintf(stderr, "No current err out\n"); + exit(1); + } + debug_out = NULL; +} + +void debug_printf_suspend(void) +{ + debug_suspended = 1; +} + +void debug_printf_resume(void) +{ + debug_suspended = 0; +} + +int debug_printf_reset(int suspended) +{ + int ret = debug_suspended; + debug_suspended = suspended; + return ret; +} + +int picoquic_sprintf(char* buf, size_t buf_len, size_t * nb_chars, const char* fmt, ...) +{ + va_list args; + va_start(args, fmt); +#ifdef _WINDOWS + int res = vsnprintf_s(buf, buf_len, _TRUNCATE, fmt, args); +#else + int res = vsnprintf(buf, buf_len, fmt, args); +#endif + va_end(args); + + if (nb_chars != NULL) { + *nb_chars = res; + } + + // vsnprintf returns <0 for errors and >=0 for nb of characters required. + // We return 0 when printing was successful. + return res >= 0 ? ((size_t)res >= buf_len) : res; +} + +int picoquic_print_connection_id_hexa(char* buf, size_t buf_len, const picoquic_connection_id_t * cnxid) +{ + static const char hex_to_char[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; + if (buf_len < ((size_t)cnxid->id_len) * 2u + 1u) { + return -1; + } + + for (unsigned i = 0; i < cnxid->id_len; i++) { + buf[i * 2u] = hex_to_char[cnxid->id[i] >> 4]; + buf[i * 2u + 1u] = hex_to_char[cnxid->id[i] & 0x0f]; + } + + buf[cnxid->id_len * 2u] = 0; + + return 0; +} + +int picoquic_parse_hexa_digit(char x) { + int ret = -1; + + if (x >= '0' && x <= '9') { + ret = x - '0'; + } + else if (x >= 'A' && x <= 'F') { + ret = x - 'A' + 10; + } + else if (x >= 'a' && x <= 'f') { + ret = x - 'a' + 10; + } + + return ret; +} + +size_t picoquic_parse_hexa(char const * hex_input, size_t input_length, uint8_t * bin_output, size_t output_max) +{ + size_t ret = 0; + if (input_length > 0 && (input_length & 1) == 0 && 2*output_max >= input_length) { + size_t offset = 0; + + while (offset < input_length) { + int a = picoquic_parse_hexa_digit(hex_input[offset++]); + int b = picoquic_parse_hexa_digit(hex_input[offset++]); + + if (a < 0 || b < 0) { + ret = 0; + break; + } + else { + bin_output[ret++] = (uint8_t)((a << 4) | b); + } + } + } + + return ret; +} + +uint8_t picoquic_parse_connection_id_hexa(char const * hex_input, size_t input_length, picoquic_connection_id_t * cnx_id) +{ + memset(cnx_id, 0, sizeof(picoquic_connection_id_t)); + cnx_id->id_len = (uint8_t) picoquic_parse_hexa(hex_input, input_length, cnx_id->id, 18); + + if (cnx_id->id_len == 0) { + memset(cnx_id, 0, sizeof(picoquic_connection_id_t)); + } + + return (cnx_id->id_len); +} + +uint8_t picoquic_create_packet_header_cnxid_lengths(uint8_t dest_len, uint8_t srce_len) +{ + uint8_t ret; + + ret = (dest_len < 4) ? 0 : (dest_len - 3); + ret <<= 4; + ret |= (srce_len < 4) ? 0 : (srce_len - 3); + + return ret; +} + +uint8_t picoquic_format_connection_id(uint8_t* bytes, size_t bytes_max, picoquic_connection_id_t cnx_id) +{ + uint8_t copied = cnx_id.id_len; + if (copied > bytes_max || copied == 0) { + copied = 0; + } else { + memcpy(bytes, cnx_id.id, copied); + } + + return copied; +} + +uint8_t picoquic_parse_connection_id(const uint8_t * bytes, uint8_t len, picoquic_connection_id_t * cnx_id) +{ + if (len <= PICOQUIC_CONNECTION_ID_MAX_SIZE) { + cnx_id->id_len = len; + memcpy(cnx_id->id, bytes, len); + } else { + len = 0; + cnx_id->id_len = 0; + } + return len; +} + +const picoquic_connection_id_t picoquic_null_connection_id = { + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 0 }; + +int picoquic_is_connection_id_null(const picoquic_connection_id_t * cnx_id) +{ + return (cnx_id->id_len == 0) ? 1 : 0; +} + +int picoquic_compare_connection_id(const picoquic_connection_id_t * cnx_id1, const picoquic_connection_id_t * cnx_id2) +{ + int ret = -1; + + if (cnx_id1->id_len == cnx_id2->id_len) { + ret = memcmp(cnx_id1->id, cnx_id2->id, cnx_id1->id_len); + } + + return ret; +} + +/* Hash connection ids for picohash_table's */ +uint64_t picoquic_connection_id_hash(const picoquic_connection_id_t * cid) +{ + uint64_t val64 = 0; + + for (size_t i = 0; i < cid->id_len; i++) { + val64 += val64 << 8; + val64 += cid->id[i]; + } + + return val64; +} + +uint64_t picoquic_val64_connection_id(picoquic_connection_id_t cnx_id) +{ + uint64_t val64 = 0; + + if (cnx_id.id_len < 8) + { + for (size_t i = 0; i < cnx_id.id_len; i++) { + val64 <<= 8; + val64 |= cnx_id.id[i]; + } + for (size_t i = cnx_id.id_len; i < 8; i++) { + val64 <<= 8; + } + } else { + for (size_t i = 0; i < 8; i++) { + val64 <<= 8; + val64 |= cnx_id.id[i]; + } + } + + return val64; +} + +void picoquic_set64_connection_id(picoquic_connection_id_t * cnx_id, uint64_t val64) +{ + for (int i = 7; i >= 0; i--) { + cnx_id->id[i] = (uint8_t)(val64 & 0xFF); + val64 >>= 8; + } + for (size_t i = 8; i < sizeof(cnx_id->id); i++) { + cnx_id->id[i] = 0; + } + cnx_id->id_len = 8; +} + +uint64_t picoquic_hash_addr(const struct sockaddr* addr) +{ + uint64_t h; + + if (addr->sa_family == AF_INET) { + struct sockaddr_in* a4 = (struct sockaddr_in*)addr; + h = picohash_bytes((uint8_t*)&a4->sin_addr , 4); + h += 128ull * a4->sin_port; + } + else { + struct sockaddr_in6* a6 = (struct sockaddr_in6*)addr; + h = picohash_bytes((uint8_t*)& a6->sin6_addr, 16); + h += 128ull * a6->sin6_port; + } + + return h; +} + +int picoquic_compare_addr(const struct sockaddr * expected, const struct sockaddr * actual) +{ + int ret = -1; + + if (expected->sa_family == actual->sa_family) { + if (expected->sa_family == AF_INET) { + struct sockaddr_in * ex = (struct sockaddr_in *)expected; + struct sockaddr_in * ac = (struct sockaddr_in *)actual; + if (ex->sin_port == ac->sin_port && +#ifdef _WINDOWS + ex->sin_addr.S_un.S_addr == ac->sin_addr.S_un.S_addr) { +#else + ex->sin_addr.s_addr == ac->sin_addr.s_addr){ +#endif + ret = 0; + } + } else { + struct sockaddr_in6 * ex = (struct sockaddr_in6 *)expected; + struct sockaddr_in6 * ac = (struct sockaddr_in6 *)actual; + + + if (ex->sin6_port == ac->sin6_port && + memcmp(&ex->sin6_addr, &ac->sin6_addr, 16) == 0) { + ret = 0; + } + } + } + + return ret; +} + +int picoquic_addr_length(const struct sockaddr* addr) +{ + int len = 0; + if (addr->sa_family == AF_INET) { + len = (int)sizeof(struct sockaddr_in); + } else if (addr->sa_family == AF_INET6) { + len = (int)sizeof(struct sockaddr_in6); + } + return len; +} +/* Copy a sockaddr to a storage value, and return the copied address length */ +void picoquic_store_addr(struct sockaddr_storage * stored_addr, const struct sockaddr * addr) +{ + int len = 0; + + if (addr == NULL || (len = picoquic_addr_length(addr)) == 0) { + stored_addr->ss_family = 0; + } + else { + memcpy(stored_addr, addr, len); + } +} + +/* Return a pointer to the IP address and IP length in a sockaddr */ +void picoquic_get_ip_addr(struct sockaddr * addr, uint8_t ** ip_addr, uint8_t * ip_addr_len) +{ + if (addr->sa_family == AF_INET) { + *ip_addr = (uint8_t *)&((struct sockaddr_in *)addr)->sin_addr; + *ip_addr_len = 4; + } + else if(addr->sa_family == AF_INET6) { + *ip_addr = (uint8_t *)&((struct sockaddr_in6 *)addr)->sin6_addr; + *ip_addr_len = 16; + } + else { + *ip_addr = NULL; + *ip_addr_len = 0; + } +} + +/* Store a test address */ +int picoquic_store_text_addr(struct sockaddr_storage* stored_addr, const char* ip_address_text, uint16_t port) +{ + int ret = 0; + struct sockaddr_in* ipv4_addr = (struct sockaddr_in*)stored_addr; + struct sockaddr_in6* ipv6_addr = (struct sockaddr_in6*)stored_addr; + + /* get the IP address of the server */ + memset(stored_addr, 0, sizeof(struct sockaddr_storage)); + + if (inet_pton(AF_INET, ip_address_text, &ipv4_addr->sin_addr) == 1) { + /* Valid IPv4 address */ + ipv4_addr->sin_family = AF_INET; + ipv4_addr->sin_port = htons((unsigned short)port); + } + else if (inet_pton(AF_INET6, ip_address_text, &ipv6_addr->sin6_addr) == 1) { + /* Valid IPv6 address */ + ipv6_addr->sin6_family = AF_INET6; + ipv6_addr->sin6_port = htons((unsigned short)port); + } + else { + ret = -1; + } + + return ret; +} + +/* Get text string for address and port */ +char const* picoquic_addr_text(struct sockaddr* addr, char* text, size_t text_size) +{ + char addr_buffer[128]; + char const* addr_text; + char const* ret_text = "?:?"; + + switch (addr->sa_family) { + case AF_INET: + addr_text = inet_ntop(AF_INET, + (const void*)(&((struct sockaddr_in*)addr)->sin_addr), + addr_buffer, sizeof(addr_buffer)); + if (picoquic_sprintf(text, text_size, NULL, "%s:%d", addr_text, ((struct sockaddr_in*) addr)->sin_port) == 0) { + ret_text = text; + } + break; + case AF_INET6: + addr_text = inet_ntop(AF_INET6, + (const void*)(&((struct sockaddr_in6*)addr)->sin6_addr), + addr_buffer, sizeof(addr_buffer)); + if (picoquic_sprintf(text, text_size, NULL, "[%s]:%d", addr_text, ((struct sockaddr_in6*) addr)->sin6_port) == 0) { + ret_text = text; + } + default: + break; + } + + return ret_text; +} + +/* Return a directory path based on solution dir and file name */ +char const* picoquic_solution_dir = NULL; + +void picoquic_set_solution_dir(char const* solution_dir) +{ + picoquic_solution_dir = solution_dir; +} + +int picoquic_get_input_path(char * target_file_path, size_t file_path_max, const char * solution_path, const char * file_name) +{ + if (solution_path == NULL) { + solution_path = PICOQUIC_DEFAULT_SOLUTION_DIR; + } + + const char * separator = PICOQUIC_FILE_SEPARATOR; + size_t solution_path_length = strlen(solution_path); + if (solution_path_length != 0 && solution_path[solution_path_length - 1] == separator[0]) { + separator = ""; + } + + int ret = picoquic_sprintf(target_file_path, file_path_max, NULL, "%s%s%s", + solution_path, separator, file_name); + + return ret; +} + +/* Safely open files in a portable way */ +FILE * picoquic_file_open_ex(char const * file_name, char const * flags, int * last_err) +{ + FILE * F; + +#ifdef _WINDOWS + errno_t err = fopen_s(&F, file_name, flags); + if (err != 0){ + if (last_err != NULL) { + *last_err = err; + } + if (F != NULL) { + fclose(F); + F = NULL; + } + } +#else + F = fopen(file_name, flags); + if (F == NULL && last_err != NULL) { + *last_err = errno; + } +#endif + + return F; +} +FILE* picoquic_file_open(char const* file_name, char const* flags) +{ + return picoquic_file_open_ex(file_name, flags, NULL); +} + +/* Safely close files in a portable way */ +FILE * picoquic_file_close(FILE * F) +{ + if (F != NULL) { + (void)fclose(F); + } + return NULL; +} + +/* Safely delete file in a portable way */ +int picoquic_file_delete(char const * file_name, int * last_err) +{ + int ret; + +#ifdef _WINDOWS + ret = _unlink(file_name); + if (last_err != NULL && ret != 0) { + *last_err = errno; + } +#else + ret = unlink(file_name); + if (last_err != NULL && ret != 0) { + *last_err = errno; + } +#endif + return ret; +} + + /* Skip and decode function. + * These functions return NULL in case of a failure (insufficient buffer). + */ + +const uint8_t* picoquic_frames_fixed_skip(const uint8_t* bytes, const uint8_t* bytes_max, size_t size) +{ + return (bytes += size) <= bytes_max ? bytes : NULL; +} + + +const uint8_t* picoquic_frames_varint_skip(const uint8_t* bytes, const uint8_t* bytes_max) +{ + return bytes < bytes_max ? picoquic_frames_fixed_skip(bytes, bytes_max, VARINT_LEN(bytes)) : NULL; +} + + +/* Parse a varint. In case of an error, *n64 is unchanged, and NULL is returned */ +const uint8_t* picoquic_frames_varint_decode(const uint8_t* bytes, const uint8_t* bytes_max, uint64_t* n64) +{ + uint8_t length; + + if (bytes < bytes_max && bytes + (length = (uint8_t)VARINT_LEN(bytes)) <= bytes_max) { + uint64_t v = *bytes++ & 0x3F; + + while (--length > 0) { + v <<= 8; + v += *bytes++; + } + + *n64 = v; + } + else { + bytes = NULL; + } + + return bytes; +} + +const uint8_t* picoquic_frames_varlen_decode(const uint8_t* bytes, const uint8_t* bytes_max, size_t* n) +{ + uint64_t len = 0; + bytes = picoquic_frames_varint_decode(bytes, bytes_max, &len); + *n = (size_t)len; + return (*n == len) ? bytes : NULL; +} + +const uint8_t* picoquic_frames_uint8_decode(const uint8_t* bytes, const uint8_t* bytes_max, uint8_t* n) +{ + if (bytes < bytes_max) { + *n = *bytes++; + } + else { + bytes = NULL; + } + return bytes; +} + + +const uint8_t* picoquic_frames_uint16_decode(const uint8_t* bytes, const uint8_t* bytes_max, uint16_t* n) +{ + if (bytes + sizeof(*n) <= bytes_max) { + *n = PICOPARSE_16(bytes); + bytes += sizeof(*n); + } + else { + bytes = NULL; + } + return bytes; +} + +const uint8_t* picoquic_frames_uint32_decode(const uint8_t* bytes, const uint8_t* bytes_max, uint32_t* n) +{ + if (bytes + sizeof(*n) <= bytes_max) { + *n = PICOPARSE_32(bytes); + bytes += sizeof(*n); + } + else { + bytes = NULL; + } + return bytes; +} + +const uint8_t* picoquic_frames_uint64_decode(const uint8_t* bytes, const uint8_t* bytes_max, uint64_t* n) +{ + if (bytes + sizeof(*n) <= bytes_max) { + *n = PICOPARSE_64(bytes); + bytes += sizeof(*n); + } + else { + bytes = NULL; + } + return bytes; +} + +const uint8_t* picoquic_frames_length_data_skip(const uint8_t* bytes, const uint8_t* bytes_max) +{ + uint64_t length; + if ((bytes = picoquic_frames_varint_decode(bytes, bytes_max, &length)) != NULL) { + bytes = picoquic_frames_fixed_skip(bytes, bytes_max, (size_t)length); + } + return bytes; +} + +const uint8_t* picoquic_frames_cid_decode(const uint8_t* bytes, const uint8_t* bytes_max, picoquic_connection_id_t* cid) +{ + bytes = picoquic_frames_uint8_decode(bytes, bytes_max, &cid->id_len); + + if (cid->id_len > PICOQUIC_CONNECTION_ID_MAX_SIZE || + bytes + cid->id_len > bytes_max) { + bytes = NULL; + } + else { + memset(cid->id, 0, sizeof(cid->id)); + memcpy(cid->id, bytes, cid->id_len); + bytes += cid->id_len; + } + + return bytes; +} + + +/* Encoding functions of the form uint8_t * picoquic_frame_XXX_encode(uint8_t * bytes, uint8_t * bytes-max, ...) + */ +uint8_t* picoquic_frames_varint_encode(uint8_t* bytes, const uint8_t* bytes_max, uint64_t n64) +{ + if (n64 < 16384) { + if (n64 < 64) { + if (bytes + 1 <= bytes_max) { + *bytes++ = (uint8_t)(n64); + } + else { + bytes = NULL; + } + } + else { + if (bytes + 2 <= bytes_max) { + *bytes++ = (uint8_t)((n64 >> 8) | 0x40); + *bytes++ = (uint8_t)(n64); + } + else { + bytes = NULL; + } + } + } + else if (n64 < 1073741824) { + if (bytes + 4 <= bytes_max) { + *bytes++ = (uint8_t)((n64 >> 24) | 0x80); + *bytes++ = (uint8_t)(n64 >> 16); + *bytes++ = (uint8_t)(n64 >> 8); + *bytes++ = (uint8_t)(n64); + } + else { + bytes = NULL; + } + } + else { + if (bytes + 8 <= bytes_max) { + *bytes++ = (uint8_t)((n64 >> 56) | 0xC0); + *bytes++ = (uint8_t)(n64 >> 48); + *bytes++ = (uint8_t)(n64 >> 40); + *bytes++ = (uint8_t)(n64 >> 32); + *bytes++ = (uint8_t)(n64 >> 24); + *bytes++ = (uint8_t)(n64 >> 16); + *bytes++ = (uint8_t)(n64 >> 8); + *bytes++ = (uint8_t)(n64); + } + else { + bytes = NULL; + } + } + + return bytes; +} + +uint8_t* picoquic_frames_varlen_encode(uint8_t* bytes, const uint8_t* bytes_max, size_t n) +{ + return picoquic_frames_varint_encode(bytes, bytes_max, n); +} + +uint8_t* picoquic_frames_uint8_encode(uint8_t* bytes, const uint8_t* bytes_max, uint8_t n) +{ + if (bytes + sizeof(n) > bytes_max) { + bytes = NULL; + } + else { + *bytes++ = n; + } + + return (bytes); +} + +uint8_t* picoquic_frames_uint16_encode(uint8_t* bytes, const uint8_t* bytes_max, uint16_t n) +{ + if (bytes + sizeof(n) > bytes_max) { + bytes = NULL; + } + else { + *bytes++ = (uint8_t)(n >> 8); + *bytes++ = (uint8_t)n; + } + return (bytes); +} + +uint8_t* picoquic_frames_uint24_encode(uint8_t* bytes, const uint8_t* bytes_max, uint32_t n) +{ + if (bytes + 3 > bytes_max) { + bytes = NULL; + } + else { + *bytes++ = (uint8_t)(n >> 16); + *bytes++ = (uint8_t)(n >> 8); + *bytes++ = (uint8_t)n; + } + return (bytes); +} + +uint8_t* picoquic_frames_uint32_encode(uint8_t* bytes, const uint8_t* bytes_max, uint32_t n) +{ + if (bytes + sizeof(n) > bytes_max) { + bytes = NULL; + } + else { + *bytes++ = (uint8_t)(n >> 24); + *bytes++ = (uint8_t)(n >> 16); + *bytes++ = (uint8_t)(n >> 8); + *bytes++ = (uint8_t)n; + } + return (bytes); +} + +uint8_t* picoquic_frames_uint64_encode(uint8_t* bytes, const uint8_t* bytes_max, uint64_t n) +{ + if (bytes + sizeof(n) > bytes_max) { + bytes = NULL; + } + else { + *bytes++ = (uint8_t)(n >> 56); + *bytes++ = (uint8_t)(n >> 48); + *bytes++ = (uint8_t)(n >> 40); + *bytes++ = (uint8_t)(n >> 32); + *bytes++ = (uint8_t)(n >> 24); + *bytes++ = (uint8_t)(n >> 16); + *bytes++ = (uint8_t)(n >> 8); + *bytes++ = (uint8_t)n; + } + return (bytes); + +} + +uint8_t* picoquic_frames_length_data_encode(uint8_t* bytes, const uint8_t* bytes_max, size_t l, const uint8_t* v) +{ + if ((bytes = picoquic_frames_varlen_encode(bytes, bytes_max, l)) != NULL && + (bytes + l) <= bytes_max) { + memcpy(bytes, v, l); + bytes += l; + } + else { + bytes = NULL; + } + + return bytes; +} + +uint8_t* picoquic_frames_cid_encode(uint8_t* bytes, const uint8_t* bytes_max, const picoquic_connection_id_t* cid) +{ + return picoquic_frames_length_data_encode(bytes, bytes_max, cid->id_len, cid->id); +} + +/* Constant time memory comparison. This is only required now for + * the comparison of 16 bytes long stateless reset secrets, so we have + * only minimal requriements for performance, and privilege portability. + * + * Returns uint64_t value so the code works for arbitrary length. + * Value is zero if strings match. + */ + +int picoquic_constant_time_memcmp(const uint8_t* x, const uint8_t* y, size_t l) +{ + uint64_t ret = 0; + + while (l > 0) { + ret += (*x++ ^ *y++); + l--; + } + + return (ret == 0)?0:-1; +} + +/* Minimal support for threads. + */ + +#ifndef _WINDOWS +static void picoquic_set_abs_delay(struct timespec* ts, uint64_t microsec_wait) { + clock_gettime(CLOCK_REALTIME, ts); + ts->tv_sec += (unsigned long)(microsec_wait / 1000000); + ts->tv_nsec += (unsigned long)((microsec_wait % 1000000)*1000); + if (ts->tv_nsec > 1000000000) { + ts->tv_sec++; + ts->tv_nsec -= 1000000000; + } +} +#endif + +int picoquic_create_thread(picoquic_thread_t * thread, picoquic_thread_fn thread_fn, void * arg) +{ +#ifdef _WINDOWS + int ret = 0; + *thread = CreateThread(NULL, 0, thread_fn, arg, 0, NULL); + if (*thread == NULL) { + ret = GetLastError(); + } +#else + int ret = pthread_create(thread, NULL, thread_fn, arg); +#endif + return ret; +} + +void picoquic_delete_thread(picoquic_thread_t * thread) +{ +#ifdef _WINDOWS + /* Wait until background thread has terminated, or timeout in milliseconds */ + if (WaitForMultipleObjects(1, thread, TRUE, 1000) == WAIT_TIMEOUT) { + /* if soft wait fails, then hard cancel */ + TerminateThread(*thread, 0); + } + /* Close the thread handle */ + CloseHandle(*thread); + *thread = NULL; +#else + if (pthread_join(*thread, NULL) != 0) { + (void)pthread_cancel(*thread); + } +#endif +} + +int picoquic_create_mutex(picoquic_mutex_t * mutex) +{ +#ifdef _WINDOWS + int ret = 0; + *mutex = CreateMutex(NULL, FALSE, NULL); + if (*mutex == NULL) { + ret = -1; + } +#else + int ret = pthread_mutex_init(mutex, NULL); +#endif + return ret; +} + +int picoquic_delete_mutex(picoquic_mutex_t* mutex) +{ +#ifdef _WINDOWS + int ret = 0; + CloseHandle(*mutex); + *mutex = NULL; +#else + int ret = pthread_mutex_destroy(mutex); +#endif + return ret; +} + +int picoquic_lock_mutex(picoquic_mutex_t * mutex) +{ +#ifdef _WINDOWS + int ret = 0; + DWORD w_ret = WaitForSingleObject(*mutex, INFINITE); + if (w_ret != WAIT_OBJECT_0) { + ret = -1; + } +#else + int ret = pthread_mutex_lock(mutex); +#endif + return ret; +} + +int picoquic_unlock_mutex(picoquic_mutex_t * mutex) +{ +#ifdef _WINDOWS + int ret = 0; + if (!ReleaseMutex(*mutex)) { + ret = -1; + } +#else + int ret = pthread_mutex_unlock(mutex); +#endif + return ret; +} + +int picoquic_create_event(picoquic_event_t* event) +{ +#ifdef _WINDOWS + int ret = 0; + *event = CreateEvent(NULL, TRUE, FALSE, NULL); + if (*event == NULL) { + ret = -1; + } +#else + int ret; + + memset(event, 0, sizeof(picoquic_event_t)); + ret = pthread_mutex_init(&event->mutex, NULL); + if (ret == 0) { + ret = pthread_cond_init(&event->cond, NULL); + } +#endif + return ret; +} + +void picoquic_delete_event(picoquic_event_t* event) +{ +#ifdef _WINDOWS + CloseHandle(*event); + *event = NULL; +#else + (void)pthread_mutex_destroy(&event->mutex); + (void)pthread_cond_destroy(&event->cond); + memset(event, 0, sizeof(picoquic_event_t)); +#endif +} + +int picoquic_signal_event(picoquic_event_t* event) +{ +#ifdef _WINDOWS + int ret = 0; + if (!SetEvent(*event)){ + ret = -1; + } +#else + int ret; + (void)pthread_mutex_lock(&event->mutex); + ret = pthread_cond_broadcast(&event->cond); + (void)pthread_mutex_unlock(&event->mutex); +#endif + return ret; +} + +int picoquic_wait_for_event(picoquic_event_t* event, uint64_t microsec_wait) +{ +#ifdef _WINDOWS + int ret = 0; + DWORD dwWaitResult; + DWORD millisec_wait = (microsec_wait == UINT64_MAX) ? INFINITE : (DWORD)(microsec_wait / 1000); + dwWaitResult = WaitForSingleObject(*event, millisec_wait); + if (dwWaitResult == WAIT_OBJECT_0) { + (void)ResetEvent(*event); + } else { + ret = -1; + } +#else + int ret; + (void)pthread_mutex_lock(&event->mutex); + if (microsec_wait == UINT64_MAX) { + ret = pthread_cond_wait(&event->cond, &event->mutex); + } + else { + struct timespec abstime; + picoquic_set_abs_delay(&abstime, microsec_wait); + ret = pthread_cond_timedwait(&event->cond, &event->mutex, &abstime); + } + (void)pthread_mutex_unlock(&event->mutex); +#endif + return ret; +} + + +/* Pseudo random generation suitable for tests. Guaranties that the +* same seed will produce the same sequence, allows for specific +* random sequence for a given test. +* Adapted from http://xoroshiro.di.unimi.it/splitmix64.c, +* Written in 2015 by Sebastiano Vigna (vigna@acm.org) */ + +uint64_t picoquic_test_random(uint64_t* random_context) +{ + uint64_t z; + *random_context += 0x9e3779b97f4a7c15; + z = *random_context; + z = (z ^ (z >> 30)) * 0xbf58476d1ce4e5b9ull; + z = (z ^ (z >> 27)) * 0x94d049bb133111ebull; + return z ^ (z >> 31); +} + +void picoquic_test_random_bytes(uint64_t* random_context, uint8_t* bytes, size_t bytes_max) +{ + size_t byte_index = 0; + + while (byte_index < bytes_max) { + uint64_t v = picoquic_test_random(random_context); + + for (int i = 0; i < 8 && byte_index < bytes_max; i++) { + bytes[byte_index++] = v & 0xFF; + v >>= 8; + } + } +} + +uint64_t picoquic_test_uniform_random(uint64_t* random_context, uint64_t rnd_max) +{ + uint64_t rnd = 0; + + if (rnd_max > 0) { + uint64_t rnd_min = ((uint64_t)((int64_t)-1)) % rnd_max; + + do { + rnd = picoquic_test_random(random_context); + } while (rnd < rnd_min); + rnd %= rnd_max; + } + + return rnd; +} + +double picoquic_test_gauss_random(uint64_t* random_context) +{ + double dx = 0; + + /* Sum of 12 variables in [0..1], provides + * average = 6.0, stdev = 3.0 */ + for (int i = 0; i < 12; i++) { + double d; + uint64_t r = picoquic_test_random(random_context); + r ^= r >> 17; + r ^= r >> 34; + d = (double)(r & 0x1ffff) + 0.5; + d /= (double)(0x20000); + dx += d; + } + + dx -= 6.0; + + return dx; +} diff --git a/deps/picoquic/wincompat.h b/deps/picoquic/wincompat.h new file mode 100644 index 0000000..f57d866 --- /dev/null +++ b/deps/picoquic/wincompat.h @@ -0,0 +1,53 @@ +/* +* Author: Christian Huitema +* Copyright (c) 2017, Private Octopus, Inc. +* All rights reserved. +* +* Permission to use, copy, modify, and distribute this software for any +* purpose with or without fee is hereby granted, provided that the above +* copyright notice and this permission notice appear in all copies. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL Private Octopus, Inc. BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef WINCOMPAT_H +#define WINCOMPAT_H + +#include +#define ssize_t int +#include + +#ifndef gettimeofday +#define gettimeofday wintimeofday + +#ifndef __attribute__ +#define __attribute__(X) +#endif + +#ifdef __cplusplus +extern "C" { +#endif + + struct timezone { + int tz_minuteswest; /* minutes west of Greenwich */ + int tz_dsttime; /* type of DST correction */ + }; + + int wintimeofday(struct timeval* tv, struct timezone* tz); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif + +#endif /* WINCOMPAT_H */ \ No newline at end of file diff --git a/deps/picoquic/winsockloop.c b/deps/picoquic/winsockloop.c new file mode 100644 index 0000000..507a205 --- /dev/null +++ b/deps/picoquic/winsockloop.c @@ -0,0 +1,755 @@ +/* +* Author: Christian Huitema +* Copyright (c) 2020, Private Octopus, Inc. +* All rights reserved. +* +* Permission to use, copy, modify, and distribute this software for any +* purpose with or without fee is hereby granted, provided that the above +* copyright notice and this permission notice appear in all copies. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL Private Octopus, Inc. BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/* Socket loop implements the "wait for messages" loop common to most servers + * and many clients. + * + * Second step: support simple servers and simple client. + * + * The "call loop back" function is called: when ready, after receiving, and after sending. The + * loop will terminate if the callback return code is not zero -- except for special processing + * of the migration testing code. + * TODO: in Windows, use WSA asynchronous calls instead of sendmsg, allowing for multiple parallel sends. + * TODO: in Linux, use multiple send per call API + * TDOO: trim the #define list. + * TODO: support the QuicDoq scenario, manage extra socket. + */ + +#ifndef _WINDOWS +#include "picoquic_packet_loop.h" + +int picoquic_packet_loop_win(picoquic_quic_t* quic, + int local_port, + int local_af, + int dest_if, + picoquic_packet_loop_cb_fn loop_callback, + void* loop_callback_ctx) +{ + return -1; +} +#else +#define WIN32_LEAN_AND_MEAN +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef SOCKET_TYPE +#define SOCKET_TYPE SOCKET +#endif +#ifndef SOCKET_CLOSE +#define SOCKET_CLOSE(x) closesocket(x) +#endif +#ifndef WSA_LAST_ERROR +#define WSA_LAST_ERROR(x) WSAGetLastError() +#endif +#ifndef socklen_t +#define socklen_t int +#endif + +#include "picosocks.h" +#include "picoquic.h" +#include "picoquic_internal.h" +#include "picoquic_packet_loop.h" +#include "picoquic_unified_log.h" + +#if 1 + /* Test support for UDP coalescing */ +void picoquic_socks_win_coalescing_test(int * recv_coalesced, int * send_coalesced) +{ + int ret; + DWORD option_value; + int option_length; + int last_error; + + *recv_coalesced = 0; + *send_coalesced = 0; + + SOCKET_TYPE fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + + if (fd != INVALID_SOCKET) { +#ifdef UDP_SEND_MSG_SIZE + option_length = (int)sizeof(option_value); + + if ((ret = getsockopt(fd, IPPROTO_UDP, UDP_SEND_MSG_SIZE, (char *)&option_value, &option_length)) != 0) { + last_error = GetLastError(); + DBG_PRINTF("UDP_SEND_MSG_SIZE not supported, returns %d (%d)", ret, last_error); + } + else { + *send_coalesced = 1; + } +#endif +#ifdef UDP_RECV_MAX_COALESCED_SIZE + option_value = 1; + option_length = (int)sizeof(option_value); + if ((ret = getsockopt(fd, IPPROTO_UDP, UDP_RECV_MAX_COALESCED_SIZE, (char*)&option_value, &option_length)) != 0) { + last_error = GetLastError(); + DBG_PRINTF("UDP_RECV_MAX_COALESCED_SIZE not supported, returns %d (%d)", ret, last_error); + } + else { + *recv_coalesced = 1; + } +#endif + closesocket(fd); + } +} +#endif + +/* Open a set of sockets in asynch mode. */ +int picoquic_packet_loop_open_sockets_win(int local_port, int local_af, + picoquic_recvmsg_async_ctx_t** sock_ctx, int * sock_af, HANDLE * events, + int nb_sockets_max) +{ + int ret = 0; + int nb_sockets = (local_af == AF_UNSPEC) ? 2 : 1; + int recv_coalesced; + int send_coalesced; + + /* Assess whether coalescing is supported */ + picoquic_socks_win_coalescing_test(&recv_coalesced, &send_coalesced); + + /* Compute how many sockets are necessary */ + if (nb_sockets > nb_sockets_max) { + DBG_PRINTF("Cannot open %d sockets, max set to %d\n", nb_sockets, nb_sockets_max); + nb_sockets = 0; + } else if (local_af == AF_UNSPEC) { + sock_af[0] = AF_INET; + sock_af[1] = AF_INET6; + } + else if (local_af == AF_INET || local_af == AF_INET6) { + sock_af[0] = local_af; + } + else { + DBG_PRINTF("Cannot open socket(AF=%d), unsupported AF\n", local_af); + nb_sockets = 0; + } + + for (int i = 0; i < nb_sockets; i++) { + int recv_set = 0; + int send_set = 0; + + sock_ctx[i] = picoquic_create_async_socket(sock_af[i], recv_coalesced, send_coalesced); + if (sock_ctx[i] == NULL) { + ret = -1; + events[i] = NULL; + } + else { + if (picoquic_socket_set_ecn_options(sock_ctx[i]->fd, sock_af[i], &recv_set, &send_set) != 0 || + picoquic_socket_set_pkt_info(sock_ctx[i]->fd, sock_af[i]) != 0 || + (local_port != 0 && picoquic_bind_to_port(sock_ctx[i]->fd, sock_af[i], local_port) != 0)){ + DBG_PRINTF("Cannot set socket (af=%d, port = %d)\n", sock_af[i], local_port); + for (int j = 0; j < i; j++) { + if (sock_ctx[i] != NULL) { + picoquic_delete_async_socket(sock_ctx[i]); + sock_ctx[i] = NULL; + + } + } + nb_sockets = 0; + break; + } + else { + events[i] = sock_ctx[i]->overlap.hEvent; + } + } + } + + return nb_sockets; +} + +/* Async version of Sendto. + * The goal is to use WSASendMsg in asynchronous mode. + * Each send operation operates with a buffer of type picoquic_sendmsg_ctx_t, + * with the following steps: + * 1) A call to picoquic_prepare_next_packet fills data buffer, addresses, etc. + * 2) A call to picooquic_sendmsg_start formats the sendmsg parameters and + * calls sendmsg in asynchronous mode. + * 3) When the send completes, the completion routine fills number of + * bytes sent, error codes, etc. + * 4) The send loop verifies that the previous send is complete before + * reusing the buffer + * The step (4) is the most tricky. We envisage that the application will + * manage several buffers so as to be able to send several packets in a + * batch. We can manage the list of buffer as a heap, with the first sent + * packet on top. If that packet is available (send complete or not yet sent) + * the loop picks it, use it, and chains it at the end of the list. If + * the list is long enough, there should never be a need to wait. When that + * happens, the application has to actively wait for the completion, + * maybe by running another "wait for receive or return immediately". + */ + +typedef struct st_picoquic_sendmsg_ctx_t { + WSAOVERLAPPED overlap; + struct st_picoquic_sendmsg_ctx_t* next; + WSABUF dataBuf; + WSAMSG msg; + char cmsg_buffer[1024]; + uint8_t * send_buffer; + size_t send_buffer_size; + struct sockaddr_storage addr_from; + struct sockaddr_storage addr_dest; + socklen_t from_length; + socklen_t dest_length; + int dest_if; + size_t send_length; + size_t send_msg_size; + int bytes_sent; + int ret; + int last_err; + GUID WSASendMsg_GUID; + unsigned int is_started:1; + unsigned int is_complete:1; +} picoquic_sendmsg_ctx_t; + +void CALLBACK picoquic_sendmsg_complete_cb( + IN DWORD dwError, + IN DWORD cbTransferred, + IN LPWSAOVERLAPPED lpOverlapped, + IN DWORD dwFlags +) +{ + picoquic_sendmsg_ctx_t* send_ctx = (picoquic_sendmsg_ctx_t*) + (((uint8_t*)lpOverlapped) - offsetof(struct st_picoquic_sendmsg_ctx_t, overlap)); + send_ctx->bytes_sent = (int)cbTransferred; + send_ctx->ret = dwError; + send_ctx->last_err = (dwError == 0) ? 0 : GetLastError(); + send_ctx->is_complete = 1; +} + +int picoquic_sendmsg_start(picoquic_recvmsg_async_ctx_t* sock_ctx, picoquic_sendmsg_ctx_t* send_ctx) +{ + int ret = 0; + DWORD numberOfBytesSent = 0; + int should_retry; + + do { + should_retry = 0; + + /* Format the message header */ + send_ctx->is_started = 1; + memset(&send_ctx->msg, 0, sizeof(send_ctx->msg)); + send_ctx->msg.name = (struct sockaddr*) & send_ctx->addr_dest; + send_ctx->msg.namelen = picoquic_addr_length((struct sockaddr*) & send_ctx->addr_dest); + send_ctx->dataBuf.buf = (char*)send_ctx->send_buffer; + send_ctx->dataBuf.len = (ULONG)send_ctx->send_length; + send_ctx->msg.lpBuffers = &send_ctx->dataBuf; + send_ctx->msg.dwBufferCount = 1; + send_ctx->msg.Control.buf = (char*)send_ctx->cmsg_buffer; + send_ctx->msg.Control.len = sizeof(send_ctx->cmsg_buffer); + + /* Format the control message */ + picoquic_socks_cmsg_format(&send_ctx->msg, send_ctx->send_length, send_ctx->send_msg_size, + (struct sockaddr*) & send_ctx->addr_from, send_ctx->dest_if); + + /* Send the message */ + /* TODO: allo for immediate termination. */ + ret = sock_ctx->WSASendMsg(sock_ctx->fd, &send_ctx->msg, 0, &numberOfBytesSent, &send_ctx->overlap, picoquic_sendmsg_complete_cb); + if (ret != 0) { + DWORD last_err = WSAGetLastError(); + if (last_err == WSA_IO_PENDING) { + ret = 0; + } + else if (last_err == WSAECONNRESET) { + should_retry = 1; + ret = 0; + } + else { + send_ctx->last_err = WSAGetLastError(); + send_ctx->ret = ret; + } + } + else { + /* Immediate completion */ + send_ctx->bytes_sent = (int)numberOfBytesSent; + send_ctx->ret = 0; + send_ctx->last_err = 0; + send_ctx->is_complete = 1; + } + } while (should_retry); + + return ret; +} + +picoquic_sendmsg_ctx_t* picoquic_socks_create_send_ctx(size_t send_buffer_size) +{ + picoquic_sendmsg_ctx_t* send_ctx = (picoquic_sendmsg_ctx_t*)malloc(sizeof(picoquic_sendmsg_ctx_t)); + if (send_ctx == NULL) { + DBG_PRINTF("Cannot allocate send ctx (%x)", send_ctx); + } + else { + uint8_t* send_buffer = (uint8_t*)malloc(send_buffer_size); + if (send_buffer == NULL) { + DBG_PRINTF("Cannot allocate send buffer (%x) size %zu", + send_buffer, send_buffer); + free(send_ctx); + send_ctx = NULL; + } + else { + memset(send_ctx, 0, sizeof(picoquic_sendmsg_ctx_t)); + send_ctx->send_buffer = send_buffer; + send_ctx->send_buffer_size = send_buffer_size; + } + } + + return send_ctx; +} + +int picoquic_socks_create_send_ctx_list(int nb_ctx, size_t send_buffer_size, + picoquic_sendmsg_ctx_t** p_send_ctx_first, picoquic_sendmsg_ctx_t** p_send_ctx_last) +{ + int ret = 0; + + for (int i = 0; i < nb_ctx; i++) { + picoquic_sendmsg_ctx_t* send_ctx = picoquic_socks_create_send_ctx(send_buffer_size); + + if (send_ctx == NULL) { + ret = -1; + break; + } + else { + if (*p_send_ctx_last == NULL) { + *p_send_ctx_last = send_ctx; + } + send_ctx->next = *p_send_ctx_first; + *p_send_ctx_first = send_ctx; + } + } + + return ret; +} + +void picoquic_socks_delete_send_ctx(picoquic_sendmsg_ctx_t* send_ctx) +{ + if (send_ctx != NULL) { + if (send_ctx->send_buffer != NULL) { + free(send_ctx->send_buffer); + } + free(send_ctx); + } +} + +void picoquic_socks_delete_send_ctx_list(picoquic_sendmsg_ctx_t** p_send_ctx_first, picoquic_sendmsg_ctx_t** p_send_ctx_last) +{ + while (*p_send_ctx_first != NULL) { + picoquic_sendmsg_ctx_t* send_ctx = *p_send_ctx_first; + *p_send_ctx_first = send_ctx->next; + picoquic_socks_delete_send_ctx(send_ctx); + } + *p_send_ctx_last = NULL; +} + +/* Specialized packet loop using Windows sockets. + */ + +int picoquic_packet_loop_win(picoquic_quic_t* quic, + int local_port, + int local_af, + int dest_if, + picoquic_packet_loop_cb_fn loop_callback, + void* loop_callback_ctx) +{ + int ret = 0; + uint64_t current_time = picoquic_get_quic_time(quic); + int64_t delay_max = 10000000; + picoquic_connection_id_t log_cid; + picoquic_recvmsg_async_ctx_t* sock_ctx[PICOQUIC_PACKET_LOOP_SOCKETS_MAX]; + int sock_af[PICOQUIC_PACKET_LOOP_SOCKETS_MAX]; + HANDLE events[PICOQUIC_PACKET_LOOP_SOCKETS_MAX]; + int nb_sockets = 0; + uint16_t socket_port = (uint16_t)local_port; + int testing_migration = 0; /* Hook for the migration test */ + uint16_t next_port = 0; /* Data for the migration test */ + picoquic_cnx_t* last_cnx = NULL; + picoquic_sendmsg_ctx_t* send_ctx_first = NULL; + picoquic_sendmsg_ctx_t* send_ctx_last = NULL; + WSADATA wsaData = { 0 }; + (void)WSA_START(MAKEWORD(2, 2), &wsaData); + memset(sock_af, 0, sizeof(sock_af)); + + + /* Open the sockets */ + if ((nb_sockets = picoquic_packet_loop_open_sockets_win( + local_port, local_af, sock_ctx, sock_af, events, PICOQUIC_PACKET_LOOP_SOCKETS_MAX)) == 0) { + ret = PICOQUIC_ERROR_UNEXPECTED_ERROR; + } + else if (loop_callback != NULL) { + ret = loop_callback(quic, picoquic_packet_loop_ready, loop_callback_ctx); + } + + + /* Create a list of contexts for sending packets */ + if (ret == 0) { + size_t send_buffer_size = PICOQUIC_MAX_PACKET_SIZE; + if (sock_ctx[0]->supports_udp_send_coalesced) { + send_buffer_size *= 10; + } + ret = picoquic_socks_create_send_ctx_list(PICOQUIC_PACKET_LOOP_SEND_MAX, send_buffer_size, + &send_ctx_first, &send_ctx_last); + } + + + /* If the socket is not already bound, need to send a first packet to commit the port number */ + if (ret == 0 && local_port == 0) { + uint8_t send_buffer[1536]; + size_t send_length = 0; + struct sockaddr_storage peer_addr; + struct sockaddr_storage local_addr; + int if_index = dest_if; + int sock_ret = 0; + int sock_err = 0; + + ret = picoquic_prepare_next_packet(quic, current_time, + send_buffer, sizeof(send_buffer), &send_length, + &peer_addr, &local_addr, &if_index, &log_cid, &last_cnx); + + if (ret == 0 && send_length > 0) { + SOCKET_TYPE send_socket = sock_ctx[0]->fd; + + sock_ret = picoquic_send_through_socket(send_socket, + (struct sockaddr*) & peer_addr, (struct sockaddr*) & local_addr, if_index, + (const char*)send_buffer, (int)send_length, &sock_err); + + if (sock_ret <= 0) { + if (last_cnx == NULL) { + picoquic_log_context_free_app_message(quic, &log_cid, "Could not send message to AF_to=%d, AF_from=%d, if=%d, ret=%d, err=%d", + peer_addr.ss_family, local_addr.ss_family, if_index, sock_ret, sock_err); + } + else { + picoquic_log_app_message(last_cnx, "Could not send message to AF_to=%d, AF_from=%d, if=%d, ret=%d, err=%d", + peer_addr.ss_family, local_addr.ss_family, if_index, sock_ret, sock_err); + } + } + else { + struct sockaddr_storage local_address; + if (picoquic_get_local_address(sock_ctx[0]->fd, &local_address) != 0) { + memset(&local_address, 0, sizeof(struct sockaddr_storage)); + fprintf(stderr, "Could not read local address.\n"); + } + else if (local_address.ss_family == AF_INET6) { + socket_port = ((struct sockaddr_in6*) & local_address)->sin6_port; + } + else if (local_address.ss_family == AF_INET) { + socket_port = ((struct sockaddr_in*) & local_address)->sin_port; + } + else { + DBG_PRINTF("Invalid local socket family: %d ", local_address.ss_family); + ret = -1; + } + } + } + else { + DBG_PRINTF("%s", "No first packet prepared, cannot run the loop."); + ret = -1; + } + } + + /* If the socket is already bound, start asynch receive */ + if (ret == 0) { + for (int i = 0; i < nb_sockets; i++) { + if (!sock_ctx[i]->is_started) { + sock_ctx[i]->is_started = 1; + ret = picoquic_recvmsg_async_start(sock_ctx[i]); + if (ret == -1) { + DBG_PRINTF("%s", "Cannot start async recv"); + } + } + } + } + + while (ret == 0) { + int socket_rank = -1; + int64_t delta_t = picoquic_get_next_wake_delay(quic, current_time, delay_max); + DWORD delta_t_ms = (delta_t < 0)?0:(DWORD)(delta_t / 1000); + DWORD ret_event = WSAWaitForMultipleEvents(nb_sockets, events, FALSE, delta_t_ms, TRUE); + current_time = picoquic_get_quic_time(quic); + + if (ret_event == WSA_WAIT_FAILED) { + DBG_PRINTF("WSAWaitForMultipleEvents fails, error 0x%x", WSAGetLastError()); + ret = -1; + } else if (ret_event >= WSA_WAIT_EVENT_0) { + socket_rank = ret_event - WSA_WAIT_EVENT_0; + /* if received data on a socket, process it. */ + if (socket_rank < nb_sockets) { + /* Received data on socket i */ + ret = picoquic_recvmsg_async_finish(sock_ctx[socket_rank]); + ResetEvent(sock_ctx[socket_rank]->overlap.hEvent); + + if (ret != 0) { + DBG_PRINTF("%s", "Cannot finish async recv"); + } + else + { + if (sock_ctx[socket_rank]->bytes_recv > 0) { + /* Document incoming port. By default, there is just one port in use. + * But we also have special code for supporting migration tests, which requires + * a second socket with a different port number. + */ + uint16_t current_recv_port = socket_port; + int recv_bytes = 0; + + if (testing_migration) { + if (socket_rank == 0) { + current_recv_port = socket_port; + } + else { + current_recv_port = next_port; + } + } + + if (sock_ctx[socket_rank]->addr_dest.ss_family == AF_INET6) { + ((struct sockaddr_in6*) & sock_ctx[socket_rank]->addr_dest)->sin6_port = current_recv_port; + } + else if (sock_ctx[socket_rank]->addr_dest.ss_family == AF_INET) { + ((struct sockaddr_in*) & sock_ctx[socket_rank]->addr_dest)->sin_port = current_recv_port; + } + + while (recv_bytes < sock_ctx[socket_rank]->bytes_recv) { + size_t recv_length = (sock_ctx[socket_rank]->bytes_recv - recv_bytes); + + if (sock_ctx[socket_rank]->udp_coalesced_size > 0 && + recv_length > sock_ctx[socket_rank]->udp_coalesced_size){ + recv_length = sock_ctx[socket_rank]->udp_coalesced_size; + } + + /* Submit the packet to the client */ + ret = picoquic_incoming_packet(quic, sock_ctx[socket_rank]->recv_buffer + recv_bytes, + recv_length, (struct sockaddr*) & sock_ctx[socket_rank]->addr_from, + (struct sockaddr*) & sock_ctx[socket_rank]->addr_dest, sock_ctx[socket_rank]->dest_if, + sock_ctx[socket_rank]->received_ecn, current_time); + recv_bytes += (int)recv_length; + } + } + + if (ret == 0) { + /* Restart waiting for packets on the socket */ + ret = picoquic_recvmsg_async_start(sock_ctx[socket_rank]); + if (ret == -1) { + DBG_PRINTF("%s", "Cannot re-start async recv"); + } + } + else { + DBG_PRINTF("Packet processing error\r\n"); + } + + if (ret == 0 && loop_callback != NULL) { + ret = loop_callback(quic, picoquic_packet_loop_after_receive, loop_callback_ctx); + } + } + } + else { + /* Receive timer */ + ret = 0; + } + + /* Send packets that are now ready */ + /* TODO: manage asynch send. */ + if (ret == 0 && (!send_ctx_first->is_started || send_ctx_first->is_complete)) { + do { + picoquic_recvmsg_async_ctx_t* sock_ctx_send = NULL; + picoquic_sendmsg_ctx_t* send_ctx = send_ctx_first; + + if (send_ctx_first->is_started && send_ctx_first->is_complete) { + if (send_ctx->ret != 0) { + if (last_cnx == NULL) { + picoquic_log_context_free_app_message(quic, &log_cid, + "Could not send message to AF_to=%d, AF_from=%d, if=%d, ret=%d, err=%d", + send_ctx->addr_dest.ss_family, send_ctx->addr_from.ss_family, send_ctx->dest_if, + send_ctx->ret, send_ctx->last_err); + } + else { + picoquic_log_app_message(last_cnx, + "Could not send message to AF_to=%d, AF_from=%d, if=%d, ret=%d, err=%d", + send_ctx->addr_dest.ss_family, send_ctx->addr_from.ss_family, send_ctx->dest_if, + send_ctx->ret, send_ctx->last_err); + } + } + } + memset(&send_ctx->overlap, 0, sizeof(send_ctx->overlap)); + send_ctx->is_started = 0; + send_ctx->is_complete = 0; + send_ctx->last_err = 0; + send_ctx->ret = 0; + send_ctx->send_msg_size = 0; + + ret = picoquic_prepare_next_packet_ex(quic, current_time, + send_ctx->send_buffer, send_ctx->send_buffer_size, &send_ctx->send_length, + &send_ctx->addr_dest, &send_ctx->addr_from, &send_ctx->dest_if, &log_cid, &last_cnx, + (sock_ctx[0]->supports_udp_send_coalesced) ? &send_ctx->send_msg_size : NULL); + + if (ret == 0 && send_ctx->send_length > 0) { + for (int i = 0; i < nb_sockets; i++) { + if (sock_af[i] == send_ctx->addr_dest.ss_family) { + sock_ctx_send = sock_ctx[i]; + break; + } + } + + if (testing_migration) { + /* This code path is only used in the migration tests */ + uint16_t send_port = (send_ctx->addr_dest.ss_family == AF_INET) ? + ((struct sockaddr_in*) & send_ctx->addr_from)->sin_port : + ((struct sockaddr_in6*) & send_ctx->addr_from)->sin6_port; + + if (send_port == next_port) { + sock_ctx_send = sock_ctx[nb_sockets - 1]; + } + } + if (sock_ctx_send == NULL) { + picoquic_log_app_message(last_cnx, + "Could not find socket for AF_to=%d, AF_from=%d", + send_ctx->addr_dest.ss_family, send_ctx->addr_from.ss_family); + ret = -1; + } + else { + ret = picoquic_sendmsg_start(sock_ctx_send, send_ctx); + } + + if (ret == 0) { + /* Queue the send context at the end of the buffer chain, + * but only if there is more than 1 such context */ + send_ctx->is_started = 1; + if (send_ctx != send_ctx_last) { + send_ctx_last->next = send_ctx; + send_ctx_first = send_ctx->next; + send_ctx->next = NULL; + send_ctx_last = send_ctx; + } + } + else { + DBG_PRINTF("Cannot start sendsmg, error: %d", send_ctx->last_err); + } + } + else { + break; + } + } while (ret == 0 && (!send_ctx_first->is_started || send_ctx_first->is_complete)); + } + else { + DBG_PRINTF("%s", "No completion routine called on time!"); + Sleep(1); + } + + if (ret == 0 && loop_callback != NULL) { + ret = loop_callback(quic, picoquic_packet_loop_after_send, loop_callback_ctx); + } + } + + /* Special code for managing tests */ + if (ret == PICOQUIC_NO_ERROR_SIMULATE_NAT || ret == PICOQUIC_NO_ERROR_SIMULATE_MIGRATION) { + /* Two pseudo error codes used for testing migration! + * What follows is really test code, which we write here because it has to handle + * the sockets, which interferes a lot with the handling of the packet loop. + */ + int s_mig_af; + picoquic_recvmsg_async_ctx_t* sock_ctx_mig = NULL; + int testing_nat = (ret == PICOQUIC_NO_ERROR_SIMULATE_NAT); + HANDLE mig_sock_event = NULL; + + next_port = socket_port + 1; + if (picoquic_packet_loop_open_sockets_win(next_port, sock_af[0], &sock_ctx_mig, &s_mig_af, &mig_sock_event, 1) != 1){ + if (last_cnx != NULL) { + picoquic_log_app_message(last_cnx, "Could not create socket for migration test, port=%d, af=%d", + next_port, sock_af[0]); + } + } else{ + sock_ctx_mig->is_started = 1; + if (picoquic_recvmsg_async_start(sock_ctx_mig) != 0) { + if (last_cnx != NULL) { + picoquic_log_app_message(last_cnx, "Could not start migration socket, port=%d, af=%d", + next_port, sock_af[0]); + } + else { + DBG_PRINTF("%s", "Cannot start the migration socket"); + } + picoquic_delete_async_socket(sock_ctx_mig); + sock_ctx_mig = NULL; + ret = -1; + } + } + if (ret == PICOQUIC_NO_ERROR_SIMULATE_NAT || ret == PICOQUIC_NO_ERROR_SIMULATE_MIGRATION) { + if (testing_nat) { + if (sock_ctx[0] != NULL) { + picoquic_delete_async_socket(sock_ctx[0]); + } + sock_ctx[0] = sock_ctx_mig; + events[0] = mig_sock_event; + ret = 0; + if (last_cnx != NULL) { + picoquic_log_app_message(last_cnx, "Testing NAT rebinding, port=%d, af=%d", + next_port, sock_af[0]); + } + } + else { + /* Testing organized migration */ + if (nb_sockets < PICOQUIC_PACKET_LOOP_SOCKETS_MAX && last_cnx != NULL) { + struct sockaddr_storage local_address; + if (last_cnx != NULL) { + picoquic_log_app_message(last_cnx, "Testing organized migration, port=%d, af=%d", + next_port, sock_af[0]); + } + picoquic_store_addr(&local_address, (struct sockaddr*) & last_cnx->path[0]->local_addr); + if (local_address.ss_family == AF_INET6) { + ((struct sockaddr_in6*) & local_address)->sin6_port = next_port; + } + else if (local_address.ss_family == AF_INET) { + ((struct sockaddr_in*) & local_address)->sin_port = next_port; + } + sock_ctx[nb_sockets] = sock_ctx_mig; + events[nb_sockets] = mig_sock_event; + nb_sockets++; + testing_migration = 1; + ret = picoquic_probe_new_path(last_cnx, (struct sockaddr*) & last_cnx->path[0]->peer_addr, + (struct sockaddr*) & local_address, current_time); + } + else { + picoquic_delete_async_socket(sock_ctx_mig); + } + } + } + } + } + + if (ret == PICOQUIC_NO_ERROR_TERMINATE_PACKET_LOOP) { + /* Normal termination requested by the application, returns no error */ + ret = 0; + } + + /* Close the sockets */ + for (int i = 0; i < nb_sockets; i++) { + if (sock_ctx[i] != NULL) { + picoquic_delete_async_socket(sock_ctx[i]); + sock_ctx[i] = NULL; + events[i] = NULL; + } + } + + /* Free the list of contexts */ + picoquic_socks_delete_send_ctx_list(&send_ctx_first, &send_ctx_last); + + return ret; +} + +#endif /* _Windows */ \ No newline at end of file diff --git a/include/robin_hood/robin_hood.h b/deps/robin_hood/robin_hood.h similarity index 100% rename from include/robin_hood/robin_hood.h rename to deps/robin_hood/robin_hood.h diff --git a/include/toml++/toml.h b/deps/toml++/toml.h similarity index 100% rename from include/toml++/toml.h rename to deps/toml++/toml.h diff --git a/include/toml++/toml_array.h b/deps/toml++/toml_array.h similarity index 100% rename from include/toml++/toml_array.h rename to deps/toml++/toml_array.h diff --git a/include/toml++/toml_array.hpp b/deps/toml++/toml_array.hpp similarity index 100% rename from include/toml++/toml_array.hpp rename to deps/toml++/toml_array.hpp diff --git a/include/toml++/toml_common.h b/deps/toml++/toml_common.h similarity index 100% rename from include/toml++/toml_common.h rename to deps/toml++/toml_common.h diff --git a/include/toml++/toml_date_time.h b/deps/toml++/toml_date_time.h similarity index 100% rename from include/toml++/toml_date_time.h rename to deps/toml++/toml_date_time.h diff --git a/include/toml++/toml_default_formatter.h b/deps/toml++/toml_default_formatter.h similarity index 100% rename from include/toml++/toml_default_formatter.h rename to deps/toml++/toml_default_formatter.h diff --git a/include/toml++/toml_default_formatter.hpp b/deps/toml++/toml_default_formatter.hpp similarity index 100% rename from include/toml++/toml_default_formatter.hpp rename to deps/toml++/toml_default_formatter.hpp diff --git a/include/toml++/toml_formatter.h b/deps/toml++/toml_formatter.h similarity index 100% rename from include/toml++/toml_formatter.h rename to deps/toml++/toml_formatter.h diff --git a/include/toml++/toml_instantiations.hpp b/deps/toml++/toml_instantiations.hpp similarity index 100% rename from include/toml++/toml_instantiations.hpp rename to deps/toml++/toml_instantiations.hpp diff --git a/include/toml++/toml_json_formatter.h b/deps/toml++/toml_json_formatter.h similarity index 100% rename from include/toml++/toml_json_formatter.h rename to deps/toml++/toml_json_formatter.h diff --git a/include/toml++/toml_json_formatter.hpp b/deps/toml++/toml_json_formatter.hpp similarity index 100% rename from include/toml++/toml_json_formatter.hpp rename to deps/toml++/toml_json_formatter.hpp diff --git a/include/toml++/toml_node.h b/deps/toml++/toml_node.h similarity index 100% rename from include/toml++/toml_node.h rename to deps/toml++/toml_node.h diff --git a/include/toml++/toml_node.hpp b/deps/toml++/toml_node.hpp similarity index 100% rename from include/toml++/toml_node.hpp rename to deps/toml++/toml_node.hpp diff --git a/include/toml++/toml_node_view.h b/deps/toml++/toml_node_view.h similarity index 100% rename from include/toml++/toml_node_view.h rename to deps/toml++/toml_node_view.h diff --git a/include/toml++/toml_parse_error.h b/deps/toml++/toml_parse_error.h similarity index 100% rename from include/toml++/toml_parse_error.h rename to deps/toml++/toml_parse_error.h diff --git a/include/toml++/toml_parser.h b/deps/toml++/toml_parser.h similarity index 100% rename from include/toml++/toml_parser.h rename to deps/toml++/toml_parser.h diff --git a/include/toml++/toml_parser.hpp b/deps/toml++/toml_parser.hpp similarity index 100% rename from include/toml++/toml_parser.hpp rename to deps/toml++/toml_parser.hpp diff --git a/include/toml++/toml_preprocessor.h b/deps/toml++/toml_preprocessor.h similarity index 100% rename from include/toml++/toml_preprocessor.h rename to deps/toml++/toml_preprocessor.h diff --git a/include/toml++/toml_print_to_stream.h b/deps/toml++/toml_print_to_stream.h similarity index 100% rename from include/toml++/toml_print_to_stream.h rename to deps/toml++/toml_print_to_stream.h diff --git a/include/toml++/toml_table.h b/deps/toml++/toml_table.h similarity index 100% rename from include/toml++/toml_table.h rename to deps/toml++/toml_table.h diff --git a/include/toml++/toml_table.hpp b/deps/toml++/toml_table.hpp similarity index 100% rename from include/toml++/toml_table.hpp rename to deps/toml++/toml_table.hpp diff --git a/include/toml++/toml_utf8.h b/deps/toml++/toml_utf8.h similarity index 100% rename from include/toml++/toml_utf8.h rename to deps/toml++/toml_utf8.h diff --git a/include/toml++/toml_utf8_generated.h b/deps/toml++/toml_utf8_generated.h similarity index 100% rename from include/toml++/toml_utf8_generated.h rename to deps/toml++/toml_utf8_generated.h diff --git a/include/toml++/toml_utf8_streams.h b/deps/toml++/toml_utf8_streams.h similarity index 100% rename from include/toml++/toml_utf8_streams.h rename to deps/toml++/toml_utf8_streams.h diff --git a/include/toml++/toml_value.h b/deps/toml++/toml_value.h similarity index 100% rename from include/toml++/toml_value.h rename to deps/toml++/toml_value.h diff --git a/include/toml++/toml_version.h b/deps/toml++/toml_version.h similarity index 100% rename from include/toml++/toml_version.h rename to deps/toml++/toml_version.h diff --git a/include/tracy/AUTHORS b/deps/tracy/AUTHORS similarity index 100% rename from include/tracy/AUTHORS rename to deps/tracy/AUTHORS diff --git a/include/tracy/LICENSE b/deps/tracy/LICENSE similarity index 100% rename from include/tracy/LICENSE rename to deps/tracy/LICENSE diff --git a/include/tracy/README.md b/deps/tracy/README.md similarity index 100% rename from include/tracy/README.md rename to deps/tracy/README.md diff --git a/include/tracy/Tracy.hpp b/deps/tracy/Tracy.hpp similarity index 100% rename from include/tracy/Tracy.hpp rename to deps/tracy/Tracy.hpp diff --git a/include/tracy/TracyC.h b/deps/tracy/TracyC.h similarity index 100% rename from include/tracy/TracyC.h rename to deps/tracy/TracyC.h diff --git a/include/tracy/TracyClient.cpp b/deps/tracy/TracyClient.cpp similarity index 100% rename from include/tracy/TracyClient.cpp rename to deps/tracy/TracyClient.cpp diff --git a/include/tracy/TracyOpenGL.hpp b/deps/tracy/TracyOpenGL.hpp similarity index 100% rename from include/tracy/TracyOpenGL.hpp rename to deps/tracy/TracyOpenGL.hpp diff --git a/include/tracy/TracyVulkan.hpp b/deps/tracy/TracyVulkan.hpp similarity index 100% rename from include/tracy/TracyVulkan.hpp rename to deps/tracy/TracyVulkan.hpp diff --git a/include/tracy/client/TracyArmCpuTable.hpp b/deps/tracy/client/TracyArmCpuTable.hpp similarity index 100% rename from include/tracy/client/TracyArmCpuTable.hpp rename to deps/tracy/client/TracyArmCpuTable.hpp diff --git a/include/tracy/client/TracyCallstack.cpp b/deps/tracy/client/TracyCallstack.cpp similarity index 100% rename from include/tracy/client/TracyCallstack.cpp rename to deps/tracy/client/TracyCallstack.cpp diff --git a/include/tracy/client/TracyCallstack.h b/deps/tracy/client/TracyCallstack.h similarity index 100% rename from include/tracy/client/TracyCallstack.h rename to deps/tracy/client/TracyCallstack.h diff --git a/include/tracy/client/TracyCallstack.hpp b/deps/tracy/client/TracyCallstack.hpp similarity index 100% rename from include/tracy/client/TracyCallstack.hpp rename to deps/tracy/client/TracyCallstack.hpp diff --git a/include/tracy/client/TracyDxt1.cpp b/deps/tracy/client/TracyDxt1.cpp similarity index 100% rename from include/tracy/client/TracyDxt1.cpp rename to deps/tracy/client/TracyDxt1.cpp diff --git a/include/tracy/client/TracyDxt1.hpp b/deps/tracy/client/TracyDxt1.hpp similarity index 100% rename from include/tracy/client/TracyDxt1.hpp rename to deps/tracy/client/TracyDxt1.hpp diff --git a/include/tracy/client/TracyFastVector.hpp b/deps/tracy/client/TracyFastVector.hpp similarity index 100% rename from include/tracy/client/TracyFastVector.hpp rename to deps/tracy/client/TracyFastVector.hpp diff --git a/include/tracy/client/TracyLock.hpp b/deps/tracy/client/TracyLock.hpp similarity index 100% rename from include/tracy/client/TracyLock.hpp rename to deps/tracy/client/TracyLock.hpp diff --git a/include/tracy/client/TracyProfiler.cpp b/deps/tracy/client/TracyProfiler.cpp similarity index 100% rename from include/tracy/client/TracyProfiler.cpp rename to deps/tracy/client/TracyProfiler.cpp diff --git a/include/tracy/client/TracyProfiler.hpp b/deps/tracy/client/TracyProfiler.hpp similarity index 100% rename from include/tracy/client/TracyProfiler.hpp rename to deps/tracy/client/TracyProfiler.hpp diff --git a/include/tracy/client/TracyScoped.hpp b/deps/tracy/client/TracyScoped.hpp similarity index 100% rename from include/tracy/client/TracyScoped.hpp rename to deps/tracy/client/TracyScoped.hpp diff --git a/include/tracy/client/TracySysTime.cpp b/deps/tracy/client/TracySysTime.cpp similarity index 100% rename from include/tracy/client/TracySysTime.cpp rename to deps/tracy/client/TracySysTime.cpp diff --git a/include/tracy/client/TracySysTime.hpp b/deps/tracy/client/TracySysTime.hpp similarity index 100% rename from include/tracy/client/TracySysTime.hpp rename to deps/tracy/client/TracySysTime.hpp diff --git a/include/tracy/client/TracySysTrace.cpp b/deps/tracy/client/TracySysTrace.cpp similarity index 100% rename from include/tracy/client/TracySysTrace.cpp rename to deps/tracy/client/TracySysTrace.cpp diff --git a/include/tracy/client/TracySysTrace.hpp b/deps/tracy/client/TracySysTrace.hpp similarity index 100% rename from include/tracy/client/TracySysTrace.hpp rename to deps/tracy/client/TracySysTrace.hpp diff --git a/include/tracy/client/TracySysTracePayload.hpp b/deps/tracy/client/TracySysTracePayload.hpp similarity index 100% rename from include/tracy/client/TracySysTracePayload.hpp rename to deps/tracy/client/TracySysTracePayload.hpp diff --git a/include/tracy/client/TracyThread.hpp b/deps/tracy/client/TracyThread.hpp similarity index 100% rename from include/tracy/client/TracyThread.hpp rename to deps/tracy/client/TracyThread.hpp diff --git a/include/tracy/client/tracy_concurrentqueue.h b/deps/tracy/client/tracy_concurrentqueue.h similarity index 100% rename from include/tracy/client/tracy_concurrentqueue.h rename to deps/tracy/client/tracy_concurrentqueue.h diff --git a/include/tracy/client/tracy_rpmalloc.cpp b/deps/tracy/client/tracy_rpmalloc.cpp similarity index 100% rename from include/tracy/client/tracy_rpmalloc.cpp rename to deps/tracy/client/tracy_rpmalloc.cpp diff --git a/include/tracy/client/tracy_rpmalloc.hpp b/deps/tracy/client/tracy_rpmalloc.hpp similarity index 100% rename from include/tracy/client/tracy_rpmalloc.hpp rename to deps/tracy/client/tracy_rpmalloc.hpp diff --git a/include/tracy/common/TracyAlign.hpp b/deps/tracy/common/TracyAlign.hpp similarity index 100% rename from include/tracy/common/TracyAlign.hpp rename to deps/tracy/common/TracyAlign.hpp diff --git a/include/tracy/common/TracyAlloc.hpp b/deps/tracy/common/TracyAlloc.hpp similarity index 100% rename from include/tracy/common/TracyAlloc.hpp rename to deps/tracy/common/TracyAlloc.hpp diff --git a/include/tracy/common/TracyApi.h b/deps/tracy/common/TracyApi.h similarity index 100% rename from include/tracy/common/TracyApi.h rename to deps/tracy/common/TracyApi.h diff --git a/include/tracy/common/TracyColor.hpp b/deps/tracy/common/TracyColor.hpp similarity index 100% rename from include/tracy/common/TracyColor.hpp rename to deps/tracy/common/TracyColor.hpp diff --git a/include/tracy/common/TracyForceInline.hpp b/deps/tracy/common/TracyForceInline.hpp similarity index 100% rename from include/tracy/common/TracyForceInline.hpp rename to deps/tracy/common/TracyForceInline.hpp diff --git a/include/tracy/common/TracyMutex.hpp b/deps/tracy/common/TracyMutex.hpp similarity index 100% rename from include/tracy/common/TracyMutex.hpp rename to deps/tracy/common/TracyMutex.hpp diff --git a/include/tracy/common/TracyProtocol.hpp b/deps/tracy/common/TracyProtocol.hpp similarity index 100% rename from include/tracy/common/TracyProtocol.hpp rename to deps/tracy/common/TracyProtocol.hpp diff --git a/include/tracy/common/TracyQueue.hpp b/deps/tracy/common/TracyQueue.hpp similarity index 100% rename from include/tracy/common/TracyQueue.hpp rename to deps/tracy/common/TracyQueue.hpp diff --git a/include/tracy/common/TracySocket.cpp b/deps/tracy/common/TracySocket.cpp similarity index 100% rename from include/tracy/common/TracySocket.cpp rename to deps/tracy/common/TracySocket.cpp diff --git a/include/tracy/common/TracySocket.hpp b/deps/tracy/common/TracySocket.hpp similarity index 100% rename from include/tracy/common/TracySocket.hpp rename to deps/tracy/common/TracySocket.hpp diff --git a/include/tracy/common/TracySystem.cpp b/deps/tracy/common/TracySystem.cpp similarity index 100% rename from include/tracy/common/TracySystem.cpp rename to deps/tracy/common/TracySystem.cpp diff --git a/include/tracy/common/TracySystem.hpp b/deps/tracy/common/TracySystem.hpp similarity index 100% rename from include/tracy/common/TracySystem.hpp rename to deps/tracy/common/TracySystem.hpp diff --git a/include/tracy/common/tracy_lz4.cpp b/deps/tracy/common/tracy_lz4.cpp similarity index 100% rename from include/tracy/common/tracy_lz4.cpp rename to deps/tracy/common/tracy_lz4.cpp diff --git a/include/tracy/common/tracy_lz4.hpp b/deps/tracy/common/tracy_lz4.hpp similarity index 100% rename from include/tracy/common/tracy_lz4.hpp rename to deps/tracy/common/tracy_lz4.hpp diff --git a/include/tracy/common/tracy_lz4hc.cpp b/deps/tracy/common/tracy_lz4hc.cpp similarity index 100% rename from include/tracy/common/tracy_lz4hc.cpp rename to deps/tracy/common/tracy_lz4hc.cpp diff --git a/include/tracy/common/tracy_lz4hc.hpp b/deps/tracy/common/tracy_lz4hc.hpp similarity index 100% rename from include/tracy/common/tracy_lz4hc.hpp rename to deps/tracy/common/tracy_lz4hc.hpp diff --git a/include/tracy/libbacktrace/LICENSE b/deps/tracy/libbacktrace/LICENSE similarity index 100% rename from include/tracy/libbacktrace/LICENSE rename to deps/tracy/libbacktrace/LICENSE diff --git a/include/tracy/libbacktrace/alloc.cpp b/deps/tracy/libbacktrace/alloc.cpp similarity index 100% rename from include/tracy/libbacktrace/alloc.cpp rename to deps/tracy/libbacktrace/alloc.cpp diff --git a/include/tracy/libbacktrace/backtrace.hpp b/deps/tracy/libbacktrace/backtrace.hpp similarity index 100% rename from include/tracy/libbacktrace/backtrace.hpp rename to deps/tracy/libbacktrace/backtrace.hpp diff --git a/include/tracy/libbacktrace/config.h b/deps/tracy/libbacktrace/config.h similarity index 100% rename from include/tracy/libbacktrace/config.h rename to deps/tracy/libbacktrace/config.h diff --git a/include/tracy/libbacktrace/dwarf.cpp b/deps/tracy/libbacktrace/dwarf.cpp similarity index 100% rename from include/tracy/libbacktrace/dwarf.cpp rename to deps/tracy/libbacktrace/dwarf.cpp diff --git a/include/tracy/libbacktrace/elf.cpp b/deps/tracy/libbacktrace/elf.cpp similarity index 100% rename from include/tracy/libbacktrace/elf.cpp rename to deps/tracy/libbacktrace/elf.cpp diff --git a/include/tracy/libbacktrace/fileline.cpp b/deps/tracy/libbacktrace/fileline.cpp similarity index 100% rename from include/tracy/libbacktrace/fileline.cpp rename to deps/tracy/libbacktrace/fileline.cpp diff --git a/include/tracy/libbacktrace/filenames.hpp b/deps/tracy/libbacktrace/filenames.hpp similarity index 100% rename from include/tracy/libbacktrace/filenames.hpp rename to deps/tracy/libbacktrace/filenames.hpp diff --git a/include/tracy/libbacktrace/internal.hpp b/deps/tracy/libbacktrace/internal.hpp similarity index 100% rename from include/tracy/libbacktrace/internal.hpp rename to deps/tracy/libbacktrace/internal.hpp diff --git a/include/tracy/libbacktrace/macho.cpp b/deps/tracy/libbacktrace/macho.cpp similarity index 100% rename from include/tracy/libbacktrace/macho.cpp rename to deps/tracy/libbacktrace/macho.cpp diff --git a/include/tracy/libbacktrace/mmapio.cpp b/deps/tracy/libbacktrace/mmapio.cpp similarity index 100% rename from include/tracy/libbacktrace/mmapio.cpp rename to deps/tracy/libbacktrace/mmapio.cpp diff --git a/include/tracy/libbacktrace/posix.cpp b/deps/tracy/libbacktrace/posix.cpp similarity index 100% rename from include/tracy/libbacktrace/posix.cpp rename to deps/tracy/libbacktrace/posix.cpp diff --git a/include/tracy/libbacktrace/sort.cpp b/deps/tracy/libbacktrace/sort.cpp similarity index 100% rename from include/tracy/libbacktrace/sort.cpp rename to deps/tracy/libbacktrace/sort.cpp diff --git a/include/tracy/libbacktrace/state.cpp b/deps/tracy/libbacktrace/state.cpp similarity index 100% rename from include/tracy/libbacktrace/state.cpp rename to deps/tracy/libbacktrace/state.cpp diff --git a/include/volk/volk.c b/deps/volk/volk.c similarity index 100% rename from include/volk/volk.c rename to deps/volk/volk.c diff --git a/include/volk/volk.h b/deps/volk/volk.h similarity index 100% rename from include/volk/volk.h rename to deps/volk/volk.h diff --git a/include/zstd/CMakeLists.txt b/deps/zstd/CMakeLists.txt similarity index 100% rename from include/zstd/CMakeLists.txt rename to deps/zstd/CMakeLists.txt diff --git a/include/zstd/common/bitstream.h b/deps/zstd/common/bitstream.h similarity index 100% rename from include/zstd/common/bitstream.h rename to deps/zstd/common/bitstream.h diff --git a/include/zstd/common/compiler.h b/deps/zstd/common/compiler.h similarity index 100% rename from include/zstd/common/compiler.h rename to deps/zstd/common/compiler.h diff --git a/include/zstd/common/cpu.h b/deps/zstd/common/cpu.h similarity index 100% rename from include/zstd/common/cpu.h rename to deps/zstd/common/cpu.h diff --git a/include/zstd/common/debug.c b/deps/zstd/common/debug.c similarity index 100% rename from include/zstd/common/debug.c rename to deps/zstd/common/debug.c diff --git a/include/zstd/common/debug.h b/deps/zstd/common/debug.h similarity index 100% rename from include/zstd/common/debug.h rename to deps/zstd/common/debug.h diff --git a/include/zstd/common/entropy_common.c b/deps/zstd/common/entropy_common.c similarity index 100% rename from include/zstd/common/entropy_common.c rename to deps/zstd/common/entropy_common.c diff --git a/include/zstd/common/error_private.c b/deps/zstd/common/error_private.c similarity index 100% rename from include/zstd/common/error_private.c rename to deps/zstd/common/error_private.c diff --git a/include/zstd/common/error_private.h b/deps/zstd/common/error_private.h similarity index 100% rename from include/zstd/common/error_private.h rename to deps/zstd/common/error_private.h diff --git a/include/zstd/common/fse.h b/deps/zstd/common/fse.h similarity index 100% rename from include/zstd/common/fse.h rename to deps/zstd/common/fse.h diff --git a/include/zstd/common/fse_decompress.c b/deps/zstd/common/fse_decompress.c similarity index 100% rename from include/zstd/common/fse_decompress.c rename to deps/zstd/common/fse_decompress.c diff --git a/include/zstd/common/huf.h b/deps/zstd/common/huf.h similarity index 100% rename from include/zstd/common/huf.h rename to deps/zstd/common/huf.h diff --git a/include/zstd/common/mem.h b/deps/zstd/common/mem.h similarity index 100% rename from include/zstd/common/mem.h rename to deps/zstd/common/mem.h diff --git a/include/zstd/common/pool.c b/deps/zstd/common/pool.c similarity index 100% rename from include/zstd/common/pool.c rename to deps/zstd/common/pool.c diff --git a/include/zstd/common/pool.h b/deps/zstd/common/pool.h similarity index 100% rename from include/zstd/common/pool.h rename to deps/zstd/common/pool.h diff --git a/include/zstd/common/threading.c b/deps/zstd/common/threading.c similarity index 100% rename from include/zstd/common/threading.c rename to deps/zstd/common/threading.c diff --git a/include/zstd/common/threading.h b/deps/zstd/common/threading.h similarity index 100% rename from include/zstd/common/threading.h rename to deps/zstd/common/threading.h diff --git a/include/zstd/common/xxhash.c b/deps/zstd/common/xxhash.c similarity index 100% rename from include/zstd/common/xxhash.c rename to deps/zstd/common/xxhash.c diff --git a/include/zstd/common/xxhash.h b/deps/zstd/common/xxhash.h similarity index 100% rename from include/zstd/common/xxhash.h rename to deps/zstd/common/xxhash.h diff --git a/include/zstd/common/zstd_common.c b/deps/zstd/common/zstd_common.c similarity index 100% rename from include/zstd/common/zstd_common.c rename to deps/zstd/common/zstd_common.c diff --git a/include/zstd/common/zstd_errors.h b/deps/zstd/common/zstd_errors.h similarity index 100% rename from include/zstd/common/zstd_errors.h rename to deps/zstd/common/zstd_errors.h diff --git a/include/zstd/common/zstd_internal.h b/deps/zstd/common/zstd_internal.h similarity index 100% rename from include/zstd/common/zstd_internal.h rename to deps/zstd/common/zstd_internal.h diff --git a/include/zstd/compress/fse_compress.c b/deps/zstd/compress/fse_compress.c similarity index 100% rename from include/zstd/compress/fse_compress.c rename to deps/zstd/compress/fse_compress.c diff --git a/include/zstd/compress/hist.c b/deps/zstd/compress/hist.c similarity index 100% rename from include/zstd/compress/hist.c rename to deps/zstd/compress/hist.c diff --git a/include/zstd/compress/hist.h b/deps/zstd/compress/hist.h similarity index 100% rename from include/zstd/compress/hist.h rename to deps/zstd/compress/hist.h diff --git a/include/zstd/compress/huf_compress.c b/deps/zstd/compress/huf_compress.c similarity index 100% rename from include/zstd/compress/huf_compress.c rename to deps/zstd/compress/huf_compress.c diff --git a/include/zstd/compress/zstd_compress.c b/deps/zstd/compress/zstd_compress.c similarity index 100% rename from include/zstd/compress/zstd_compress.c rename to deps/zstd/compress/zstd_compress.c diff --git a/include/zstd/compress/zstd_compress_internal.h b/deps/zstd/compress/zstd_compress_internal.h similarity index 100% rename from include/zstd/compress/zstd_compress_internal.h rename to deps/zstd/compress/zstd_compress_internal.h diff --git a/include/zstd/compress/zstd_compress_literals.c b/deps/zstd/compress/zstd_compress_literals.c similarity index 100% rename from include/zstd/compress/zstd_compress_literals.c rename to deps/zstd/compress/zstd_compress_literals.c diff --git a/include/zstd/compress/zstd_compress_literals.h b/deps/zstd/compress/zstd_compress_literals.h similarity index 100% rename from include/zstd/compress/zstd_compress_literals.h rename to deps/zstd/compress/zstd_compress_literals.h diff --git a/include/zstd/compress/zstd_compress_sequences.c b/deps/zstd/compress/zstd_compress_sequences.c similarity index 100% rename from include/zstd/compress/zstd_compress_sequences.c rename to deps/zstd/compress/zstd_compress_sequences.c diff --git a/include/zstd/compress/zstd_compress_sequences.h b/deps/zstd/compress/zstd_compress_sequences.h similarity index 100% rename from include/zstd/compress/zstd_compress_sequences.h rename to deps/zstd/compress/zstd_compress_sequences.h diff --git a/include/zstd/compress/zstd_compress_superblock.c b/deps/zstd/compress/zstd_compress_superblock.c similarity index 100% rename from include/zstd/compress/zstd_compress_superblock.c rename to deps/zstd/compress/zstd_compress_superblock.c diff --git a/include/zstd/compress/zstd_compress_superblock.h b/deps/zstd/compress/zstd_compress_superblock.h similarity index 100% rename from include/zstd/compress/zstd_compress_superblock.h rename to deps/zstd/compress/zstd_compress_superblock.h diff --git a/include/zstd/compress/zstd_cwksp.h b/deps/zstd/compress/zstd_cwksp.h similarity index 100% rename from include/zstd/compress/zstd_cwksp.h rename to deps/zstd/compress/zstd_cwksp.h diff --git a/include/zstd/compress/zstd_double_fast.c b/deps/zstd/compress/zstd_double_fast.c similarity index 100% rename from include/zstd/compress/zstd_double_fast.c rename to deps/zstd/compress/zstd_double_fast.c diff --git a/include/zstd/compress/zstd_double_fast.h b/deps/zstd/compress/zstd_double_fast.h similarity index 100% rename from include/zstd/compress/zstd_double_fast.h rename to deps/zstd/compress/zstd_double_fast.h diff --git a/include/zstd/compress/zstd_fast.c b/deps/zstd/compress/zstd_fast.c similarity index 100% rename from include/zstd/compress/zstd_fast.c rename to deps/zstd/compress/zstd_fast.c diff --git a/include/zstd/compress/zstd_fast.h b/deps/zstd/compress/zstd_fast.h similarity index 100% rename from include/zstd/compress/zstd_fast.h rename to deps/zstd/compress/zstd_fast.h diff --git a/include/zstd/compress/zstd_lazy.c b/deps/zstd/compress/zstd_lazy.c similarity index 100% rename from include/zstd/compress/zstd_lazy.c rename to deps/zstd/compress/zstd_lazy.c diff --git a/include/zstd/compress/zstd_lazy.h b/deps/zstd/compress/zstd_lazy.h similarity index 100% rename from include/zstd/compress/zstd_lazy.h rename to deps/zstd/compress/zstd_lazy.h diff --git a/include/zstd/compress/zstd_ldm.c b/deps/zstd/compress/zstd_ldm.c similarity index 100% rename from include/zstd/compress/zstd_ldm.c rename to deps/zstd/compress/zstd_ldm.c diff --git a/include/zstd/compress/zstd_ldm.h b/deps/zstd/compress/zstd_ldm.h similarity index 100% rename from include/zstd/compress/zstd_ldm.h rename to deps/zstd/compress/zstd_ldm.h diff --git a/include/zstd/compress/zstd_opt.c b/deps/zstd/compress/zstd_opt.c similarity index 100% rename from include/zstd/compress/zstd_opt.c rename to deps/zstd/compress/zstd_opt.c diff --git a/include/zstd/compress/zstd_opt.h b/deps/zstd/compress/zstd_opt.h similarity index 100% rename from include/zstd/compress/zstd_opt.h rename to deps/zstd/compress/zstd_opt.h diff --git a/include/zstd/compress/zstdmt_compress.c b/deps/zstd/compress/zstdmt_compress.c similarity index 100% rename from include/zstd/compress/zstdmt_compress.c rename to deps/zstd/compress/zstdmt_compress.c diff --git a/include/zstd/compress/zstdmt_compress.h b/deps/zstd/compress/zstdmt_compress.h similarity index 100% rename from include/zstd/compress/zstdmt_compress.h rename to deps/zstd/compress/zstdmt_compress.h diff --git a/include/zstd/decompress/huf_decompress.c b/deps/zstd/decompress/huf_decompress.c similarity index 100% rename from include/zstd/decompress/huf_decompress.c rename to deps/zstd/decompress/huf_decompress.c diff --git a/include/zstd/decompress/zstd_ddict.c b/deps/zstd/decompress/zstd_ddict.c similarity index 100% rename from include/zstd/decompress/zstd_ddict.c rename to deps/zstd/decompress/zstd_ddict.c diff --git a/include/zstd/decompress/zstd_ddict.h b/deps/zstd/decompress/zstd_ddict.h similarity index 100% rename from include/zstd/decompress/zstd_ddict.h rename to deps/zstd/decompress/zstd_ddict.h diff --git a/include/zstd/decompress/zstd_decompress.c b/deps/zstd/decompress/zstd_decompress.c similarity index 100% rename from include/zstd/decompress/zstd_decompress.c rename to deps/zstd/decompress/zstd_decompress.c diff --git a/include/zstd/decompress/zstd_decompress_block.c b/deps/zstd/decompress/zstd_decompress_block.c similarity index 100% rename from include/zstd/decompress/zstd_decompress_block.c rename to deps/zstd/decompress/zstd_decompress_block.c diff --git a/include/zstd/decompress/zstd_decompress_block.h b/deps/zstd/decompress/zstd_decompress_block.h similarity index 100% rename from include/zstd/decompress/zstd_decompress_block.h rename to deps/zstd/decompress/zstd_decompress_block.h diff --git a/include/zstd/decompress/zstd_decompress_internal.h b/deps/zstd/decompress/zstd_decompress_internal.h similarity index 100% rename from include/zstd/decompress/zstd_decompress_internal.h rename to deps/zstd/decompress/zstd_decompress_internal.h diff --git a/include/zstd/dictBuilder/cover.c b/deps/zstd/dictBuilder/cover.c similarity index 100% rename from include/zstd/dictBuilder/cover.c rename to deps/zstd/dictBuilder/cover.c diff --git a/include/zstd/dictBuilder/cover.h b/deps/zstd/dictBuilder/cover.h similarity index 100% rename from include/zstd/dictBuilder/cover.h rename to deps/zstd/dictBuilder/cover.h diff --git a/include/zstd/dictBuilder/divsufsort.c b/deps/zstd/dictBuilder/divsufsort.c similarity index 100% rename from include/zstd/dictBuilder/divsufsort.c rename to deps/zstd/dictBuilder/divsufsort.c diff --git a/include/zstd/dictBuilder/divsufsort.h b/deps/zstd/dictBuilder/divsufsort.h similarity index 100% rename from include/zstd/dictBuilder/divsufsort.h rename to deps/zstd/dictBuilder/divsufsort.h diff --git a/include/zstd/dictBuilder/fastcover.c b/deps/zstd/dictBuilder/fastcover.c similarity index 100% rename from include/zstd/dictBuilder/fastcover.c rename to deps/zstd/dictBuilder/fastcover.c diff --git a/include/zstd/dictBuilder/zdict.c b/deps/zstd/dictBuilder/zdict.c similarity index 100% rename from include/zstd/dictBuilder/zdict.c rename to deps/zstd/dictBuilder/zdict.c diff --git a/include/zstd/dictBuilder/zdict.h b/deps/zstd/dictBuilder/zdict.h similarity index 100% rename from include/zstd/dictBuilder/zdict.h rename to deps/zstd/dictBuilder/zdict.h diff --git a/include/zstd/zstd.h b/deps/zstd/zstd.h similarity index 100% rename from include/zstd/zstd.h rename to deps/zstd/zstd.h diff --git a/include/enet/CMakeLists.txt b/include/enet/CMakeLists.txt deleted file mode 100644 index a99fe7e..0000000 --- a/include/enet/CMakeLists.txt +++ /dev/null @@ -1,92 +0,0 @@ -# The "configure" step. -include(CheckFunctionExists) -include(CheckStructHasMember) -include(CheckTypeSize) -check_function_exists("fcntl" HAS_FCNTL) -check_function_exists("poll" HAS_POLL) -check_function_exists("getaddrinfo" HAS_GETADDRINFO) -check_function_exists("getnameinfo" HAS_GETNAMEINFO) -check_function_exists("gethostbyname_r" HAS_GETHOSTBYNAME_R) -check_function_exists("gethostbyaddr_r" HAS_GETHOSTBYADDR_R) -check_function_exists("inet_pton" HAS_INET_PTON) -check_function_exists("inet_ntop" HAS_INET_NTOP) -check_struct_has_member("struct msghdr" "msg_flags" "sys/types.h;sys/socket.h" HAS_MSGHDR_FLAGS) -set(CMAKE_EXTRA_INCLUDE_FILES "sys/types.h" "sys/socket.h") -check_type_size("socklen_t" HAS_SOCKLEN_T BUILTIN_TYPES_ONLY) -unset(CMAKE_EXTRA_INCLUDE_FILES) -if(MSVC) - add_definitions(-W3) -else() - add_definitions(-Wno-error) -endif() - -if(HAS_FCNTL) - add_definitions(-DHAS_FCNTL=1) -endif() -if(HAS_POLL) - add_definitions(-DHAS_POLL=1) -endif() -if(HAS_GETNAMEINFO) - add_definitions(-DHAS_GETNAMEINFO=1) -endif() -if(HAS_GETADDRINFO) - add_definitions(-DHAS_GETADDRINFO=1) -endif() -if(HAS_GETHOSTBYNAME_R) - add_definitions(-DHAS_GETHOSTBYNAME_R=1) -endif() -if(HAS_GETHOSTBYADDR_R) - add_definitions(-DHAS_GETHOSTBYADDR_R=1) -endif() -if(HAS_INET_PTON) - add_definitions(-DHAS_INET_PTON=1) -endif() -if(HAS_INET_NTOP) - add_definitions(-DHAS_INET_NTOP=1) -endif() -if(HAS_MSGHDR_FLAGS) - add_definitions(-DHAS_MSGHDR_FLAGS=1) -endif() -if(HAS_SOCKLEN_T) - add_definitions(-DHAS_SOCKLEN_T=1) -endif() - -include_directories(${PROJECT_SOURCE_DIR}/include) - -set(INCLUDE_FILES_PREFIX include/enet) -set(INCLUDE_FILES - ${INCLUDE_FILES_PREFIX}/callbacks.h - ${INCLUDE_FILES_PREFIX}/enet.h - ${INCLUDE_FILES_PREFIX}/list.h - ${INCLUDE_FILES_PREFIX}/protocol.h - ${INCLUDE_FILES_PREFIX}/time.h - ${INCLUDE_FILES_PREFIX}/types.h - ${INCLUDE_FILES_PREFIX}/unix.h - ${INCLUDE_FILES_PREFIX}/utility.h - ${INCLUDE_FILES_PREFIX}/win32.h -) - -set(SOURCE_FILES - callbacks.c - compress.c - host.c - list.c - packet.c - peer.c - protocol.c - unix.c - win32.c) - -source_group(include FILES ${INCLUDE_FILES}) -source_group(source FILES ${SOURCE_FILES}) - -add_library(enet INTERFACE) -target_include_directories(enet INTERFACE include) - -add_library(enet_static STATIC ${INCLUDE_FILES} ${SOURCE_FILES}) -target_link_libraries(enet_static PUBLIC enet) -add_library(enet::enet_static ALIAS enet_static) - -if (MINGW) - target_link_libraries(enet_static winmm ws2_32) -endif() diff --git a/include/enet/ChangeLog b/include/enet/ChangeLog deleted file mode 100644 index b6ca52e..0000000 --- a/include/enet/ChangeLog +++ /dev/null @@ -1,196 +0,0 @@ -ENet 1.3.16 (September 8, 2020): - -* fix bug in unreliable fragment queuing -* use single output queue for reliable and unreliable packets for saner ordering -* revert experimental throttle changes that were less stable than prior algorithm - -ENet 1.3.15 (April 20, 2020): - -* quicker RTT initialization -* use fractional precision for RTT calculations -* fixes for packet throttle with low RTT variance -* miscellaneous socket bug fixes - -ENet 1.3.14 (January 27, 2019): - -* bug fix for enet_peer_disconnect_later() -* use getaddrinfo and getnameinfo where available -* miscellenous cleanups - -ENet 1.3.13 (April 30, 2015): - -* miscellaneous bug fixes -* added premake and cmake support -* miscellaneous documentation cleanups - -ENet 1.3.12 (April 24, 2014): - -* added maximumPacketSize and maximumWaitingData fields to ENetHost to limit the amount of -data waiting to be delivered on a peer (beware that the default maximumPacketSize is -32MB and should be set higher if desired as should maximumWaitingData) - -ENet 1.3.11 (December 26, 2013): - -* allow an ENetHost to connect to itself -* fixed possible bug with disconnect notifications during connect attempts -* fixed some preprocessor definition bugs - -ENet 1.3.10 (October 23, 2013); - -* doubled maximum reliable window size -* fixed RCVTIMEO/SNDTIMEO socket options and also added NODELAY - -ENet 1.3.9 (August 19, 2013): - -* added duplicatePeers option to ENetHost which can limit the number of peers from duplicate IPs -* added enet_socket_get_option() and ENET_SOCKOPT_ERROR -* added enet_host_random_seed() platform stub - -ENet 1.3.8 (June 2, 2013): - -* added enet_linked_version() for checking the linked version -* added enet_socket_get_address() for querying the local address of a socket -* silenced some debugging prints unless ENET_DEBUG is defined during compilation -* handle EINTR in enet_socket_wait() so that enet_host_service() doesn't propagate errors from signals -* optimized enet_host_bandwidth_throttle() to be less expensive for large numbers of peers - -ENet 1.3.7 (March 6, 2013): - -* added ENET_PACKET_FLAG_SENT to indicate that a packet is being freed because it has been sent -* added userData field to ENetPacket -* changed how random seed is generated on Windows to avoid import warnings -* fixed case where disconnects could be generated with no preceding connect event - -ENet 1.3.6 (December 11, 2012): - -* added support for intercept callback in ENetHost that can be used to process raw packets before ENet -* added enet_socket_shutdown() for issuing shutdown on a socket -* fixed enet_socket_connect() to not error on non-blocking connects -* fixed bug in MTU negotiation during connections - -ENet 1.3.5 (July 31, 2012): - -* fixed bug in unreliable packet fragment queuing - -ENet 1.3.4 (May 29, 2012): - -* added enet_peer_ping_interval() for configuring per-peer ping intervals -* added enet_peer_timeout() for configuring per-peer timeouts -* added protocol packet size limits - -ENet 1.3.3 (June 28, 2011): - -* fixed bug with simultaneous disconnects not dispatching events - -ENet 1.3.2 (May 31, 2011): - -* added support for unreliable packet fragmenting via the packet flag -ENET_PACKET_FLAG_UNRELIABLE_FRAGMENT -* fixed regression in unreliable packet queuing -* added check against received port to limit some forms of IP-spoofing - -ENet 1.3.1 (February 10, 2011): - -* fixed bug in tracking of reliable data in transit -* reliable data window size now scales with the throttle -* fixed bug in fragment length calculation when checksums are used - -ENet 1.3.0 (June 5, 2010): - -* enet_host_create() now requires the channel limit to be specified as -a parameter -* enet_host_connect() now accepts a data parameter which is supplied -to the receiving receiving host in the event data field for a connect event -* added an adaptive order-2 PPM range coder as a built-in compressor option -which can be set with enet_host_compress_with_range_coder() -* added support for packet compression configurable with a callback -* improved session number handling to not rely on the packet checksum -field, saving 4 bytes per packet unless the checksum option is used -* removed the dependence on the rand callback for session number handling - -Caveats: This version is not protocol compatible with the 1.2 series or -earlier. The enet_host_connect and enet_host_create API functions require -supplying additional parameters. - -ENet 1.2.5 (June 28, 2011): - -* fixed bug with simultaneous disconnects not dispatching events - -ENet 1.2.4 (May 31, 2011): - -* fixed regression in unreliable packet queuing -* added check against received port to limit some forms of IP-spoofing - -ENet 1.2.3 (February 10, 2011): - -* fixed bug in tracking reliable data in transit - -ENet 1.2.2 (June 5, 2010): - -* checksum functionality is now enabled by setting a checksum callback -inside ENetHost instead of being a configure script option -* added totalSentData, totalSentPackets, totalReceivedData, and -totalReceivedPackets counters inside ENetHost for getting usage -statistics -* added enet_host_channel_limit() for limiting the maximum number of -channels allowed by connected peers -* now uses dispatch queues for event dispatch rather than potentially -unscalable array walking -* added no_memory callback that is called when a malloc attempt fails, -such that if no_memory returns rather than aborts (the default behavior), -then the error is propagated to the return value of the API calls -* now uses packed attribute for protocol structures on platforms with -strange alignment rules -* improved autoconf build system contributed by Nathan Brink allowing -for easier building as a shared library - -Caveats: If you were using the compile-time option that enabled checksums, -make sure to set the checksum callback inside ENetHost to enet_crc32 to -regain the old behavior. The ENetCallbacks structure has added new fields, -so make sure to clear the structure to zero before use if -using enet_initialize_with_callbacks(). - -ENet 1.2.1 (November 12, 2009): - -* fixed bug that could cause disconnect events to be dropped -* added thin wrapper around select() for portable usage -* added ENET_SOCKOPT_REUSEADDR socket option -* factored enet_socket_bind()/enet_socket_listen() out of enet_socket_create() -* added contributed Code::Blocks build file - -ENet 1.2 (February 12, 2008): - -* fixed bug in VERIFY_CONNECT acknowledgement that could cause connect -attempts to occasionally timeout -* fixed acknowledgements to check both the outgoing and sent queues -when removing acknowledged packets -* fixed accidental bit rot in the MSVC project file -* revised sequence number overflow handling to address some possible -disconnect bugs -* added enet_host_check_events() for getting only local queued events -* factored out socket option setting into enet_socket_set_option() so -that socket options are now set separately from enet_socket_create() - -Caveats: While this release is superficially protocol compatible with 1.1, -differences in the sequence number overflow handling can potentially cause -random disconnects. - -ENet 1.1 (June 6, 2007): - -* optional CRC32 just in case someone needs a stronger checksum than UDP -provides (--enable-crc32 configure option) -* the size of packet headers are half the size they used to be (so less -overhead when sending small packets) -* enet_peer_disconnect_later() that waits till all queued outgoing -packets get sent before issuing an actual disconnect -* freeCallback field in individual packets for notification of when a -packet is about to be freed -* ENET_PACKET_FLAG_NO_ALLOCATE for supplying pre-allocated data to a -packet (can be used in concert with freeCallback to support some custom -allocation schemes that the normal memory allocation callbacks would -normally not allow) -* enet_address_get_host_ip() for printing address numbers -* promoted the enet_socket_*() functions to be part of the API now -* a few stability/crash fixes - - diff --git a/include/enet/LICENSE b/include/enet/LICENSE deleted file mode 100644 index 6906f8e..0000000 --- a/include/enet/LICENSE +++ /dev/null @@ -1,7 +0,0 @@ -Copyright (c) 2002-2020 Lee Salzman - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/include/enet/Makefile.am b/include/enet/Makefile.am deleted file mode 100644 index d3a7e50..0000000 --- a/include/enet/Makefile.am +++ /dev/null @@ -1,22 +0,0 @@ -pkgconfigdir = $(libdir)/pkgconfig -nodist_pkgconfig_DATA = libenet.pc - -enetincludedir=$(includedir)/enet -enetinclude_HEADERS = \ - include/enet/callbacks.h \ - include/enet/enet.h \ - include/enet/list.h \ - include/enet/protocol.h \ - include/enet/time.h \ - include/enet/types.h \ - include/enet/unix.h \ - include/enet/utility.h \ - include/enet/win32.h - -lib_LTLIBRARIES = libenet.la -libenet_la_SOURCES = callbacks.c compress.c host.c list.c packet.c peer.c protocol.c unix.c win32.c -# see info '(libtool) Updating version info' before making a release -libenet_la_LDFLAGS = $(AM_LDFLAGS) -version-info 7:4:0 -AM_CPPFLAGS = -I$(top_srcdir)/include - -ACLOCAL_AMFLAGS = -Im4 diff --git a/include/enet/README b/include/enet/README deleted file mode 100644 index 54b2d21..0000000 --- a/include/enet/README +++ /dev/null @@ -1,15 +0,0 @@ -Please visit the ENet homepage at http://enet.bespin.org for installation -and usage instructions. - -If you obtained this package from github, the quick description on how to build -is: - -# Generate the build system. - -autoreconf -vfi - -# Compile and install the library. - -./configure && make && make install - - diff --git a/include/enet/callbacks.c b/include/enet/callbacks.c deleted file mode 100644 index b3990af..0000000 --- a/include/enet/callbacks.c +++ /dev/null @@ -1,53 +0,0 @@ -/** - @file callbacks.c - @brief ENet callback functions -*/ -#define ENET_BUILDING_LIB 1 -#include "enet/enet.h" - -static ENetCallbacks callbacks = { malloc, free, abort }; - -int -enet_initialize_with_callbacks (ENetVersion version, const ENetCallbacks * inits) -{ - if (version < ENET_VERSION_CREATE (1, 3, 0)) - return -1; - - if (inits -> malloc != NULL || inits -> free != NULL) - { - if (inits -> malloc == NULL || inits -> free == NULL) - return -1; - - callbacks.malloc = inits -> malloc; - callbacks.free = inits -> free; - } - - if (inits -> no_memory != NULL) - callbacks.no_memory = inits -> no_memory; - - return enet_initialize (); -} - -ENetVersion -enet_linked_version (void) -{ - return ENET_VERSION; -} - -void * -enet_malloc (size_t size) -{ - void * memory = callbacks.malloc (size); - - if (memory == NULL) - callbacks.no_memory (); - - return memory; -} - -void -enet_free (void * memory) -{ - callbacks.free (memory); -} - diff --git a/include/enet/compress.c b/include/enet/compress.c deleted file mode 100644 index 784489a..0000000 --- a/include/enet/compress.c +++ /dev/null @@ -1,654 +0,0 @@ -/** - @file compress.c - @brief An adaptive order-2 PPM range coder -*/ -#define ENET_BUILDING_LIB 1 -#include -#include "enet/enet.h" - -typedef struct _ENetSymbol -{ - /* binary indexed tree of symbols */ - enet_uint8 value; - enet_uint8 count; - enet_uint16 under; - enet_uint16 left, right; - - /* context defined by this symbol */ - enet_uint16 symbols; - enet_uint16 escapes; - enet_uint16 total; - enet_uint16 parent; -} ENetSymbol; - -/* adaptation constants tuned aggressively for small packet sizes rather than large file compression */ -enum -{ - ENET_RANGE_CODER_TOP = 1<<24, - ENET_RANGE_CODER_BOTTOM = 1<<16, - - ENET_CONTEXT_SYMBOL_DELTA = 3, - ENET_CONTEXT_SYMBOL_MINIMUM = 1, - ENET_CONTEXT_ESCAPE_MINIMUM = 1, - - ENET_SUBCONTEXT_ORDER = 2, - ENET_SUBCONTEXT_SYMBOL_DELTA = 2, - ENET_SUBCONTEXT_ESCAPE_DELTA = 5 -}; - -/* context exclusion roughly halves compression speed, so disable for now */ -#undef ENET_CONTEXT_EXCLUSION - -typedef struct _ENetRangeCoder -{ - /* only allocate enough symbols for reasonable MTUs, would need to be larger for large file compression */ - ENetSymbol symbols[4096]; -} ENetRangeCoder; - -void * -enet_range_coder_create (void) -{ - ENetRangeCoder * rangeCoder = (ENetRangeCoder *) enet_malloc (sizeof (ENetRangeCoder)); - if (rangeCoder == NULL) - return NULL; - - return rangeCoder; -} - -void -enet_range_coder_destroy (void * context) -{ - ENetRangeCoder * rangeCoder = (ENetRangeCoder *) context; - if (rangeCoder == NULL) - return; - - enet_free (rangeCoder); -} - -#define ENET_SYMBOL_CREATE(symbol, value_, count_) \ -{ \ - symbol = & rangeCoder -> symbols [nextSymbol ++]; \ - symbol -> value = value_; \ - symbol -> count = count_; \ - symbol -> under = count_; \ - symbol -> left = 0; \ - symbol -> right = 0; \ - symbol -> symbols = 0; \ - symbol -> escapes = 0; \ - symbol -> total = 0; \ - symbol -> parent = 0; \ -} - -#define ENET_CONTEXT_CREATE(context, escapes_, minimum) \ -{ \ - ENET_SYMBOL_CREATE (context, 0, 0); \ - (context) -> escapes = escapes_; \ - (context) -> total = escapes_ + 256*minimum; \ - (context) -> symbols = 0; \ -} - -static enet_uint16 -enet_symbol_rescale (ENetSymbol * symbol) -{ - enet_uint16 total = 0; - for (;;) - { - symbol -> count -= symbol->count >> 1; - symbol -> under = symbol -> count; - if (symbol -> left) - symbol -> under += enet_symbol_rescale (symbol + symbol -> left); - total += symbol -> under; - if (! symbol -> right) break; - symbol += symbol -> right; - } - return total; -} - -#define ENET_CONTEXT_RESCALE(context, minimum) \ -{ \ - (context) -> total = (context) -> symbols ? enet_symbol_rescale ((context) + (context) -> symbols) : 0; \ - (context) -> escapes -= (context) -> escapes >> 1; \ - (context) -> total += (context) -> escapes + 256*minimum; \ -} - -#define ENET_RANGE_CODER_OUTPUT(value) \ -{ \ - if (outData >= outEnd) \ - return 0; \ - * outData ++ = value; \ -} - -#define ENET_RANGE_CODER_ENCODE(under, count, total) \ -{ \ - encodeRange /= (total); \ - encodeLow += (under) * encodeRange; \ - encodeRange *= (count); \ - for (;;) \ - { \ - if((encodeLow ^ (encodeLow + encodeRange)) >= ENET_RANGE_CODER_TOP) \ - { \ - if(encodeRange >= ENET_RANGE_CODER_BOTTOM) break; \ - encodeRange = -encodeLow & (ENET_RANGE_CODER_BOTTOM - 1); \ - } \ - ENET_RANGE_CODER_OUTPUT (encodeLow >> 24); \ - encodeRange <<= 8; \ - encodeLow <<= 8; \ - } \ -} - -#define ENET_RANGE_CODER_FLUSH \ -{ \ - while (encodeLow) \ - { \ - ENET_RANGE_CODER_OUTPUT (encodeLow >> 24); \ - encodeLow <<= 8; \ - } \ -} - -#define ENET_RANGE_CODER_FREE_SYMBOLS \ -{ \ - if (nextSymbol >= sizeof (rangeCoder -> symbols) / sizeof (ENetSymbol) - ENET_SUBCONTEXT_ORDER ) \ - { \ - nextSymbol = 0; \ - ENET_CONTEXT_CREATE (root, ENET_CONTEXT_ESCAPE_MINIMUM, ENET_CONTEXT_SYMBOL_MINIMUM); \ - predicted = 0; \ - order = 0; \ - } \ -} - -#define ENET_CONTEXT_ENCODE(context, symbol_, value_, under_, count_, update, minimum) \ -{ \ - under_ = value*minimum; \ - count_ = minimum; \ - if (! (context) -> symbols) \ - { \ - ENET_SYMBOL_CREATE (symbol_, value_, update); \ - (context) -> symbols = symbol_ - (context); \ - } \ - else \ - { \ - ENetSymbol * node = (context) + (context) -> symbols; \ - for (;;) \ - { \ - if (value_ < node -> value) \ - { \ - node -> under += update; \ - if (node -> left) { node += node -> left; continue; } \ - ENET_SYMBOL_CREATE (symbol_, value_, update); \ - node -> left = symbol_ - node; \ - } \ - else \ - if (value_ > node -> value) \ - { \ - under_ += node -> under; \ - if (node -> right) { node += node -> right; continue; } \ - ENET_SYMBOL_CREATE (symbol_, value_, update); \ - node -> right = symbol_ - node; \ - } \ - else \ - { \ - count_ += node -> count; \ - under_ += node -> under - node -> count; \ - node -> under += update; \ - node -> count += update; \ - symbol_ = node; \ - } \ - break; \ - } \ - } \ -} - -#ifdef ENET_CONTEXT_EXCLUSION -static const ENetSymbol emptyContext = { 0, 0, 0, 0, 0, 0, 0, 0, 0 }; - -#define ENET_CONTEXT_WALK(context, body) \ -{ \ - const ENetSymbol * node = (context) + (context) -> symbols; \ - const ENetSymbol * stack [256]; \ - size_t stackSize = 0; \ - while (node -> left) \ - { \ - stack [stackSize ++] = node; \ - node += node -> left; \ - } \ - for (;;) \ - { \ - body; \ - if (node -> right) \ - { \ - node += node -> right; \ - while (node -> left) \ - { \ - stack [stackSize ++] = node; \ - node += node -> left; \ - } \ - } \ - else \ - if (stackSize <= 0) \ - break; \ - else \ - node = stack [-- stackSize]; \ - } \ -} - -#define ENET_CONTEXT_ENCODE_EXCLUDE(context, value_, under, total, minimum) \ -ENET_CONTEXT_WALK(context, { \ - if (node -> value != value_) \ - { \ - enet_uint16 parentCount = rangeCoder -> symbols [node -> parent].count + minimum; \ - if (node -> value < value_) \ - under -= parentCount; \ - total -= parentCount; \ - } \ -}) -#endif - -size_t -enet_range_coder_compress (void * context, const ENetBuffer * inBuffers, size_t inBufferCount, size_t inLimit, enet_uint8 * outData, size_t outLimit) -{ - ENetRangeCoder * rangeCoder = (ENetRangeCoder *) context; - enet_uint8 * outStart = outData, * outEnd = & outData [outLimit]; - const enet_uint8 * inData, * inEnd; - enet_uint32 encodeLow = 0, encodeRange = ~0; - ENetSymbol * root; - enet_uint16 predicted = 0; - size_t order = 0, nextSymbol = 0; - - if (rangeCoder == NULL || inBufferCount <= 0 || inLimit <= 0) - return 0; - - inData = (const enet_uint8 *) inBuffers -> data; - inEnd = & inData [inBuffers -> dataLength]; - inBuffers ++; - inBufferCount --; - - ENET_CONTEXT_CREATE (root, ENET_CONTEXT_ESCAPE_MINIMUM, ENET_CONTEXT_SYMBOL_MINIMUM); - - for (;;) - { - ENetSymbol * subcontext, * symbol; -#ifdef ENET_CONTEXT_EXCLUSION - const ENetSymbol * childContext = & emptyContext; -#endif - enet_uint8 value; - enet_uint16 count, under, * parent = & predicted, total; - if (inData >= inEnd) - { - if (inBufferCount <= 0) - break; - inData = (const enet_uint8 *) inBuffers -> data; - inEnd = & inData [inBuffers -> dataLength]; - inBuffers ++; - inBufferCount --; - } - value = * inData ++; - - for (subcontext = & rangeCoder -> symbols [predicted]; - subcontext != root; -#ifdef ENET_CONTEXT_EXCLUSION - childContext = subcontext, -#endif - subcontext = & rangeCoder -> symbols [subcontext -> parent]) - { - ENET_CONTEXT_ENCODE (subcontext, symbol, value, under, count, ENET_SUBCONTEXT_SYMBOL_DELTA, 0); - * parent = symbol - rangeCoder -> symbols; - parent = & symbol -> parent; - total = subcontext -> total; -#ifdef ENET_CONTEXT_EXCLUSION - if (childContext -> total > ENET_SUBCONTEXT_SYMBOL_DELTA + ENET_SUBCONTEXT_ESCAPE_DELTA) - ENET_CONTEXT_ENCODE_EXCLUDE (childContext, value, under, total, 0); -#endif - if (count > 0) - { - ENET_RANGE_CODER_ENCODE (subcontext -> escapes + under, count, total); - } - else - { - if (subcontext -> escapes > 0 && subcontext -> escapes < total) - ENET_RANGE_CODER_ENCODE (0, subcontext -> escapes, total); - subcontext -> escapes += ENET_SUBCONTEXT_ESCAPE_DELTA; - subcontext -> total += ENET_SUBCONTEXT_ESCAPE_DELTA; - } - subcontext -> total += ENET_SUBCONTEXT_SYMBOL_DELTA; - if (count > 0xFF - 2*ENET_SUBCONTEXT_SYMBOL_DELTA || subcontext -> total > ENET_RANGE_CODER_BOTTOM - 0x100) - ENET_CONTEXT_RESCALE (subcontext, 0); - if (count > 0) goto nextInput; - } - - ENET_CONTEXT_ENCODE (root, symbol, value, under, count, ENET_CONTEXT_SYMBOL_DELTA, ENET_CONTEXT_SYMBOL_MINIMUM); - * parent = symbol - rangeCoder -> symbols; - parent = & symbol -> parent; - total = root -> total; -#ifdef ENET_CONTEXT_EXCLUSION - if (childContext -> total > ENET_SUBCONTEXT_SYMBOL_DELTA + ENET_SUBCONTEXT_ESCAPE_DELTA) - ENET_CONTEXT_ENCODE_EXCLUDE (childContext, value, under, total, ENET_CONTEXT_SYMBOL_MINIMUM); -#endif - ENET_RANGE_CODER_ENCODE (root -> escapes + under, count, total); - root -> total += ENET_CONTEXT_SYMBOL_DELTA; - if (count > 0xFF - 2*ENET_CONTEXT_SYMBOL_DELTA + ENET_CONTEXT_SYMBOL_MINIMUM || root -> total > ENET_RANGE_CODER_BOTTOM - 0x100) - ENET_CONTEXT_RESCALE (root, ENET_CONTEXT_SYMBOL_MINIMUM); - - nextInput: - if (order >= ENET_SUBCONTEXT_ORDER) - predicted = rangeCoder -> symbols [predicted].parent; - else - order ++; - ENET_RANGE_CODER_FREE_SYMBOLS; - } - - ENET_RANGE_CODER_FLUSH; - - return (size_t) (outData - outStart); -} - -#define ENET_RANGE_CODER_SEED \ -{ \ - if (inData < inEnd) decodeCode |= * inData ++ << 24; \ - if (inData < inEnd) decodeCode |= * inData ++ << 16; \ - if (inData < inEnd) decodeCode |= * inData ++ << 8; \ - if (inData < inEnd) decodeCode |= * inData ++; \ -} - -#define ENET_RANGE_CODER_READ(total) ((decodeCode - decodeLow) / (decodeRange /= (total))) - -#define ENET_RANGE_CODER_DECODE(under, count, total) \ -{ \ - decodeLow += (under) * decodeRange; \ - decodeRange *= (count); \ - for (;;) \ - { \ - if((decodeLow ^ (decodeLow + decodeRange)) >= ENET_RANGE_CODER_TOP) \ - { \ - if(decodeRange >= ENET_RANGE_CODER_BOTTOM) break; \ - decodeRange = -decodeLow & (ENET_RANGE_CODER_BOTTOM - 1); \ - } \ - decodeCode <<= 8; \ - if (inData < inEnd) \ - decodeCode |= * inData ++; \ - decodeRange <<= 8; \ - decodeLow <<= 8; \ - } \ -} - -#define ENET_CONTEXT_DECODE(context, symbol_, code, value_, under_, count_, update, minimum, createRoot, visitNode, createRight, createLeft) \ -{ \ - under_ = 0; \ - count_ = minimum; \ - if (! (context) -> symbols) \ - { \ - createRoot; \ - } \ - else \ - { \ - ENetSymbol * node = (context) + (context) -> symbols; \ - for (;;) \ - { \ - enet_uint16 after = under_ + node -> under + (node -> value + 1)*minimum, before = node -> count + minimum; \ - visitNode; \ - if (code >= after) \ - { \ - under_ += node -> under; \ - if (node -> right) { node += node -> right; continue; } \ - createRight; \ - } \ - else \ - if (code < after - before) \ - { \ - node -> under += update; \ - if (node -> left) { node += node -> left; continue; } \ - createLeft; \ - } \ - else \ - { \ - value_ = node -> value; \ - count_ += node -> count; \ - under_ = after - before; \ - node -> under += update; \ - node -> count += update; \ - symbol_ = node; \ - } \ - break; \ - } \ - } \ -} - -#define ENET_CONTEXT_TRY_DECODE(context, symbol_, code, value_, under_, count_, update, minimum, exclude) \ -ENET_CONTEXT_DECODE (context, symbol_, code, value_, under_, count_, update, minimum, return 0, exclude (node -> value, after, before), return 0, return 0) - -#define ENET_CONTEXT_ROOT_DECODE(context, symbol_, code, value_, under_, count_, update, minimum, exclude) \ -ENET_CONTEXT_DECODE (context, symbol_, code, value_, under_, count_, update, minimum, \ - { \ - value_ = code / minimum; \ - under_ = code - code%minimum; \ - ENET_SYMBOL_CREATE (symbol_, value_, update); \ - (context) -> symbols = symbol_ - (context); \ - }, \ - exclude (node -> value, after, before), \ - { \ - value_ = node->value + 1 + (code - after)/minimum; \ - under_ = code - (code - after)%minimum; \ - ENET_SYMBOL_CREATE (symbol_, value_, update); \ - node -> right = symbol_ - node; \ - }, \ - { \ - value_ = node->value - 1 - (after - before - code - 1)/minimum; \ - under_ = code - (after - before - code - 1)%minimum; \ - ENET_SYMBOL_CREATE (symbol_, value_, update); \ - node -> left = symbol_ - node; \ - }) \ - -#ifdef ENET_CONTEXT_EXCLUSION -typedef struct _ENetExclude -{ - enet_uint8 value; - enet_uint16 under; -} ENetExclude; - -#define ENET_CONTEXT_DECODE_EXCLUDE(context, total, minimum) \ -{ \ - enet_uint16 under = 0; \ - nextExclude = excludes; \ - ENET_CONTEXT_WALK (context, { \ - under += rangeCoder -> symbols [node -> parent].count + minimum; \ - nextExclude -> value = node -> value; \ - nextExclude -> under = under; \ - nextExclude ++; \ - }); \ - total -= under; \ -} - -#define ENET_CONTEXT_EXCLUDED(value_, after, before) \ -{ \ - size_t low = 0, high = nextExclude - excludes; \ - for(;;) \ - { \ - size_t mid = (low + high) >> 1; \ - const ENetExclude * exclude = & excludes [mid]; \ - if (value_ < exclude -> value) \ - { \ - if (low + 1 < high) \ - { \ - high = mid; \ - continue; \ - } \ - if (exclude > excludes) \ - after -= exclude [-1].under; \ - } \ - else \ - { \ - if (value_ > exclude -> value) \ - { \ - if (low + 1 < high) \ - { \ - low = mid; \ - continue; \ - } \ - } \ - else \ - before = 0; \ - after -= exclude -> under; \ - } \ - break; \ - } \ -} -#endif - -#define ENET_CONTEXT_NOT_EXCLUDED(value_, after, before) - -size_t -enet_range_coder_decompress (void * context, const enet_uint8 * inData, size_t inLimit, enet_uint8 * outData, size_t outLimit) -{ - ENetRangeCoder * rangeCoder = (ENetRangeCoder *) context; - enet_uint8 * outStart = outData, * outEnd = & outData [outLimit]; - const enet_uint8 * inEnd = & inData [inLimit]; - enet_uint32 decodeLow = 0, decodeCode = 0, decodeRange = ~0; - ENetSymbol * root; - enet_uint16 predicted = 0; - size_t order = 0, nextSymbol = 0; -#ifdef ENET_CONTEXT_EXCLUSION - ENetExclude excludes [256]; - ENetExclude * nextExclude = excludes; -#endif - - if (rangeCoder == NULL || inLimit <= 0) - return 0; - - ENET_CONTEXT_CREATE (root, ENET_CONTEXT_ESCAPE_MINIMUM, ENET_CONTEXT_SYMBOL_MINIMUM); - - ENET_RANGE_CODER_SEED; - - for (;;) - { - ENetSymbol * subcontext, * symbol, * patch; -#ifdef ENET_CONTEXT_EXCLUSION - const ENetSymbol * childContext = & emptyContext; -#endif - enet_uint8 value = 0; - enet_uint16 code, under, count, bottom, * parent = & predicted, total; - - for (subcontext = & rangeCoder -> symbols [predicted]; - subcontext != root; -#ifdef ENET_CONTEXT_EXCLUSION - childContext = subcontext, -#endif - subcontext = & rangeCoder -> symbols [subcontext -> parent]) - { - if (subcontext -> escapes <= 0) - continue; - total = subcontext -> total; -#ifdef ENET_CONTEXT_EXCLUSION - if (childContext -> total > 0) - ENET_CONTEXT_DECODE_EXCLUDE (childContext, total, 0); -#endif - if (subcontext -> escapes >= total) - continue; - code = ENET_RANGE_CODER_READ (total); - if (code < subcontext -> escapes) - { - ENET_RANGE_CODER_DECODE (0, subcontext -> escapes, total); - continue; - } - code -= subcontext -> escapes; -#ifdef ENET_CONTEXT_EXCLUSION - if (childContext -> total > 0) - { - ENET_CONTEXT_TRY_DECODE (subcontext, symbol, code, value, under, count, ENET_SUBCONTEXT_SYMBOL_DELTA, 0, ENET_CONTEXT_EXCLUDED); - } - else -#endif - { - ENET_CONTEXT_TRY_DECODE (subcontext, symbol, code, value, under, count, ENET_SUBCONTEXT_SYMBOL_DELTA, 0, ENET_CONTEXT_NOT_EXCLUDED); - } - bottom = symbol - rangeCoder -> symbols; - ENET_RANGE_CODER_DECODE (subcontext -> escapes + under, count, total); - subcontext -> total += ENET_SUBCONTEXT_SYMBOL_DELTA; - if (count > 0xFF - 2*ENET_SUBCONTEXT_SYMBOL_DELTA || subcontext -> total > ENET_RANGE_CODER_BOTTOM - 0x100) - ENET_CONTEXT_RESCALE (subcontext, 0); - goto patchContexts; - } - - total = root -> total; -#ifdef ENET_CONTEXT_EXCLUSION - if (childContext -> total > 0) - ENET_CONTEXT_DECODE_EXCLUDE (childContext, total, ENET_CONTEXT_SYMBOL_MINIMUM); -#endif - code = ENET_RANGE_CODER_READ (total); - if (code < root -> escapes) - { - ENET_RANGE_CODER_DECODE (0, root -> escapes, total); - break; - } - code -= root -> escapes; -#ifdef ENET_CONTEXT_EXCLUSION - if (childContext -> total > 0) - { - ENET_CONTEXT_ROOT_DECODE (root, symbol, code, value, under, count, ENET_CONTEXT_SYMBOL_DELTA, ENET_CONTEXT_SYMBOL_MINIMUM, ENET_CONTEXT_EXCLUDED); - } - else -#endif - { - ENET_CONTEXT_ROOT_DECODE (root, symbol, code, value, under, count, ENET_CONTEXT_SYMBOL_DELTA, ENET_CONTEXT_SYMBOL_MINIMUM, ENET_CONTEXT_NOT_EXCLUDED); - } - bottom = symbol - rangeCoder -> symbols; - ENET_RANGE_CODER_DECODE (root -> escapes + under, count, total); - root -> total += ENET_CONTEXT_SYMBOL_DELTA; - if (count > 0xFF - 2*ENET_CONTEXT_SYMBOL_DELTA + ENET_CONTEXT_SYMBOL_MINIMUM || root -> total > ENET_RANGE_CODER_BOTTOM - 0x100) - ENET_CONTEXT_RESCALE (root, ENET_CONTEXT_SYMBOL_MINIMUM); - - patchContexts: - for (patch = & rangeCoder -> symbols [predicted]; - patch != subcontext; - patch = & rangeCoder -> symbols [patch -> parent]) - { - ENET_CONTEXT_ENCODE (patch, symbol, value, under, count, ENET_SUBCONTEXT_SYMBOL_DELTA, 0); - * parent = symbol - rangeCoder -> symbols; - parent = & symbol -> parent; - if (count <= 0) - { - patch -> escapes += ENET_SUBCONTEXT_ESCAPE_DELTA; - patch -> total += ENET_SUBCONTEXT_ESCAPE_DELTA; - } - patch -> total += ENET_SUBCONTEXT_SYMBOL_DELTA; - if (count > 0xFF - 2*ENET_SUBCONTEXT_SYMBOL_DELTA || patch -> total > ENET_RANGE_CODER_BOTTOM - 0x100) - ENET_CONTEXT_RESCALE (patch, 0); - } - * parent = bottom; - - ENET_RANGE_CODER_OUTPUT (value); - - if (order >= ENET_SUBCONTEXT_ORDER) - predicted = rangeCoder -> symbols [predicted].parent; - else - order ++; - ENET_RANGE_CODER_FREE_SYMBOLS; - } - - return (size_t) (outData - outStart); -} - -/** @defgroup host ENet host functions - @{ -*/ - -/** Sets the packet compressor the host should use to the default range coder. - @param host host to enable the range coder for - @returns 0 on success, < 0 on failure -*/ -int -enet_host_compress_with_range_coder (ENetHost * host) -{ - ENetCompressor compressor; - memset (& compressor, 0, sizeof (compressor)); - compressor.context = enet_range_coder_create(); - if (compressor.context == NULL) - return -1; - compressor.compress = enet_range_coder_compress; - compressor.decompress = enet_range_coder_decompress; - compressor.destroy = enet_range_coder_destroy; - enet_host_compress (host, & compressor); - return 0; -} - -/** @} */ - - diff --git a/include/enet/configure.ac b/include/enet/configure.ac deleted file mode 100644 index a36ae9b..0000000 --- a/include/enet/configure.ac +++ /dev/null @@ -1,28 +0,0 @@ -AC_INIT([libenet], [1.3.16]) -AC_CONFIG_SRCDIR([include/enet/enet.h]) -AM_INIT_AUTOMAKE([foreign]) - -AC_CONFIG_MACRO_DIR([m4]) - -AC_PROG_CC -AC_PROG_LIBTOOL - -AC_CHECK_FUNC(getaddrinfo, [AC_DEFINE(HAS_GETADDRINFO)]) -AC_CHECK_FUNC(getnameinfo, [AC_DEFINE(HAS_GETNAMEINFO)]) -AC_CHECK_FUNC(gethostbyaddr_r, [AC_DEFINE(HAS_GETHOSTBYADDR_R)]) -AC_CHECK_FUNC(gethostbyname_r, [AC_DEFINE(HAS_GETHOSTBYNAME_R)]) -AC_CHECK_FUNC(poll, [AC_DEFINE(HAS_POLL)]) -AC_CHECK_FUNC(fcntl, [AC_DEFINE(HAS_FCNTL)]) -AC_CHECK_FUNC(inet_pton, [AC_DEFINE(HAS_INET_PTON)]) -AC_CHECK_FUNC(inet_ntop, [AC_DEFINE(HAS_INET_NTOP)]) - -AC_CHECK_MEMBER(struct msghdr.msg_flags, [AC_DEFINE(HAS_MSGHDR_FLAGS)], , [#include ]) - -AC_CHECK_TYPE(socklen_t, [AC_DEFINE(HAS_SOCKLEN_T)], , - #include - #include -) - -AC_CONFIG_FILES([Makefile - libenet.pc]) -AC_OUTPUT diff --git a/include/enet/host.c b/include/enet/host.c deleted file mode 100644 index 3b2180f..0000000 --- a/include/enet/host.c +++ /dev/null @@ -1,491 +0,0 @@ -/** - @file host.c - @brief ENet host management functions -*/ -#define ENET_BUILDING_LIB 1 -#include -#include "enet/enet.h" - -/** @defgroup host ENet host functions - @{ -*/ - -/** Creates a host for communicating to peers. - - @param address the address at which other peers may connect to this host. If NULL, then no peers may connect to the host. - @param peerCount the maximum number of peers that should be allocated for the host. - @param channelLimit the maximum number of channels allowed; if 0, then this is equivalent to ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT - @param incomingBandwidth downstream bandwidth of the host in bytes/second; if 0, ENet will assume unlimited bandwidth. - @param outgoingBandwidth upstream bandwidth of the host in bytes/second; if 0, ENet will assume unlimited bandwidth. - - @returns the host on success and NULL on failure - - @remarks ENet will strategically drop packets on specific sides of a connection between hosts - to ensure the host's bandwidth is not overwhelmed. The bandwidth parameters also determine - the window size of a connection which limits the amount of reliable packets that may be in transit - at any given time. -*/ -ENetHost * -enet_host_create (const ENetAddress * address, size_t peerCount, size_t channelLimit, enet_uint32 incomingBandwidth, enet_uint32 outgoingBandwidth) -{ - ENetHost * host; - ENetPeer * currentPeer; - - if (peerCount > ENET_PROTOCOL_MAXIMUM_PEER_ID) - return NULL; - - host = (ENetHost *) enet_malloc (sizeof (ENetHost)); - if (host == NULL) - return NULL; - memset (host, 0, sizeof (ENetHost)); - - host -> peers = (ENetPeer *) enet_malloc (peerCount * sizeof (ENetPeer)); - if (host -> peers == NULL) - { - enet_free (host); - - return NULL; - } - memset (host -> peers, 0, peerCount * sizeof (ENetPeer)); - - host -> socket = enet_socket_create (ENET_SOCKET_TYPE_DATAGRAM); - if (host -> socket == ENET_SOCKET_NULL || (address != NULL && enet_socket_bind (host -> socket, address) < 0)) - { - if (host -> socket != ENET_SOCKET_NULL) - enet_socket_destroy (host -> socket); - - enet_free (host -> peers); - enet_free (host); - - return NULL; - } - - enet_socket_set_option (host -> socket, ENET_SOCKOPT_NONBLOCK, 1); - enet_socket_set_option (host -> socket, ENET_SOCKOPT_BROADCAST, 1); - enet_socket_set_option (host -> socket, ENET_SOCKOPT_RCVBUF, ENET_HOST_RECEIVE_BUFFER_SIZE); - enet_socket_set_option (host -> socket, ENET_SOCKOPT_SNDBUF, ENET_HOST_SEND_BUFFER_SIZE); - - if (address != NULL && enet_socket_get_address (host -> socket, & host -> address) < 0) - host -> address = * address; - - if (! channelLimit || channelLimit > ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT) - channelLimit = ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT; - else - if (channelLimit < ENET_PROTOCOL_MINIMUM_CHANNEL_COUNT) - channelLimit = ENET_PROTOCOL_MINIMUM_CHANNEL_COUNT; - - host -> randomSeed = (enet_uint32) (size_t) host; - host -> randomSeed += enet_host_random_seed (); - host -> randomSeed = (host -> randomSeed << 16) | (host -> randomSeed >> 16); - host -> channelLimit = channelLimit; - host -> incomingBandwidth = incomingBandwidth; - host -> outgoingBandwidth = outgoingBandwidth; - host -> bandwidthThrottleEpoch = 0; - host -> recalculateBandwidthLimits = 0; - host -> mtu = ENET_HOST_DEFAULT_MTU; - host -> peerCount = peerCount; - host -> commandCount = 0; - host -> bufferCount = 0; - host -> checksum = NULL; - host -> receivedAddress.host = ENET_HOST_ANY; - host -> receivedAddress.port = 0; - host -> receivedData = NULL; - host -> receivedDataLength = 0; - - host -> totalSentData = 0; - host -> totalSentPackets = 0; - host -> totalReceivedData = 0; - host -> totalReceivedPackets = 0; - - host -> connectedPeers = 0; - host -> bandwidthLimitedPeers = 0; - host -> duplicatePeers = ENET_PROTOCOL_MAXIMUM_PEER_ID; - host -> maximumPacketSize = ENET_HOST_DEFAULT_MAXIMUM_PACKET_SIZE; - host -> maximumWaitingData = ENET_HOST_DEFAULT_MAXIMUM_WAITING_DATA; - - host -> compressor.context = NULL; - host -> compressor.compress = NULL; - host -> compressor.decompress = NULL; - host -> compressor.destroy = NULL; - - host -> intercept = NULL; - - enet_list_clear (& host -> dispatchQueue); - - for (currentPeer = host -> peers; - currentPeer < & host -> peers [host -> peerCount]; - ++ currentPeer) - { - currentPeer -> host = host; - currentPeer -> incomingPeerID = currentPeer - host -> peers; - currentPeer -> outgoingSessionID = currentPeer -> incomingSessionID = 0xFF; - currentPeer -> data = NULL; - - enet_list_clear (& currentPeer -> acknowledgements); - enet_list_clear (& currentPeer -> sentReliableCommands); - enet_list_clear (& currentPeer -> sentUnreliableCommands); - enet_list_clear (& currentPeer -> outgoingCommands); - enet_list_clear (& currentPeer -> dispatchedCommands); - - enet_peer_reset (currentPeer); - } - - return host; -} - -/** Destroys the host and all resources associated with it. - @param host pointer to the host to destroy -*/ -void -enet_host_destroy (ENetHost * host) -{ - ENetPeer * currentPeer; - - if (host == NULL) - return; - - enet_socket_destroy (host -> socket); - - for (currentPeer = host -> peers; - currentPeer < & host -> peers [host -> peerCount]; - ++ currentPeer) - { - enet_peer_reset (currentPeer); - } - - if (host -> compressor.context != NULL && host -> compressor.destroy) - (* host -> compressor.destroy) (host -> compressor.context); - - enet_free (host -> peers); - enet_free (host); -} - -/** Initiates a connection to a foreign host. - @param host host seeking the connection - @param address destination for the connection - @param channelCount number of channels to allocate - @param data user data supplied to the receiving host - @returns a peer representing the foreign host on success, NULL on failure - @remarks The peer returned will have not completed the connection until enet_host_service() - notifies of an ENET_EVENT_TYPE_CONNECT event for the peer. -*/ -ENetPeer * -enet_host_connect (ENetHost * host, const ENetAddress * address, size_t channelCount, enet_uint32 data) -{ - ENetPeer * currentPeer; - ENetChannel * channel; - ENetProtocol command; - - if (channelCount < ENET_PROTOCOL_MINIMUM_CHANNEL_COUNT) - channelCount = ENET_PROTOCOL_MINIMUM_CHANNEL_COUNT; - else - if (channelCount > ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT) - channelCount = ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT; - - for (currentPeer = host -> peers; - currentPeer < & host -> peers [host -> peerCount]; - ++ currentPeer) - { - if (currentPeer -> state == ENET_PEER_STATE_DISCONNECTED) - break; - } - - if (currentPeer >= & host -> peers [host -> peerCount]) - return NULL; - - currentPeer -> channels = (ENetChannel *) enet_malloc (channelCount * sizeof (ENetChannel)); - if (currentPeer -> channels == NULL) - return NULL; - currentPeer -> channelCount = channelCount; - currentPeer -> state = ENET_PEER_STATE_CONNECTING; - currentPeer -> address = * address; - currentPeer -> connectID = ++ host -> randomSeed; - - if (host -> outgoingBandwidth == 0) - currentPeer -> windowSize = ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE; - else - currentPeer -> windowSize = (host -> outgoingBandwidth / - ENET_PEER_WINDOW_SIZE_SCALE) * - ENET_PROTOCOL_MINIMUM_WINDOW_SIZE; - - if (currentPeer -> windowSize < ENET_PROTOCOL_MINIMUM_WINDOW_SIZE) - currentPeer -> windowSize = ENET_PROTOCOL_MINIMUM_WINDOW_SIZE; - else - if (currentPeer -> windowSize > ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE) - currentPeer -> windowSize = ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE; - - for (channel = currentPeer -> channels; - channel < & currentPeer -> channels [channelCount]; - ++ channel) - { - channel -> outgoingReliableSequenceNumber = 0; - channel -> outgoingUnreliableSequenceNumber = 0; - channel -> incomingReliableSequenceNumber = 0; - channel -> incomingUnreliableSequenceNumber = 0; - - enet_list_clear (& channel -> incomingReliableCommands); - enet_list_clear (& channel -> incomingUnreliableCommands); - - channel -> usedReliableWindows = 0; - memset (channel -> reliableWindows, 0, sizeof (channel -> reliableWindows)); - } - - command.header.command = ENET_PROTOCOL_COMMAND_CONNECT | ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE; - command.header.channelID = 0xFF; - command.connect.outgoingPeerID = ENET_HOST_TO_NET_16 (currentPeer -> incomingPeerID); - command.connect.incomingSessionID = currentPeer -> incomingSessionID; - command.connect.outgoingSessionID = currentPeer -> outgoingSessionID; - command.connect.mtu = ENET_HOST_TO_NET_32 (currentPeer -> mtu); - command.connect.windowSize = ENET_HOST_TO_NET_32 (currentPeer -> windowSize); - command.connect.channelCount = ENET_HOST_TO_NET_32 (channelCount); - command.connect.incomingBandwidth = ENET_HOST_TO_NET_32 (host -> incomingBandwidth); - command.connect.outgoingBandwidth = ENET_HOST_TO_NET_32 (host -> outgoingBandwidth); - command.connect.packetThrottleInterval = ENET_HOST_TO_NET_32 (currentPeer -> packetThrottleInterval); - command.connect.packetThrottleAcceleration = ENET_HOST_TO_NET_32 (currentPeer -> packetThrottleAcceleration); - command.connect.packetThrottleDeceleration = ENET_HOST_TO_NET_32 (currentPeer -> packetThrottleDeceleration); - command.connect.connectID = currentPeer -> connectID; - command.connect.data = ENET_HOST_TO_NET_32 (data); - - enet_peer_queue_outgoing_command (currentPeer, & command, NULL, 0, 0); - - return currentPeer; -} - -/** Queues a packet to be sent to all peers associated with the host. - @param host host on which to broadcast the packet - @param channelID channel on which to broadcast - @param packet packet to broadcast -*/ -void -enet_host_broadcast (ENetHost * host, enet_uint8 channelID, ENetPacket * packet) -{ - ENetPeer * currentPeer; - - for (currentPeer = host -> peers; - currentPeer < & host -> peers [host -> peerCount]; - ++ currentPeer) - { - if (currentPeer -> state != ENET_PEER_STATE_CONNECTED) - continue; - - enet_peer_send (currentPeer, channelID, packet); - } - - if (packet -> referenceCount == 0) - enet_packet_destroy (packet); -} - -/** Sets the packet compressor the host should use to compress and decompress packets. - @param host host to enable or disable compression for - @param compressor callbacks for for the packet compressor; if NULL, then compression is disabled -*/ -void -enet_host_compress (ENetHost * host, const ENetCompressor * compressor) -{ - if (host -> compressor.context != NULL && host -> compressor.destroy) - (* host -> compressor.destroy) (host -> compressor.context); - - if (compressor) - host -> compressor = * compressor; - else - host -> compressor.context = NULL; -} - -/** Limits the maximum allowed channels of future incoming connections. - @param host host to limit - @param channelLimit the maximum number of channels allowed; if 0, then this is equivalent to ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT -*/ -void -enet_host_channel_limit (ENetHost * host, size_t channelLimit) -{ - if (! channelLimit || channelLimit > ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT) - channelLimit = ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT; - else - if (channelLimit < ENET_PROTOCOL_MINIMUM_CHANNEL_COUNT) - channelLimit = ENET_PROTOCOL_MINIMUM_CHANNEL_COUNT; - - host -> channelLimit = channelLimit; -} - - -/** Adjusts the bandwidth limits of a host. - @param host host to adjust - @param incomingBandwidth new incoming bandwidth - @param outgoingBandwidth new outgoing bandwidth - @remarks the incoming and outgoing bandwidth parameters are identical in function to those - specified in enet_host_create(). -*/ -void -enet_host_bandwidth_limit (ENetHost * host, enet_uint32 incomingBandwidth, enet_uint32 outgoingBandwidth) -{ - host -> incomingBandwidth = incomingBandwidth; - host -> outgoingBandwidth = outgoingBandwidth; - host -> recalculateBandwidthLimits = 1; -} - -void -enet_host_bandwidth_throttle (ENetHost * host) -{ - enet_uint32 timeCurrent = enet_time_get (), - elapsedTime = timeCurrent - host -> bandwidthThrottleEpoch, - peersRemaining = (enet_uint32) host -> connectedPeers, - dataTotal = ~0, - bandwidth = ~0, - throttle = 0, - bandwidthLimit = 0; - int needsAdjustment = host -> bandwidthLimitedPeers > 0 ? 1 : 0; - ENetPeer * peer; - ENetProtocol command; - - if (elapsedTime < ENET_HOST_BANDWIDTH_THROTTLE_INTERVAL) - return; - - host -> bandwidthThrottleEpoch = timeCurrent; - - if (peersRemaining == 0) - return; - - if (host -> outgoingBandwidth != 0) - { - dataTotal = 0; - bandwidth = (host -> outgoingBandwidth * elapsedTime) / 1000; - - for (peer = host -> peers; - peer < & host -> peers [host -> peerCount]; - ++ peer) - { - if (peer -> state != ENET_PEER_STATE_CONNECTED && peer -> state != ENET_PEER_STATE_DISCONNECT_LATER) - continue; - - dataTotal += peer -> outgoingDataTotal; - } - } - - while (peersRemaining > 0 && needsAdjustment != 0) - { - needsAdjustment = 0; - - if (dataTotal <= bandwidth) - throttle = ENET_PEER_PACKET_THROTTLE_SCALE; - else - throttle = (bandwidth * ENET_PEER_PACKET_THROTTLE_SCALE) / dataTotal; - - for (peer = host -> peers; - peer < & host -> peers [host -> peerCount]; - ++ peer) - { - enet_uint32 peerBandwidth; - - if ((peer -> state != ENET_PEER_STATE_CONNECTED && peer -> state != ENET_PEER_STATE_DISCONNECT_LATER) || - peer -> incomingBandwidth == 0 || - peer -> outgoingBandwidthThrottleEpoch == timeCurrent) - continue; - - peerBandwidth = (peer -> incomingBandwidth * elapsedTime) / 1000; - if ((throttle * peer -> outgoingDataTotal) / ENET_PEER_PACKET_THROTTLE_SCALE <= peerBandwidth) - continue; - - peer -> packetThrottleLimit = (peerBandwidth * - ENET_PEER_PACKET_THROTTLE_SCALE) / peer -> outgoingDataTotal; - - if (peer -> packetThrottleLimit == 0) - peer -> packetThrottleLimit = 1; - - if (peer -> packetThrottle > peer -> packetThrottleLimit) - peer -> packetThrottle = peer -> packetThrottleLimit; - - peer -> outgoingBandwidthThrottleEpoch = timeCurrent; - - peer -> incomingDataTotal = 0; - peer -> outgoingDataTotal = 0; - - needsAdjustment = 1; - -- peersRemaining; - bandwidth -= peerBandwidth; - dataTotal -= peerBandwidth; - } - } - - if (peersRemaining > 0) - { - if (dataTotal <= bandwidth) - throttle = ENET_PEER_PACKET_THROTTLE_SCALE; - else - throttle = (bandwidth * ENET_PEER_PACKET_THROTTLE_SCALE) / dataTotal; - - for (peer = host -> peers; - peer < & host -> peers [host -> peerCount]; - ++ peer) - { - if ((peer -> state != ENET_PEER_STATE_CONNECTED && peer -> state != ENET_PEER_STATE_DISCONNECT_LATER) || - peer -> outgoingBandwidthThrottleEpoch == timeCurrent) - continue; - - peer -> packetThrottleLimit = throttle; - - if (peer -> packetThrottle > peer -> packetThrottleLimit) - peer -> packetThrottle = peer -> packetThrottleLimit; - - peer -> incomingDataTotal = 0; - peer -> outgoingDataTotal = 0; - } - } - - if (host -> recalculateBandwidthLimits) - { - host -> recalculateBandwidthLimits = 0; - - peersRemaining = (enet_uint32) host -> connectedPeers; - bandwidth = host -> incomingBandwidth; - needsAdjustment = 1; - - if (bandwidth == 0) - bandwidthLimit = 0; - else - while (peersRemaining > 0 && needsAdjustment != 0) - { - needsAdjustment = 0; - bandwidthLimit = bandwidth / peersRemaining; - - for (peer = host -> peers; - peer < & host -> peers [host -> peerCount]; - ++ peer) - { - if ((peer -> state != ENET_PEER_STATE_CONNECTED && peer -> state != ENET_PEER_STATE_DISCONNECT_LATER) || - peer -> incomingBandwidthThrottleEpoch == timeCurrent) - continue; - - if (peer -> outgoingBandwidth > 0 && - peer -> outgoingBandwidth >= bandwidthLimit) - continue; - - peer -> incomingBandwidthThrottleEpoch = timeCurrent; - - needsAdjustment = 1; - -- peersRemaining; - bandwidth -= peer -> outgoingBandwidth; - } - } - - for (peer = host -> peers; - peer < & host -> peers [host -> peerCount]; - ++ peer) - { - if (peer -> state != ENET_PEER_STATE_CONNECTED && peer -> state != ENET_PEER_STATE_DISCONNECT_LATER) - continue; - - command.header.command = ENET_PROTOCOL_COMMAND_BANDWIDTH_LIMIT | ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE; - command.header.channelID = 0xFF; - command.bandwidthLimit.outgoingBandwidth = ENET_HOST_TO_NET_32 (host -> outgoingBandwidth); - - if (peer -> incomingBandwidthThrottleEpoch == timeCurrent) - command.bandwidthLimit.incomingBandwidth = ENET_HOST_TO_NET_32 (peer -> outgoingBandwidth); - else - command.bandwidthLimit.incomingBandwidth = ENET_HOST_TO_NET_32 (bandwidthLimit); - - enet_peer_queue_outgoing_command (peer, & command, NULL, 0, 0); - } - } -} - -/** @} */ diff --git a/include/enet/include/enet/callbacks.h b/include/enet/include/enet/callbacks.h deleted file mode 100644 index 340a4a9..0000000 --- a/include/enet/include/enet/callbacks.h +++ /dev/null @@ -1,27 +0,0 @@ -/** - @file callbacks.h - @brief ENet callbacks -*/ -#ifndef __ENET_CALLBACKS_H__ -#define __ENET_CALLBACKS_H__ - -#include - -typedef struct _ENetCallbacks -{ - void * (ENET_CALLBACK * malloc) (size_t size); - void (ENET_CALLBACK * free) (void * memory); - void (ENET_CALLBACK * no_memory) (void); -} ENetCallbacks; - -/** @defgroup callbacks ENet internal callbacks - @{ - @ingroup private -*/ -extern void * enet_malloc (size_t); -extern void enet_free (void *); - -/** @} */ - -#endif /* __ENET_CALLBACKS_H__ */ - diff --git a/include/enet/include/enet/enet.h b/include/enet/include/enet/enet.h deleted file mode 100644 index c5b0ec4..0000000 --- a/include/enet/include/enet/enet.h +++ /dev/null @@ -1,612 +0,0 @@ -/** - @file enet.h - @brief ENet public header file -*/ -#ifndef __ENET_ENET_H__ -#define __ENET_ENET_H__ - -#ifdef __cplusplus -extern "C" -{ -#endif - -#include - -#ifdef _WIN32 -#include "enet/win32.h" -#else -#include "enet/unix.h" -#endif - -#include "enet/types.h" -#include "enet/protocol.h" -#include "enet/list.h" -#include "enet/callbacks.h" - -#define ENET_VERSION_MAJOR 1 -#define ENET_VERSION_MINOR 3 -#define ENET_VERSION_PATCH 16 -#define ENET_VERSION_CREATE(major, minor, patch) (((major)<<16) | ((minor)<<8) | (patch)) -#define ENET_VERSION_GET_MAJOR(version) (((version)>>16)&0xFF) -#define ENET_VERSION_GET_MINOR(version) (((version)>>8)&0xFF) -#define ENET_VERSION_GET_PATCH(version) ((version)&0xFF) -#define ENET_VERSION ENET_VERSION_CREATE(ENET_VERSION_MAJOR, ENET_VERSION_MINOR, ENET_VERSION_PATCH) - -typedef enet_uint32 ENetVersion; - -struct _ENetHost; -struct _ENetEvent; -struct _ENetPacket; - -typedef enum _ENetSocketType -{ - ENET_SOCKET_TYPE_STREAM = 1, - ENET_SOCKET_TYPE_DATAGRAM = 2 -} ENetSocketType; - -typedef enum _ENetSocketWait -{ - ENET_SOCKET_WAIT_NONE = 0, - ENET_SOCKET_WAIT_SEND = (1 << 0), - ENET_SOCKET_WAIT_RECEIVE = (1 << 1), - ENET_SOCKET_WAIT_INTERRUPT = (1 << 2) -} ENetSocketWait; - -typedef enum _ENetSocketOption -{ - ENET_SOCKOPT_NONBLOCK = 1, - ENET_SOCKOPT_BROADCAST = 2, - ENET_SOCKOPT_RCVBUF = 3, - ENET_SOCKOPT_SNDBUF = 4, - ENET_SOCKOPT_REUSEADDR = 5, - ENET_SOCKOPT_RCVTIMEO = 6, - ENET_SOCKOPT_SNDTIMEO = 7, - ENET_SOCKOPT_ERROR = 8, - ENET_SOCKOPT_NODELAY = 9 -} ENetSocketOption; - -typedef enum _ENetSocketShutdown -{ - ENET_SOCKET_SHUTDOWN_READ = 0, - ENET_SOCKET_SHUTDOWN_WRITE = 1, - ENET_SOCKET_SHUTDOWN_READ_WRITE = 2 -} ENetSocketShutdown; - -#define ENET_HOST_ANY 0 -#define ENET_HOST_BROADCAST 0xFFFFFFFFU -#define ENET_PORT_ANY 0 - -/** - * Portable internet address structure. - * - * The host must be specified in network byte-order, and the port must be in host - * byte-order. The constant ENET_HOST_ANY may be used to specify the default - * server host. The constant ENET_HOST_BROADCAST may be used to specify the - * broadcast address (255.255.255.255). This makes sense for enet_host_connect, - * but not for enet_host_create. Once a server responds to a broadcast, the - * address is updated from ENET_HOST_BROADCAST to the server's actual IP address. - */ -typedef struct _ENetAddress -{ - enet_uint32 host; - enet_uint16 port; -} ENetAddress; - -/** - * Packet flag bit constants. - * - * The host must be specified in network byte-order, and the port must be in - * host byte-order. The constant ENET_HOST_ANY may be used to specify the - * default server host. - - @sa ENetPacket -*/ -typedef enum _ENetPacketFlag -{ - /** packet must be received by the target peer and resend attempts should be - * made until the packet is delivered */ - ENET_PACKET_FLAG_RELIABLE = (1 << 0), - /** packet will not be sequenced with other packets - * not supported for reliable packets - */ - ENET_PACKET_FLAG_UNSEQUENCED = (1 << 1), - /** packet will not allocate data, and user must supply it instead */ - ENET_PACKET_FLAG_NO_ALLOCATE = (1 << 2), - /** packet will be fragmented using unreliable (instead of reliable) sends - * if it exceeds the MTU */ - ENET_PACKET_FLAG_UNRELIABLE_FRAGMENT = (1 << 3), - - /** whether the packet has been sent from all queues it has been entered into */ - ENET_PACKET_FLAG_SENT = (1<<8) -} ENetPacketFlag; - -typedef void (ENET_CALLBACK * ENetPacketFreeCallback) (struct _ENetPacket *); - -/** - * ENet packet structure. - * - * An ENet data packet that may be sent to or received from a peer. The shown - * fields should only be read and never modified. The data field contains the - * allocated data for the packet. The dataLength fields specifies the length - * of the allocated data. The flags field is either 0 (specifying no flags), - * or a bitwise-or of any combination of the following flags: - * - * ENET_PACKET_FLAG_RELIABLE - packet must be received by the target peer - * and resend attempts should be made until the packet is delivered - * - * ENET_PACKET_FLAG_UNSEQUENCED - packet will not be sequenced with other packets - * (not supported for reliable packets) - * - * ENET_PACKET_FLAG_NO_ALLOCATE - packet will not allocate data, and user must supply it instead - * - * ENET_PACKET_FLAG_UNRELIABLE_FRAGMENT - packet will be fragmented using unreliable - * (instead of reliable) sends if it exceeds the MTU - * - * ENET_PACKET_FLAG_SENT - whether the packet has been sent from all queues it has been entered into - @sa ENetPacketFlag - */ -typedef struct _ENetPacket -{ - size_t referenceCount; /**< internal use only */ - enet_uint32 flags; /**< bitwise-or of ENetPacketFlag constants */ - enet_uint8 * data; /**< allocated data for packet */ - size_t dataLength; /**< length of data */ - ENetPacketFreeCallback freeCallback; /**< function to be called when the packet is no longer in use */ - void * userData; /**< application private data, may be freely modified */ -} ENetPacket; - -typedef struct _ENetAcknowledgement -{ - ENetListNode acknowledgementList; - enet_uint32 sentTime; - ENetProtocol command; -} ENetAcknowledgement; - -typedef struct _ENetOutgoingCommand -{ - ENetListNode outgoingCommandList; - enet_uint16 reliableSequenceNumber; - enet_uint16 unreliableSequenceNumber; - enet_uint32 sentTime; - enet_uint32 roundTripTimeout; - enet_uint32 roundTripTimeoutLimit; - enet_uint32 fragmentOffset; - enet_uint16 fragmentLength; - enet_uint16 sendAttempts; - ENetProtocol command; - ENetPacket * packet; -} ENetOutgoingCommand; - -typedef struct _ENetIncomingCommand -{ - ENetListNode incomingCommandList; - enet_uint16 reliableSequenceNumber; - enet_uint16 unreliableSequenceNumber; - ENetProtocol command; - enet_uint32 fragmentCount; - enet_uint32 fragmentsRemaining; - enet_uint32 * fragments; - ENetPacket * packet; -} ENetIncomingCommand; - -typedef enum _ENetPeerState -{ - ENET_PEER_STATE_DISCONNECTED = 0, - ENET_PEER_STATE_CONNECTING = 1, - ENET_PEER_STATE_ACKNOWLEDGING_CONNECT = 2, - ENET_PEER_STATE_CONNECTION_PENDING = 3, - ENET_PEER_STATE_CONNECTION_SUCCEEDED = 4, - ENET_PEER_STATE_CONNECTED = 5, - ENET_PEER_STATE_DISCONNECT_LATER = 6, - ENET_PEER_STATE_DISCONNECTING = 7, - ENET_PEER_STATE_ACKNOWLEDGING_DISCONNECT = 8, - ENET_PEER_STATE_ZOMBIE = 9 -} ENetPeerState; - -#ifndef ENET_BUFFER_MAXIMUM -#define ENET_BUFFER_MAXIMUM (1 + 2 * ENET_PROTOCOL_MAXIMUM_PACKET_COMMANDS) -#endif - -enum -{ - ENET_HOST_RECEIVE_BUFFER_SIZE = 256 * 1024, - ENET_HOST_SEND_BUFFER_SIZE = 256 * 1024, - ENET_HOST_BANDWIDTH_THROTTLE_INTERVAL = 1000, - ENET_HOST_DEFAULT_MTU = 1400, - ENET_HOST_DEFAULT_MAXIMUM_PACKET_SIZE = 32 * 1024 * 1024, - ENET_HOST_DEFAULT_MAXIMUM_WAITING_DATA = 32 * 1024 * 1024, - - ENET_PEER_DEFAULT_ROUND_TRIP_TIME = 500, - ENET_PEER_DEFAULT_PACKET_THROTTLE = 32, - ENET_PEER_PACKET_THROTTLE_SCALE = 32, - ENET_PEER_PACKET_THROTTLE_COUNTER = 7, - ENET_PEER_PACKET_THROTTLE_ACCELERATION = 2, - ENET_PEER_PACKET_THROTTLE_DECELERATION = 2, - ENET_PEER_PACKET_THROTTLE_INTERVAL = 5000, - ENET_PEER_PACKET_LOSS_SCALE = (1 << 16), - ENET_PEER_PACKET_LOSS_INTERVAL = 10000, - ENET_PEER_WINDOW_SIZE_SCALE = 64 * 1024, - ENET_PEER_TIMEOUT_LIMIT = 32, - ENET_PEER_TIMEOUT_MINIMUM = 5000, - ENET_PEER_TIMEOUT_MAXIMUM = 30000, - ENET_PEER_PING_INTERVAL = 500, - ENET_PEER_UNSEQUENCED_WINDOWS = 64, - ENET_PEER_UNSEQUENCED_WINDOW_SIZE = 1024, - ENET_PEER_FREE_UNSEQUENCED_WINDOWS = 32, - ENET_PEER_RELIABLE_WINDOWS = 16, - ENET_PEER_RELIABLE_WINDOW_SIZE = 0x1000, - ENET_PEER_FREE_RELIABLE_WINDOWS = 8 -}; - -typedef struct _ENetChannel -{ - enet_uint16 outgoingReliableSequenceNumber; - enet_uint16 outgoingUnreliableSequenceNumber; - enet_uint16 usedReliableWindows; - enet_uint16 reliableWindows [ENET_PEER_RELIABLE_WINDOWS]; - enet_uint16 incomingReliableSequenceNumber; - enet_uint16 incomingUnreliableSequenceNumber; - ENetList incomingReliableCommands; - ENetList incomingUnreliableCommands; -} ENetChannel; - -typedef enum _ENetPeerFlag -{ - ENET_PEER_FLAG_NEEDS_DISPATCH = (1 << 0) -} ENetPeerFlag; - -/** - * An ENet peer which data packets may be sent or received from. - * - * No fields should be modified unless otherwise specified. - */ -typedef struct _ENetPeer -{ - ENetListNode dispatchList; - struct _ENetHost * host; - enet_uint16 outgoingPeerID; - enet_uint16 incomingPeerID; - enet_uint32 connectID; - enet_uint8 outgoingSessionID; - enet_uint8 incomingSessionID; - ENetAddress address; /**< Internet address of the peer */ - void * data; /**< Application private data, may be freely modified */ - ENetPeerState state; - ENetChannel * channels; - size_t channelCount; /**< Number of channels allocated for communication with peer */ - enet_uint32 incomingBandwidth; /**< Downstream bandwidth of the client in bytes/second */ - enet_uint32 outgoingBandwidth; /**< Upstream bandwidth of the client in bytes/second */ - enet_uint32 incomingBandwidthThrottleEpoch; - enet_uint32 outgoingBandwidthThrottleEpoch; - enet_uint32 incomingDataTotal; - enet_uint32 outgoingDataTotal; - enet_uint32 lastSendTime; - enet_uint32 lastReceiveTime; - enet_uint32 nextTimeout; - enet_uint32 earliestTimeout; - enet_uint32 packetLossEpoch; - enet_uint32 packetsSent; - enet_uint32 packetsLost; - enet_uint32 packetLoss; /**< mean packet loss of reliable packets as a ratio with respect to the constant ENET_PEER_PACKET_LOSS_SCALE */ - enet_uint32 packetLossVariance; - enet_uint32 packetThrottle; - enet_uint32 packetThrottleLimit; - enet_uint32 packetThrottleCounter; - enet_uint32 packetThrottleEpoch; - enet_uint32 packetThrottleAcceleration; - enet_uint32 packetThrottleDeceleration; - enet_uint32 packetThrottleInterval; - enet_uint32 pingInterval; - enet_uint32 timeoutLimit; - enet_uint32 timeoutMinimum; - enet_uint32 timeoutMaximum; - enet_uint32 lastRoundTripTime; - enet_uint32 lowestRoundTripTime; - enet_uint32 lastRoundTripTimeVariance; - enet_uint32 highestRoundTripTimeVariance; - enet_uint32 roundTripTime; /**< mean round trip time (RTT), in milliseconds, between sending a reliable packet and receiving its acknowledgement */ - enet_uint32 roundTripTimeVariance; - enet_uint32 mtu; - enet_uint32 windowSize; - enet_uint32 reliableDataInTransit; - enet_uint16 outgoingReliableSequenceNumber; - ENetList acknowledgements; - ENetList sentReliableCommands; - ENetList sentUnreliableCommands; - ENetList outgoingCommands; - ENetList dispatchedCommands; - enet_uint16 flags; - enet_uint16 reserved; - enet_uint16 incomingUnsequencedGroup; - enet_uint16 outgoingUnsequencedGroup; - enet_uint32 unsequencedWindow [ENET_PEER_UNSEQUENCED_WINDOW_SIZE / 32]; - enet_uint32 eventData; - size_t totalWaitingData; -} ENetPeer; - -/** An ENet packet compressor for compressing UDP packets before socket sends or receives. - */ -typedef struct _ENetCompressor -{ - /** Context data for the compressor. Must be non-NULL. */ - void * context; - /** Compresses from inBuffers[0:inBufferCount-1], containing inLimit bytes, to outData, outputting at most outLimit bytes. Should return 0 on failure. */ - size_t (ENET_CALLBACK * compress) (void * context, const ENetBuffer * inBuffers, size_t inBufferCount, size_t inLimit, enet_uint8 * outData, size_t outLimit); - /** Decompresses from inData, containing inLimit bytes, to outData, outputting at most outLimit bytes. Should return 0 on failure. */ - size_t (ENET_CALLBACK * decompress) (void * context, const enet_uint8 * inData, size_t inLimit, enet_uint8 * outData, size_t outLimit); - /** Destroys the context when compression is disabled or the host is destroyed. May be NULL. */ - void (ENET_CALLBACK * destroy) (void * context); -} ENetCompressor; - -/** Callback that computes the checksum of the data held in buffers[0:bufferCount-1] */ -typedef enet_uint32 (ENET_CALLBACK * ENetChecksumCallback) (const ENetBuffer * buffers, size_t bufferCount); - -/** Callback for intercepting received raw UDP packets. Should return 1 to intercept, 0 to ignore, or -1 to propagate an error. */ -typedef int (ENET_CALLBACK * ENetInterceptCallback) (struct _ENetHost * host, struct _ENetEvent * event); - -/** An ENet host for communicating with peers. - * - * No fields should be modified unless otherwise stated. - - @sa enet_host_create() - @sa enet_host_destroy() - @sa enet_host_connect() - @sa enet_host_service() - @sa enet_host_flush() - @sa enet_host_broadcast() - @sa enet_host_compress() - @sa enet_host_compress_with_range_coder() - @sa enet_host_channel_limit() - @sa enet_host_bandwidth_limit() - @sa enet_host_bandwidth_throttle() - */ -typedef struct _ENetHost -{ - ENetSocket socket; - ENetAddress address; /**< Internet address of the host */ - enet_uint32 incomingBandwidth; /**< downstream bandwidth of the host */ - enet_uint32 outgoingBandwidth; /**< upstream bandwidth of the host */ - enet_uint32 bandwidthThrottleEpoch; - enet_uint32 mtu; - enet_uint32 randomSeed; - int recalculateBandwidthLimits; - ENetPeer * peers; /**< array of peers allocated for this host */ - size_t peerCount; /**< number of peers allocated for this host */ - size_t channelLimit; /**< maximum number of channels allowed for connected peers */ - enet_uint32 serviceTime; - ENetList dispatchQueue; - int continueSending; - size_t packetSize; - enet_uint16 headerFlags; - ENetProtocol commands [ENET_PROTOCOL_MAXIMUM_PACKET_COMMANDS]; - size_t commandCount; - ENetBuffer buffers [ENET_BUFFER_MAXIMUM]; - size_t bufferCount; - ENetChecksumCallback checksum; /**< callback the user can set to enable packet checksums for this host */ - ENetCompressor compressor; - enet_uint8 packetData [2][ENET_PROTOCOL_MAXIMUM_MTU]; - ENetAddress receivedAddress; - enet_uint8 * receivedData; - size_t receivedDataLength; - enet_uint32 totalSentData; /**< total data sent, user should reset to 0 as needed to prevent overflow */ - enet_uint32 totalSentPackets; /**< total UDP packets sent, user should reset to 0 as needed to prevent overflow */ - enet_uint32 totalReceivedData; /**< total data received, user should reset to 0 as needed to prevent overflow */ - enet_uint32 totalReceivedPackets; /**< total UDP packets received, user should reset to 0 as needed to prevent overflow */ - ENetInterceptCallback intercept; /**< callback the user can set to intercept received raw UDP packets */ - size_t connectedPeers; - size_t bandwidthLimitedPeers; - size_t duplicatePeers; /**< optional number of allowed peers from duplicate IPs, defaults to ENET_PROTOCOL_MAXIMUM_PEER_ID */ - size_t maximumPacketSize; /**< the maximum allowable packet size that may be sent or received on a peer */ - size_t maximumWaitingData; /**< the maximum aggregate amount of buffer space a peer may use waiting for packets to be delivered */ -} ENetHost; - -/** - * An ENet event type, as specified in @ref ENetEvent. - */ -typedef enum _ENetEventType -{ - /** no event occurred within the specified time limit */ - ENET_EVENT_TYPE_NONE = 0, - - /** a connection request initiated by enet_host_connect has completed. - * The peer field contains the peer which successfully connected. - */ - ENET_EVENT_TYPE_CONNECT = 1, - - /** a peer has disconnected. This event is generated on a successful - * completion of a disconnect initiated by enet_peer_disconnect, if - * a peer has timed out, or if a connection request intialized by - * enet_host_connect has timed out. The peer field contains the peer - * which disconnected. The data field contains user supplied data - * describing the disconnection, or 0, if none is available. - */ - ENET_EVENT_TYPE_DISCONNECT = 2, - - /** a packet has been received from a peer. The peer field specifies the - * peer which sent the packet. The channelID field specifies the channel - * number upon which the packet was received. The packet field contains - * the packet that was received; this packet must be destroyed with - * enet_packet_destroy after use. - */ - ENET_EVENT_TYPE_RECEIVE = 3 -} ENetEventType; - -/** - * An ENet event as returned by enet_host_service(). - - @sa enet_host_service - */ -typedef struct _ENetEvent -{ - ENetEventType type; /**< type of the event */ - ENetPeer * peer; /**< peer that generated a connect, disconnect or receive event */ - enet_uint8 channelID; /**< channel on the peer that generated the event, if appropriate */ - enet_uint32 data; /**< data associated with the event, if appropriate */ - ENetPacket * packet; /**< packet associated with the event, if appropriate */ -} ENetEvent; - -/** @defgroup global ENet global functions - @{ -*/ - -/** - Initializes ENet globally. Must be called prior to using any functions in - ENet. - @returns 0 on success, < 0 on failure -*/ -ENET_API int enet_initialize (void); - -/** - Initializes ENet globally and supplies user-overridden callbacks. Must be called prior to using any functions in ENet. Do not use enet_initialize() if you use this variant. Make sure the ENetCallbacks structure is zeroed out so that any additional callbacks added in future versions will be properly ignored. - - @param version the constant ENET_VERSION should be supplied so ENet knows which version of ENetCallbacks struct to use - @param inits user-overridden callbacks where any NULL callbacks will use ENet's defaults - @returns 0 on success, < 0 on failure -*/ -ENET_API int enet_initialize_with_callbacks (ENetVersion version, const ENetCallbacks * inits); - -/** - Shuts down ENet globally. Should be called when a program that has - initialized ENet exits. -*/ -ENET_API void enet_deinitialize (void); - -/** - Gives the linked version of the ENet library. - @returns the version number -*/ -ENET_API ENetVersion enet_linked_version (void); - -/** @} */ - -/** @defgroup private ENet private implementation functions */ - -/** - Returns the wall-time in milliseconds. Its initial value is unspecified - unless otherwise set. - */ -ENET_API enet_uint32 enet_time_get (void); -/** - Sets the current wall-time in milliseconds. - */ -ENET_API void enet_time_set (enet_uint32); - -/** @defgroup socket ENet socket functions - @{ -*/ -ENET_API ENetSocket enet_socket_create (ENetSocketType); -ENET_API int enet_socket_bind (ENetSocket, const ENetAddress *); -ENET_API int enet_socket_get_address (ENetSocket, ENetAddress *); -ENET_API int enet_socket_listen (ENetSocket, int); -ENET_API ENetSocket enet_socket_accept (ENetSocket, ENetAddress *); -ENET_API int enet_socket_connect (ENetSocket, const ENetAddress *); -ENET_API int enet_socket_send (ENetSocket, const ENetAddress *, const ENetBuffer *, size_t); -ENET_API int enet_socket_receive (ENetSocket, ENetAddress *, ENetBuffer *, size_t); -ENET_API int enet_socket_wait (ENetSocket, enet_uint32 *, enet_uint32); -ENET_API int enet_socket_set_option (ENetSocket, ENetSocketOption, int); -ENET_API int enet_socket_get_option (ENetSocket, ENetSocketOption, int *); -ENET_API int enet_socket_shutdown (ENetSocket, ENetSocketShutdown); -ENET_API void enet_socket_destroy (ENetSocket); -ENET_API int enet_socketset_select (ENetSocket, ENetSocketSet *, ENetSocketSet *, enet_uint32); - -/** @} */ - -/** @defgroup Address ENet address functions - @{ -*/ - -/** Attempts to parse the printable form of the IP address in the parameter hostName - and sets the host field in the address parameter if successful. - @param address destination to store the parsed IP address - @param hostName IP address to parse - @retval 0 on success - @retval < 0 on failure - @returns the address of the given hostName in address on success -*/ -ENET_API int enet_address_set_host_ip (ENetAddress * address, const char * hostName); - -/** Attempts to resolve the host named by the parameter hostName and sets - the host field in the address parameter if successful. - @param address destination to store resolved address - @param hostName host name to lookup - @retval 0 on success - @retval < 0 on failure - @returns the address of the given hostName in address on success -*/ -ENET_API int enet_address_set_host (ENetAddress * address, const char * hostName); - -/** Gives the printable form of the IP address specified in the address parameter. - @param address address printed - @param hostName destination for name, must not be NULL - @param nameLength maximum length of hostName. - @returns the null-terminated name of the host in hostName on success - @retval 0 on success - @retval < 0 on failure -*/ -ENET_API int enet_address_get_host_ip (const ENetAddress * address, char * hostName, size_t nameLength); - -/** Attempts to do a reverse lookup of the host field in the address parameter. - @param address address used for reverse lookup - @param hostName destination for name, must not be NULL - @param nameLength maximum length of hostName. - @returns the null-terminated name of the host in hostName on success - @retval 0 on success - @retval < 0 on failure -*/ -ENET_API int enet_address_get_host (const ENetAddress * address, char * hostName, size_t nameLength); - -/** @} */ - -ENET_API ENetPacket * enet_packet_create (const void *, size_t, enet_uint32); -ENET_API void enet_packet_destroy (ENetPacket *); -ENET_API int enet_packet_resize (ENetPacket *, size_t); -ENET_API enet_uint32 enet_crc32 (const ENetBuffer *, size_t); - -ENET_API ENetHost * enet_host_create (const ENetAddress *, size_t, size_t, enet_uint32, enet_uint32); -ENET_API void enet_host_destroy (ENetHost *); -ENET_API ENetPeer * enet_host_connect (ENetHost *, const ENetAddress *, size_t, enet_uint32); -ENET_API int enet_host_check_events (ENetHost *, ENetEvent *); -ENET_API int enet_host_service (ENetHost *, ENetEvent *, enet_uint32); -ENET_API void enet_host_flush (ENetHost *); -ENET_API void enet_host_broadcast (ENetHost *, enet_uint8, ENetPacket *); -ENET_API void enet_host_compress (ENetHost *, const ENetCompressor *); -ENET_API int enet_host_compress_with_range_coder (ENetHost * host); -ENET_API void enet_host_channel_limit (ENetHost *, size_t); -ENET_API void enet_host_bandwidth_limit (ENetHost *, enet_uint32, enet_uint32); -extern void enet_host_bandwidth_throttle (ENetHost *); -extern enet_uint32 enet_host_random_seed (void); - -ENET_API int enet_peer_send (ENetPeer *, enet_uint8, ENetPacket *); -ENET_API ENetPacket * enet_peer_receive (ENetPeer *, enet_uint8 * channelID); -ENET_API void enet_peer_ping (ENetPeer *); -ENET_API void enet_peer_ping_interval (ENetPeer *, enet_uint32); -ENET_API void enet_peer_timeout (ENetPeer *, enet_uint32, enet_uint32, enet_uint32); -ENET_API void enet_peer_reset (ENetPeer *); -ENET_API void enet_peer_disconnect (ENetPeer *, enet_uint32); -ENET_API void enet_peer_disconnect_now (ENetPeer *, enet_uint32); -ENET_API void enet_peer_disconnect_later (ENetPeer *, enet_uint32); -ENET_API void enet_peer_throttle_configure (ENetPeer *, enet_uint32, enet_uint32, enet_uint32); -extern int enet_peer_throttle (ENetPeer *, enet_uint32); -extern void enet_peer_reset_queues (ENetPeer *); -extern void enet_peer_setup_outgoing_command (ENetPeer *, ENetOutgoingCommand *); -extern ENetOutgoingCommand * enet_peer_queue_outgoing_command (ENetPeer *, const ENetProtocol *, ENetPacket *, enet_uint32, enet_uint16); -extern ENetIncomingCommand * enet_peer_queue_incoming_command (ENetPeer *, const ENetProtocol *, const void *, size_t, enet_uint32, enet_uint32); -extern ENetAcknowledgement * enet_peer_queue_acknowledgement (ENetPeer *, const ENetProtocol *, enet_uint16); -extern void enet_peer_dispatch_incoming_unreliable_commands (ENetPeer *, ENetChannel *, ENetIncomingCommand *); -extern void enet_peer_dispatch_incoming_reliable_commands (ENetPeer *, ENetChannel *, ENetIncomingCommand *); -extern void enet_peer_on_connect (ENetPeer *); -extern void enet_peer_on_disconnect (ENetPeer *); - -ENET_API void * enet_range_coder_create (void); -ENET_API void enet_range_coder_destroy (void *); -ENET_API size_t enet_range_coder_compress (void *, const ENetBuffer *, size_t, size_t, enet_uint8 *, size_t); -ENET_API size_t enet_range_coder_decompress (void *, const enet_uint8 *, size_t, enet_uint8 *, size_t); - -extern size_t enet_protocol_command_size (enet_uint8); - -#ifdef __cplusplus -} -#endif - -#endif /* __ENET_ENET_H__ */ - diff --git a/include/enet/include/enet/list.h b/include/enet/include/enet/list.h deleted file mode 100644 index d7b2600..0000000 --- a/include/enet/include/enet/list.h +++ /dev/null @@ -1,43 +0,0 @@ -/** - @file list.h - @brief ENet list management -*/ -#ifndef __ENET_LIST_H__ -#define __ENET_LIST_H__ - -#include - -typedef struct _ENetListNode -{ - struct _ENetListNode * next; - struct _ENetListNode * previous; -} ENetListNode; - -typedef ENetListNode * ENetListIterator; - -typedef struct _ENetList -{ - ENetListNode sentinel; -} ENetList; - -extern void enet_list_clear (ENetList *); - -extern ENetListIterator enet_list_insert (ENetListIterator, void *); -extern void * enet_list_remove (ENetListIterator); -extern ENetListIterator enet_list_move (ENetListIterator, void *, void *); - -extern size_t enet_list_size (ENetList *); - -#define enet_list_begin(list) ((list) -> sentinel.next) -#define enet_list_end(list) (& (list) -> sentinel) - -#define enet_list_empty(list) (enet_list_begin (list) == enet_list_end (list)) - -#define enet_list_next(iterator) ((iterator) -> next) -#define enet_list_previous(iterator) ((iterator) -> previous) - -#define enet_list_front(list) ((void *) (list) -> sentinel.next) -#define enet_list_back(list) ((void *) (list) -> sentinel.previous) - -#endif /* __ENET_LIST_H__ */ - diff --git a/include/enet/include/enet/protocol.h b/include/enet/include/enet/protocol.h deleted file mode 100644 index f8c73d8..0000000 --- a/include/enet/include/enet/protocol.h +++ /dev/null @@ -1,198 +0,0 @@ -/** - @file protocol.h - @brief ENet protocol -*/ -#ifndef __ENET_PROTOCOL_H__ -#define __ENET_PROTOCOL_H__ - -#include "enet/types.h" - -enum -{ - ENET_PROTOCOL_MINIMUM_MTU = 576, - ENET_PROTOCOL_MAXIMUM_MTU = 4096, - ENET_PROTOCOL_MAXIMUM_PACKET_COMMANDS = 32, - ENET_PROTOCOL_MINIMUM_WINDOW_SIZE = 4096, - ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE = 65536, - ENET_PROTOCOL_MINIMUM_CHANNEL_COUNT = 1, - ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT = 255, - ENET_PROTOCOL_MAXIMUM_PEER_ID = 0xFFF, - ENET_PROTOCOL_MAXIMUM_FRAGMENT_COUNT = 1024 * 1024 -}; - -typedef enum _ENetProtocolCommand -{ - ENET_PROTOCOL_COMMAND_NONE = 0, - ENET_PROTOCOL_COMMAND_ACKNOWLEDGE = 1, - ENET_PROTOCOL_COMMAND_CONNECT = 2, - ENET_PROTOCOL_COMMAND_VERIFY_CONNECT = 3, - ENET_PROTOCOL_COMMAND_DISCONNECT = 4, - ENET_PROTOCOL_COMMAND_PING = 5, - ENET_PROTOCOL_COMMAND_SEND_RELIABLE = 6, - ENET_PROTOCOL_COMMAND_SEND_UNRELIABLE = 7, - ENET_PROTOCOL_COMMAND_SEND_FRAGMENT = 8, - ENET_PROTOCOL_COMMAND_SEND_UNSEQUENCED = 9, - ENET_PROTOCOL_COMMAND_BANDWIDTH_LIMIT = 10, - ENET_PROTOCOL_COMMAND_THROTTLE_CONFIGURE = 11, - ENET_PROTOCOL_COMMAND_SEND_UNRELIABLE_FRAGMENT = 12, - ENET_PROTOCOL_COMMAND_COUNT = 13, - - ENET_PROTOCOL_COMMAND_MASK = 0x0F -} ENetProtocolCommand; - -typedef enum _ENetProtocolFlag -{ - ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE = (1 << 7), - ENET_PROTOCOL_COMMAND_FLAG_UNSEQUENCED = (1 << 6), - - ENET_PROTOCOL_HEADER_FLAG_COMPRESSED = (1 << 14), - ENET_PROTOCOL_HEADER_FLAG_SENT_TIME = (1 << 15), - ENET_PROTOCOL_HEADER_FLAG_MASK = ENET_PROTOCOL_HEADER_FLAG_COMPRESSED | ENET_PROTOCOL_HEADER_FLAG_SENT_TIME, - - ENET_PROTOCOL_HEADER_SESSION_MASK = (3 << 12), - ENET_PROTOCOL_HEADER_SESSION_SHIFT = 12 -} ENetProtocolFlag; - -#ifdef _MSC_VER -#pragma pack(push, 1) -#define ENET_PACKED -#elif defined(__GNUC__) || defined(__clang__) -#define ENET_PACKED __attribute__ ((packed)) -#else -#define ENET_PACKED -#endif - -typedef struct _ENetProtocolHeader -{ - enet_uint16 peerID; - enet_uint16 sentTime; -} ENET_PACKED ENetProtocolHeader; - -typedef struct _ENetProtocolCommandHeader -{ - enet_uint8 command; - enet_uint8 channelID; - enet_uint16 reliableSequenceNumber; -} ENET_PACKED ENetProtocolCommandHeader; - -typedef struct _ENetProtocolAcknowledge -{ - ENetProtocolCommandHeader header; - enet_uint16 receivedReliableSequenceNumber; - enet_uint16 receivedSentTime; -} ENET_PACKED ENetProtocolAcknowledge; - -typedef struct _ENetProtocolConnect -{ - ENetProtocolCommandHeader header; - enet_uint16 outgoingPeerID; - enet_uint8 incomingSessionID; - enet_uint8 outgoingSessionID; - enet_uint32 mtu; - enet_uint32 windowSize; - enet_uint32 channelCount; - enet_uint32 incomingBandwidth; - enet_uint32 outgoingBandwidth; - enet_uint32 packetThrottleInterval; - enet_uint32 packetThrottleAcceleration; - enet_uint32 packetThrottleDeceleration; - enet_uint32 connectID; - enet_uint32 data; -} ENET_PACKED ENetProtocolConnect; - -typedef struct _ENetProtocolVerifyConnect -{ - ENetProtocolCommandHeader header; - enet_uint16 outgoingPeerID; - enet_uint8 incomingSessionID; - enet_uint8 outgoingSessionID; - enet_uint32 mtu; - enet_uint32 windowSize; - enet_uint32 channelCount; - enet_uint32 incomingBandwidth; - enet_uint32 outgoingBandwidth; - enet_uint32 packetThrottleInterval; - enet_uint32 packetThrottleAcceleration; - enet_uint32 packetThrottleDeceleration; - enet_uint32 connectID; -} ENET_PACKED ENetProtocolVerifyConnect; - -typedef struct _ENetProtocolBandwidthLimit -{ - ENetProtocolCommandHeader header; - enet_uint32 incomingBandwidth; - enet_uint32 outgoingBandwidth; -} ENET_PACKED ENetProtocolBandwidthLimit; - -typedef struct _ENetProtocolThrottleConfigure -{ - ENetProtocolCommandHeader header; - enet_uint32 packetThrottleInterval; - enet_uint32 packetThrottleAcceleration; - enet_uint32 packetThrottleDeceleration; -} ENET_PACKED ENetProtocolThrottleConfigure; - -typedef struct _ENetProtocolDisconnect -{ - ENetProtocolCommandHeader header; - enet_uint32 data; -} ENET_PACKED ENetProtocolDisconnect; - -typedef struct _ENetProtocolPing -{ - ENetProtocolCommandHeader header; -} ENET_PACKED ENetProtocolPing; - -typedef struct _ENetProtocolSendReliable -{ - ENetProtocolCommandHeader header; - enet_uint16 dataLength; -} ENET_PACKED ENetProtocolSendReliable; - -typedef struct _ENetProtocolSendUnreliable -{ - ENetProtocolCommandHeader header; - enet_uint16 unreliableSequenceNumber; - enet_uint16 dataLength; -} ENET_PACKED ENetProtocolSendUnreliable; - -typedef struct _ENetProtocolSendUnsequenced -{ - ENetProtocolCommandHeader header; - enet_uint16 unsequencedGroup; - enet_uint16 dataLength; -} ENET_PACKED ENetProtocolSendUnsequenced; - -typedef struct _ENetProtocolSendFragment -{ - ENetProtocolCommandHeader header; - enet_uint16 startSequenceNumber; - enet_uint16 dataLength; - enet_uint32 fragmentCount; - enet_uint32 fragmentNumber; - enet_uint32 totalLength; - enet_uint32 fragmentOffset; -} ENET_PACKED ENetProtocolSendFragment; - -typedef union _ENetProtocol -{ - ENetProtocolCommandHeader header; - ENetProtocolAcknowledge acknowledge; - ENetProtocolConnect connect; - ENetProtocolVerifyConnect verifyConnect; - ENetProtocolDisconnect disconnect; - ENetProtocolPing ping; - ENetProtocolSendReliable sendReliable; - ENetProtocolSendUnreliable sendUnreliable; - ENetProtocolSendUnsequenced sendUnsequenced; - ENetProtocolSendFragment sendFragment; - ENetProtocolBandwidthLimit bandwidthLimit; - ENetProtocolThrottleConfigure throttleConfigure; -} ENET_PACKED ENetProtocol; - -#ifdef _MSC_VER -#pragma pack(pop) -#endif - -#endif /* __ENET_PROTOCOL_H__ */ - diff --git a/include/enet/include/enet/time.h b/include/enet/include/enet/time.h deleted file mode 100644 index c82a546..0000000 --- a/include/enet/include/enet/time.h +++ /dev/null @@ -1,18 +0,0 @@ -/** - @file time.h - @brief ENet time constants and macros -*/ -#ifndef __ENET_TIME_H__ -#define __ENET_TIME_H__ - -#define ENET_TIME_OVERFLOW 86400000 - -#define ENET_TIME_LESS(a, b) ((a) - (b) >= ENET_TIME_OVERFLOW) -#define ENET_TIME_GREATER(a, b) ((b) - (a) >= ENET_TIME_OVERFLOW) -#define ENET_TIME_LESS_EQUAL(a, b) (! ENET_TIME_GREATER (a, b)) -#define ENET_TIME_GREATER_EQUAL(a, b) (! ENET_TIME_LESS (a, b)) - -#define ENET_TIME_DIFFERENCE(a, b) ((a) - (b) >= ENET_TIME_OVERFLOW ? (b) - (a) : (a) - (b)) - -#endif /* __ENET_TIME_H__ */ - diff --git a/include/enet/include/enet/types.h b/include/enet/include/enet/types.h deleted file mode 100644 index ab010a4..0000000 --- a/include/enet/include/enet/types.h +++ /dev/null @@ -1,13 +0,0 @@ -/** - @file types.h - @brief type definitions for ENet -*/ -#ifndef __ENET_TYPES_H__ -#define __ENET_TYPES_H__ - -typedef unsigned char enet_uint8; /**< unsigned 8-bit type */ -typedef unsigned short enet_uint16; /**< unsigned 16-bit type */ -typedef unsigned int enet_uint32; /**< unsigned 32-bit type */ - -#endif /* __ENET_TYPES_H__ */ - diff --git a/include/enet/include/enet/unix.h b/include/enet/include/enet/unix.h deleted file mode 100644 index b55be33..0000000 --- a/include/enet/include/enet/unix.h +++ /dev/null @@ -1,48 +0,0 @@ -/** - @file unix.h - @brief ENet Unix header -*/ -#ifndef __ENET_UNIX_H__ -#define __ENET_UNIX_H__ - -#include -#include -#include -#include -#include -#include -#include - -#ifdef MSG_MAXIOVLEN -#define ENET_BUFFER_MAXIMUM MSG_MAXIOVLEN -#endif - -typedef int ENetSocket; - -#define ENET_SOCKET_NULL -1 - -#define ENET_HOST_TO_NET_16(value) (htons (value)) /**< macro that converts host to net byte-order of a 16-bit value */ -#define ENET_HOST_TO_NET_32(value) (htonl (value)) /**< macro that converts host to net byte-order of a 32-bit value */ - -#define ENET_NET_TO_HOST_16(value) (ntohs (value)) /**< macro that converts net to host byte-order of a 16-bit value */ -#define ENET_NET_TO_HOST_32(value) (ntohl (value)) /**< macro that converts net to host byte-order of a 32-bit value */ - -typedef struct -{ - void * data; - size_t dataLength; -} ENetBuffer; - -#define ENET_CALLBACK - -#define ENET_API extern - -typedef fd_set ENetSocketSet; - -#define ENET_SOCKETSET_EMPTY(sockset) FD_ZERO (& (sockset)) -#define ENET_SOCKETSET_ADD(sockset, socket) FD_SET (socket, & (sockset)) -#define ENET_SOCKETSET_REMOVE(sockset, socket) FD_CLR (socket, & (sockset)) -#define ENET_SOCKETSET_CHECK(sockset, socket) FD_ISSET (socket, & (sockset)) - -#endif /* __ENET_UNIX_H__ */ - diff --git a/include/enet/include/enet/utility.h b/include/enet/include/enet/utility.h deleted file mode 100644 index b04bb7a..0000000 --- a/include/enet/include/enet/utility.h +++ /dev/null @@ -1,13 +0,0 @@ -/** - @file utility.h - @brief ENet utility header -*/ -#ifndef __ENET_UTILITY_H__ -#define __ENET_UTILITY_H__ - -#define ENET_MAX(x, y) ((x) > (y) ? (x) : (y)) -#define ENET_MIN(x, y) ((x) < (y) ? (x) : (y)) -#define ENET_DIFFERENCE(x, y) ((x) < (y) ? (y) - (x) : (x) - (y)) - -#endif /* __ENET_UTILITY_H__ */ - diff --git a/include/enet/include/enet/win32.h b/include/enet/include/enet/win32.h deleted file mode 100644 index e73ca9d..0000000 --- a/include/enet/include/enet/win32.h +++ /dev/null @@ -1,57 +0,0 @@ -/** - @file win32.h - @brief ENet Win32 header -*/ -#ifndef __ENET_WIN32_H__ -#define __ENET_WIN32_H__ - -#ifdef _MSC_VER -#ifdef ENET_BUILDING_LIB -#pragma warning (disable: 4267) // size_t to int conversion -#pragma warning (disable: 4244) // 64bit to 32bit int -#pragma warning (disable: 4018) // signed/unsigned mismatch -#pragma warning (disable: 4146) // unary minus operator applied to unsigned type -#endif -#endif - -#include -#include - -typedef SOCKET ENetSocket; - -#define ENET_SOCKET_NULL INVALID_SOCKET - -#define ENET_HOST_TO_NET_16(value) (htons (value)) -#define ENET_HOST_TO_NET_32(value) (htonl (value)) - -#define ENET_NET_TO_HOST_16(value) (ntohs (value)) -#define ENET_NET_TO_HOST_32(value) (ntohl (value)) - -typedef struct -{ - size_t dataLength; - void * data; -} ENetBuffer; - -#define ENET_CALLBACK __cdecl - -#ifdef ENET_DLL -#ifdef ENET_BUILDING_LIB -#define ENET_API __declspec( dllexport ) -#else -#define ENET_API __declspec( dllimport ) -#endif /* ENET_BUILDING_LIB */ -#else /* !ENET_DLL */ -#define ENET_API extern -#endif /* ENET_DLL */ - -typedef fd_set ENetSocketSet; - -#define ENET_SOCKETSET_EMPTY(sockset) FD_ZERO (& (sockset)) -#define ENET_SOCKETSET_ADD(sockset, socket) FD_SET (socket, & (sockset)) -#define ENET_SOCKETSET_REMOVE(sockset, socket) FD_CLR (socket, & (sockset)) -#define ENET_SOCKETSET_CHECK(sockset, socket) FD_ISSET (socket, & (sockset)) - -#endif /* __ENET_WIN32_H__ */ - - diff --git a/include/enet/libenet.pc.in b/include/enet/libenet.pc.in deleted file mode 100644 index 7af85ad..0000000 --- a/include/enet/libenet.pc.in +++ /dev/null @@ -1,10 +0,0 @@ -prefix=@prefix@ -exec_prefix=@exec_prefix@ -libdir=@libdir@ -includedir=@includedir@ - -Name: @PACKAGE_NAME@ -Description: Low-latency UDP networking library supporting optional reliability -Version: @PACKAGE_VERSION@ -Cflags: -I${includedir} -Libs: -L${libdir} -lenet diff --git a/include/enet/list.c b/include/enet/list.c deleted file mode 100644 index 1c1a8df..0000000 --- a/include/enet/list.c +++ /dev/null @@ -1,75 +0,0 @@ -/** - @file list.c - @brief ENet linked list functions -*/ -#define ENET_BUILDING_LIB 1 -#include "enet/enet.h" - -/** - @defgroup list ENet linked list utility functions - @ingroup private - @{ -*/ -void -enet_list_clear (ENetList * list) -{ - list -> sentinel.next = & list -> sentinel; - list -> sentinel.previous = & list -> sentinel; -} - -ENetListIterator -enet_list_insert (ENetListIterator position, void * data) -{ - ENetListIterator result = (ENetListIterator) data; - - result -> previous = position -> previous; - result -> next = position; - - result -> previous -> next = result; - position -> previous = result; - - return result; -} - -void * -enet_list_remove (ENetListIterator position) -{ - position -> previous -> next = position -> next; - position -> next -> previous = position -> previous; - - return position; -} - -ENetListIterator -enet_list_move (ENetListIterator position, void * dataFirst, void * dataLast) -{ - ENetListIterator first = (ENetListIterator) dataFirst, - last = (ENetListIterator) dataLast; - - first -> previous -> next = last -> next; - last -> next -> previous = first -> previous; - - first -> previous = position -> previous; - last -> next = position; - - first -> previous -> next = first; - position -> previous = last; - - return first; -} - -size_t -enet_list_size (ENetList * list) -{ - size_t size = 0; - ENetListIterator position; - - for (position = enet_list_begin (list); - position != enet_list_end (list); - position = enet_list_next (position)) - ++ size; - - return size; -} - -/** @} */ diff --git a/include/enet/packet.c b/include/enet/packet.c deleted file mode 100644 index 5fa78b2..0000000 --- a/include/enet/packet.c +++ /dev/null @@ -1,165 +0,0 @@ -/** - @file packet.c - @brief ENet packet management functions -*/ -#include -#define ENET_BUILDING_LIB 1 -#include "enet/enet.h" - -/** @defgroup Packet ENet packet functions - @{ -*/ - -/** Creates a packet that may be sent to a peer. - @param data initial contents of the packet's data; the packet's data will remain uninitialized if data is NULL. - @param dataLength size of the data allocated for this packet - @param flags flags for this packet as described for the ENetPacket structure. - @returns the packet on success, NULL on failure -*/ -ENetPacket * -enet_packet_create (const void * data, size_t dataLength, enet_uint32 flags) -{ - ENetPacket * packet = (ENetPacket *) enet_malloc (sizeof (ENetPacket)); - if (packet == NULL) - return NULL; - - if (flags & ENET_PACKET_FLAG_NO_ALLOCATE) - packet -> data = (enet_uint8 *) data; - else - if (dataLength <= 0) - packet -> data = NULL; - else - { - packet -> data = (enet_uint8 *) enet_malloc (dataLength); - if (packet -> data == NULL) - { - enet_free (packet); - return NULL; - } - - if (data != NULL) - memcpy (packet -> data, data, dataLength); - } - - packet -> referenceCount = 0; - packet -> flags = flags; - packet -> dataLength = dataLength; - packet -> freeCallback = NULL; - packet -> userData = NULL; - - return packet; -} - -/** Destroys the packet and deallocates its data. - @param packet packet to be destroyed -*/ -void -enet_packet_destroy (ENetPacket * packet) -{ - if (packet == NULL) - return; - - if (packet -> freeCallback != NULL) - (* packet -> freeCallback) (packet); - if (! (packet -> flags & ENET_PACKET_FLAG_NO_ALLOCATE) && - packet -> data != NULL) - enet_free (packet -> data); - enet_free (packet); -} - -/** Attempts to resize the data in the packet to length specified in the - dataLength parameter - @param packet packet to resize - @param dataLength new size for the packet data - @returns 0 on success, < 0 on failure -*/ -int -enet_packet_resize (ENetPacket * packet, size_t dataLength) -{ - enet_uint8 * newData; - - if (dataLength <= packet -> dataLength || (packet -> flags & ENET_PACKET_FLAG_NO_ALLOCATE)) - { - packet -> dataLength = dataLength; - - return 0; - } - - newData = (enet_uint8 *) enet_malloc (dataLength); - if (newData == NULL) - return -1; - - memcpy (newData, packet -> data, packet -> dataLength); - enet_free (packet -> data); - - packet -> data = newData; - packet -> dataLength = dataLength; - - return 0; -} - -static int initializedCRC32 = 0; -static enet_uint32 crcTable [256]; - -static enet_uint32 -reflect_crc (int val, int bits) -{ - int result = 0, bit; - - for (bit = 0; bit < bits; bit ++) - { - if(val & 1) result |= 1 << (bits - 1 - bit); - val >>= 1; - } - - return result; -} - -static void -initialize_crc32 (void) -{ - int byte; - - for (byte = 0; byte < 256; ++ byte) - { - enet_uint32 crc = reflect_crc (byte, 8) << 24; - int offset; - - for(offset = 0; offset < 8; ++ offset) - { - if (crc & 0x80000000) - crc = (crc << 1) ^ 0x04c11db7; - else - crc <<= 1; - } - - crcTable [byte] = reflect_crc (crc, 32); - } - - initializedCRC32 = 1; -} - -enet_uint32 -enet_crc32 (const ENetBuffer * buffers, size_t bufferCount) -{ - enet_uint32 crc = 0xFFFFFFFF; - - if (! initializedCRC32) initialize_crc32 (); - - while (bufferCount -- > 0) - { - const enet_uint8 * data = (const enet_uint8 *) buffers -> data, - * dataEnd = & data [buffers -> dataLength]; - - while (data < dataEnd) - { - crc = (crc >> 8) ^ crcTable [(crc & 0xFF) ^ *data++]; - } - - ++ buffers; - } - - return ENET_HOST_TO_NET_32 (~ crc); -} - -/** @} */ diff --git a/include/enet/peer.c b/include/enet/peer.c deleted file mode 100644 index 9370ef4..0000000 --- a/include/enet/peer.c +++ /dev/null @@ -1,1003 +0,0 @@ -/** - @file peer.c - @brief ENet peer management functions -*/ -#include -#define ENET_BUILDING_LIB 1 -#include "enet/enet.h" - -/** @defgroup peer ENet peer functions - @{ -*/ - -/** Configures throttle parameter for a peer. - - Unreliable packets are dropped by ENet in response to the varying conditions - of the Internet connection to the peer. The throttle represents a probability - that an unreliable packet should not be dropped and thus sent by ENet to the peer. - The lowest mean round trip time from the sending of a reliable packet to the - receipt of its acknowledgement is measured over an amount of time specified by - the interval parameter in milliseconds. If a measured round trip time happens to - be significantly less than the mean round trip time measured over the interval, - then the throttle probability is increased to allow more traffic by an amount - specified in the acceleration parameter, which is a ratio to the ENET_PEER_PACKET_THROTTLE_SCALE - constant. If a measured round trip time happens to be significantly greater than - the mean round trip time measured over the interval, then the throttle probability - is decreased to limit traffic by an amount specified in the deceleration parameter, which - is a ratio to the ENET_PEER_PACKET_THROTTLE_SCALE constant. When the throttle has - a value of ENET_PEER_PACKET_THROTTLE_SCALE, no unreliable packets are dropped by - ENet, and so 100% of all unreliable packets will be sent. When the throttle has a - value of 0, all unreliable packets are dropped by ENet, and so 0% of all unreliable - packets will be sent. Intermediate values for the throttle represent intermediate - probabilities between 0% and 100% of unreliable packets being sent. The bandwidth - limits of the local and foreign hosts are taken into account to determine a - sensible limit for the throttle probability above which it should not raise even in - the best of conditions. - - @param peer peer to configure - @param interval interval, in milliseconds, over which to measure lowest mean RTT; the default value is ENET_PEER_PACKET_THROTTLE_INTERVAL. - @param acceleration rate at which to increase the throttle probability as mean RTT declines - @param deceleration rate at which to decrease the throttle probability as mean RTT increases -*/ -void -enet_peer_throttle_configure (ENetPeer * peer, enet_uint32 interval, enet_uint32 acceleration, enet_uint32 deceleration) -{ - ENetProtocol command; - - peer -> packetThrottleInterval = interval; - peer -> packetThrottleAcceleration = acceleration; - peer -> packetThrottleDeceleration = deceleration; - - command.header.command = ENET_PROTOCOL_COMMAND_THROTTLE_CONFIGURE | ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE; - command.header.channelID = 0xFF; - - command.throttleConfigure.packetThrottleInterval = ENET_HOST_TO_NET_32 (interval); - command.throttleConfigure.packetThrottleAcceleration = ENET_HOST_TO_NET_32 (acceleration); - command.throttleConfigure.packetThrottleDeceleration = ENET_HOST_TO_NET_32 (deceleration); - - enet_peer_queue_outgoing_command (peer, & command, NULL, 0, 0); -} - -int -enet_peer_throttle (ENetPeer * peer, enet_uint32 rtt) -{ - if (peer -> lastRoundTripTime <= peer -> lastRoundTripTimeVariance) - { - peer -> packetThrottle = peer -> packetThrottleLimit; - } - else - if (rtt <= peer -> lastRoundTripTime) - { - peer -> packetThrottle += peer -> packetThrottleAcceleration; - - if (peer -> packetThrottle > peer -> packetThrottleLimit) - peer -> packetThrottle = peer -> packetThrottleLimit; - - return 1; - } - else - if (rtt > peer -> lastRoundTripTime + 2 * peer -> lastRoundTripTimeVariance) - { - if (peer -> packetThrottle > peer -> packetThrottleDeceleration) - peer -> packetThrottle -= peer -> packetThrottleDeceleration; - else - peer -> packetThrottle = 0; - - return -1; - } - - return 0; -} - -/** Queues a packet to be sent. - @param peer destination for the packet - @param channelID channel on which to send - @param packet packet to send - @retval 0 on success - @retval < 0 on failure -*/ -int -enet_peer_send (ENetPeer * peer, enet_uint8 channelID, ENetPacket * packet) -{ - ENetChannel * channel = & peer -> channels [channelID]; - ENetProtocol command; - size_t fragmentLength; - - if (peer -> state != ENET_PEER_STATE_CONNECTED || - channelID >= peer -> channelCount || - packet -> dataLength > peer -> host -> maximumPacketSize) - return -1; - - fragmentLength = peer -> mtu - sizeof (ENetProtocolHeader) - sizeof (ENetProtocolSendFragment); - if (peer -> host -> checksum != NULL) - fragmentLength -= sizeof(enet_uint32); - - if (packet -> dataLength > fragmentLength) - { - enet_uint32 fragmentCount = (packet -> dataLength + fragmentLength - 1) / fragmentLength, - fragmentNumber, - fragmentOffset; - enet_uint8 commandNumber; - enet_uint16 startSequenceNumber; - ENetList fragments; - ENetOutgoingCommand * fragment; - - if (fragmentCount > ENET_PROTOCOL_MAXIMUM_FRAGMENT_COUNT) - return -1; - - if ((packet -> flags & (ENET_PACKET_FLAG_RELIABLE | ENET_PACKET_FLAG_UNRELIABLE_FRAGMENT)) == ENET_PACKET_FLAG_UNRELIABLE_FRAGMENT && - channel -> outgoingUnreliableSequenceNumber < 0xFFFF) - { - commandNumber = ENET_PROTOCOL_COMMAND_SEND_UNRELIABLE_FRAGMENT; - startSequenceNumber = ENET_HOST_TO_NET_16 (channel -> outgoingUnreliableSequenceNumber + 1); - } - else - { - commandNumber = ENET_PROTOCOL_COMMAND_SEND_FRAGMENT | ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE; - startSequenceNumber = ENET_HOST_TO_NET_16 (channel -> outgoingReliableSequenceNumber + 1); - } - - enet_list_clear (& fragments); - - for (fragmentNumber = 0, - fragmentOffset = 0; - fragmentOffset < packet -> dataLength; - ++ fragmentNumber, - fragmentOffset += fragmentLength) - { - if (packet -> dataLength - fragmentOffset < fragmentLength) - fragmentLength = packet -> dataLength - fragmentOffset; - - fragment = (ENetOutgoingCommand *) enet_malloc (sizeof (ENetOutgoingCommand)); - if (fragment == NULL) - { - while (! enet_list_empty (& fragments)) - { - fragment = (ENetOutgoingCommand *) enet_list_remove (enet_list_begin (& fragments)); - - enet_free (fragment); - } - - return -1; - } - - fragment -> fragmentOffset = fragmentOffset; - fragment -> fragmentLength = fragmentLength; - fragment -> packet = packet; - fragment -> command.header.command = commandNumber; - fragment -> command.header.channelID = channelID; - fragment -> command.sendFragment.startSequenceNumber = startSequenceNumber; - fragment -> command.sendFragment.dataLength = ENET_HOST_TO_NET_16 (fragmentLength); - fragment -> command.sendFragment.fragmentCount = ENET_HOST_TO_NET_32 (fragmentCount); - fragment -> command.sendFragment.fragmentNumber = ENET_HOST_TO_NET_32 (fragmentNumber); - fragment -> command.sendFragment.totalLength = ENET_HOST_TO_NET_32 (packet -> dataLength); - fragment -> command.sendFragment.fragmentOffset = ENET_NET_TO_HOST_32 (fragmentOffset); - - enet_list_insert (enet_list_end (& fragments), fragment); - } - - packet -> referenceCount += fragmentNumber; - - while (! enet_list_empty (& fragments)) - { - fragment = (ENetOutgoingCommand *) enet_list_remove (enet_list_begin (& fragments)); - - enet_peer_setup_outgoing_command (peer, fragment); - } - - return 0; - } - - command.header.channelID = channelID; - - if ((packet -> flags & (ENET_PACKET_FLAG_RELIABLE | ENET_PACKET_FLAG_UNSEQUENCED)) == ENET_PACKET_FLAG_UNSEQUENCED) - { - command.header.command = ENET_PROTOCOL_COMMAND_SEND_UNSEQUENCED | ENET_PROTOCOL_COMMAND_FLAG_UNSEQUENCED; - command.sendUnsequenced.dataLength = ENET_HOST_TO_NET_16 (packet -> dataLength); - } - else - if (packet -> flags & ENET_PACKET_FLAG_RELIABLE || channel -> outgoingUnreliableSequenceNumber >= 0xFFFF) - { - command.header.command = ENET_PROTOCOL_COMMAND_SEND_RELIABLE | ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE; - command.sendReliable.dataLength = ENET_HOST_TO_NET_16 (packet -> dataLength); - } - else - { - command.header.command = ENET_PROTOCOL_COMMAND_SEND_UNRELIABLE; - command.sendUnreliable.dataLength = ENET_HOST_TO_NET_16 (packet -> dataLength); - } - - if (enet_peer_queue_outgoing_command (peer, & command, packet, 0, packet -> dataLength) == NULL) - return -1; - - return 0; -} - -/** Attempts to dequeue any incoming queued packet. - @param peer peer to dequeue packets from - @param channelID holds the channel ID of the channel the packet was received on success - @returns a pointer to the packet, or NULL if there are no available incoming queued packets -*/ -ENetPacket * -enet_peer_receive (ENetPeer * peer, enet_uint8 * channelID) -{ - ENetIncomingCommand * incomingCommand; - ENetPacket * packet; - - if (enet_list_empty (& peer -> dispatchedCommands)) - return NULL; - - incomingCommand = (ENetIncomingCommand *) enet_list_remove (enet_list_begin (& peer -> dispatchedCommands)); - - if (channelID != NULL) - * channelID = incomingCommand -> command.header.channelID; - - packet = incomingCommand -> packet; - - -- packet -> referenceCount; - - if (incomingCommand -> fragments != NULL) - enet_free (incomingCommand -> fragments); - - enet_free (incomingCommand); - - peer -> totalWaitingData -= packet -> dataLength; - - return packet; -} - -static void -enet_peer_reset_outgoing_commands (ENetList * queue) -{ - ENetOutgoingCommand * outgoingCommand; - - while (! enet_list_empty (queue)) - { - outgoingCommand = (ENetOutgoingCommand *) enet_list_remove (enet_list_begin (queue)); - - if (outgoingCommand -> packet != NULL) - { - -- outgoingCommand -> packet -> referenceCount; - - if (outgoingCommand -> packet -> referenceCount == 0) - enet_packet_destroy (outgoingCommand -> packet); - } - - enet_free (outgoingCommand); - } -} - -static void -enet_peer_remove_incoming_commands (ENetList * queue, ENetListIterator startCommand, ENetListIterator endCommand, ENetIncomingCommand * excludeCommand) -{ - ENetListIterator currentCommand; - - for (currentCommand = startCommand; currentCommand != endCommand; ) - { - ENetIncomingCommand * incomingCommand = (ENetIncomingCommand *) currentCommand; - - currentCommand = enet_list_next (currentCommand); - - if (incomingCommand == excludeCommand) - continue; - - enet_list_remove (& incomingCommand -> incomingCommandList); - - if (incomingCommand -> packet != NULL) - { - -- incomingCommand -> packet -> referenceCount; - - if (incomingCommand -> packet -> referenceCount == 0) - enet_packet_destroy (incomingCommand -> packet); - } - - if (incomingCommand -> fragments != NULL) - enet_free (incomingCommand -> fragments); - - enet_free (incomingCommand); - } -} - -static void -enet_peer_reset_incoming_commands (ENetList * queue) -{ - enet_peer_remove_incoming_commands(queue, enet_list_begin (queue), enet_list_end (queue), NULL); -} - -void -enet_peer_reset_queues (ENetPeer * peer) -{ - ENetChannel * channel; - - if (peer -> flags & ENET_PEER_FLAG_NEEDS_DISPATCH) - { - enet_list_remove (& peer -> dispatchList); - - peer -> flags &= ~ ENET_PEER_FLAG_NEEDS_DISPATCH; - } - - while (! enet_list_empty (& peer -> acknowledgements)) - enet_free (enet_list_remove (enet_list_begin (& peer -> acknowledgements))); - - enet_peer_reset_outgoing_commands (& peer -> sentReliableCommands); - enet_peer_reset_outgoing_commands (& peer -> sentUnreliableCommands); - enet_peer_reset_outgoing_commands (& peer -> outgoingCommands); - enet_peer_reset_incoming_commands (& peer -> dispatchedCommands); - - if (peer -> channels != NULL && peer -> channelCount > 0) - { - for (channel = peer -> channels; - channel < & peer -> channels [peer -> channelCount]; - ++ channel) - { - enet_peer_reset_incoming_commands (& channel -> incomingReliableCommands); - enet_peer_reset_incoming_commands (& channel -> incomingUnreliableCommands); - } - - enet_free (peer -> channels); - } - - peer -> channels = NULL; - peer -> channelCount = 0; -} - -void -enet_peer_on_connect (ENetPeer * peer) -{ - if (peer -> state != ENET_PEER_STATE_CONNECTED && peer -> state != ENET_PEER_STATE_DISCONNECT_LATER) - { - if (peer -> incomingBandwidth != 0) - ++ peer -> host -> bandwidthLimitedPeers; - - ++ peer -> host -> connectedPeers; - } -} - -void -enet_peer_on_disconnect (ENetPeer * peer) -{ - if (peer -> state == ENET_PEER_STATE_CONNECTED || peer -> state == ENET_PEER_STATE_DISCONNECT_LATER) - { - if (peer -> incomingBandwidth != 0) - -- peer -> host -> bandwidthLimitedPeers; - - -- peer -> host -> connectedPeers; - } -} - -/** Forcefully disconnects a peer. - @param peer peer to forcefully disconnect - @remarks The foreign host represented by the peer is not notified of the disconnection and will timeout - on its connection to the local host. -*/ -void -enet_peer_reset (ENetPeer * peer) -{ - enet_peer_on_disconnect (peer); - - peer -> outgoingPeerID = ENET_PROTOCOL_MAXIMUM_PEER_ID; - peer -> connectID = 0; - - peer -> state = ENET_PEER_STATE_DISCONNECTED; - - peer -> incomingBandwidth = 0; - peer -> outgoingBandwidth = 0; - peer -> incomingBandwidthThrottleEpoch = 0; - peer -> outgoingBandwidthThrottleEpoch = 0; - peer -> incomingDataTotal = 0; - peer -> outgoingDataTotal = 0; - peer -> lastSendTime = 0; - peer -> lastReceiveTime = 0; - peer -> nextTimeout = 0; - peer -> earliestTimeout = 0; - peer -> packetLossEpoch = 0; - peer -> packetsSent = 0; - peer -> packetsLost = 0; - peer -> packetLoss = 0; - peer -> packetLossVariance = 0; - peer -> packetThrottle = ENET_PEER_DEFAULT_PACKET_THROTTLE; - peer -> packetThrottleLimit = ENET_PEER_PACKET_THROTTLE_SCALE; - peer -> packetThrottleCounter = 0; - peer -> packetThrottleEpoch = 0; - peer -> packetThrottleAcceleration = ENET_PEER_PACKET_THROTTLE_ACCELERATION; - peer -> packetThrottleDeceleration = ENET_PEER_PACKET_THROTTLE_DECELERATION; - peer -> packetThrottleInterval = ENET_PEER_PACKET_THROTTLE_INTERVAL; - peer -> pingInterval = ENET_PEER_PING_INTERVAL; - peer -> timeoutLimit = ENET_PEER_TIMEOUT_LIMIT; - peer -> timeoutMinimum = ENET_PEER_TIMEOUT_MINIMUM; - peer -> timeoutMaximum = ENET_PEER_TIMEOUT_MAXIMUM; - peer -> lastRoundTripTime = ENET_PEER_DEFAULT_ROUND_TRIP_TIME; - peer -> lowestRoundTripTime = ENET_PEER_DEFAULT_ROUND_TRIP_TIME; - peer -> lastRoundTripTimeVariance = 0; - peer -> highestRoundTripTimeVariance = 0; - peer -> roundTripTime = ENET_PEER_DEFAULT_ROUND_TRIP_TIME; - peer -> roundTripTimeVariance = 0; - peer -> mtu = peer -> host -> mtu; - peer -> reliableDataInTransit = 0; - peer -> outgoingReliableSequenceNumber = 0; - peer -> windowSize = ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE; - peer -> incomingUnsequencedGroup = 0; - peer -> outgoingUnsequencedGroup = 0; - peer -> eventData = 0; - peer -> totalWaitingData = 0; - peer -> flags = 0; - - memset (peer -> unsequencedWindow, 0, sizeof (peer -> unsequencedWindow)); - - enet_peer_reset_queues (peer); -} - -/** Sends a ping request to a peer. - @param peer destination for the ping request - @remarks ping requests factor into the mean round trip time as designated by the - roundTripTime field in the ENetPeer structure. ENet automatically pings all connected - peers at regular intervals, however, this function may be called to ensure more - frequent ping requests. -*/ -void -enet_peer_ping (ENetPeer * peer) -{ - ENetProtocol command; - - if (peer -> state != ENET_PEER_STATE_CONNECTED) - return; - - command.header.command = ENET_PROTOCOL_COMMAND_PING | ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE; - command.header.channelID = 0xFF; - - enet_peer_queue_outgoing_command (peer, & command, NULL, 0, 0); -} - -/** Sets the interval at which pings will be sent to a peer. - - Pings are used both to monitor the liveness of the connection and also to dynamically - adjust the throttle during periods of low traffic so that the throttle has reasonable - responsiveness during traffic spikes. - - @param peer the peer to adjust - @param pingInterval the interval at which to send pings; defaults to ENET_PEER_PING_INTERVAL if 0 -*/ -void -enet_peer_ping_interval (ENetPeer * peer, enet_uint32 pingInterval) -{ - peer -> pingInterval = pingInterval ? pingInterval : ENET_PEER_PING_INTERVAL; -} - -/** Sets the timeout parameters for a peer. - - The timeout parameter control how and when a peer will timeout from a failure to acknowledge - reliable traffic. Timeout values use an exponential backoff mechanism, where if a reliable - packet is not acknowledge within some multiple of the average RTT plus a variance tolerance, - the timeout will be doubled until it reaches a set limit. If the timeout is thus at this - limit and reliable packets have been sent but not acknowledged within a certain minimum time - period, the peer will be disconnected. Alternatively, if reliable packets have been sent - but not acknowledged for a certain maximum time period, the peer will be disconnected regardless - of the current timeout limit value. - - @param peer the peer to adjust - @param timeoutLimit the timeout limit; defaults to ENET_PEER_TIMEOUT_LIMIT if 0 - @param timeoutMinimum the timeout minimum; defaults to ENET_PEER_TIMEOUT_MINIMUM if 0 - @param timeoutMaximum the timeout maximum; defaults to ENET_PEER_TIMEOUT_MAXIMUM if 0 -*/ - -void -enet_peer_timeout (ENetPeer * peer, enet_uint32 timeoutLimit, enet_uint32 timeoutMinimum, enet_uint32 timeoutMaximum) -{ - peer -> timeoutLimit = timeoutLimit ? timeoutLimit : ENET_PEER_TIMEOUT_LIMIT; - peer -> timeoutMinimum = timeoutMinimum ? timeoutMinimum : ENET_PEER_TIMEOUT_MINIMUM; - peer -> timeoutMaximum = timeoutMaximum ? timeoutMaximum : ENET_PEER_TIMEOUT_MAXIMUM; -} - -/** Force an immediate disconnection from a peer. - @param peer peer to disconnect - @param data data describing the disconnection - @remarks No ENET_EVENT_DISCONNECT event will be generated. The foreign peer is not - guaranteed to receive the disconnect notification, and is reset immediately upon - return from this function. -*/ -void -enet_peer_disconnect_now (ENetPeer * peer, enet_uint32 data) -{ - ENetProtocol command; - - if (peer -> state == ENET_PEER_STATE_DISCONNECTED) - return; - - if (peer -> state != ENET_PEER_STATE_ZOMBIE && - peer -> state != ENET_PEER_STATE_DISCONNECTING) - { - enet_peer_reset_queues (peer); - - command.header.command = ENET_PROTOCOL_COMMAND_DISCONNECT | ENET_PROTOCOL_COMMAND_FLAG_UNSEQUENCED; - command.header.channelID = 0xFF; - command.disconnect.data = ENET_HOST_TO_NET_32 (data); - - enet_peer_queue_outgoing_command (peer, & command, NULL, 0, 0); - - enet_host_flush (peer -> host); - } - - enet_peer_reset (peer); -} - -/** Request a disconnection from a peer. - @param peer peer to request a disconnection - @param data data describing the disconnection - @remarks An ENET_EVENT_DISCONNECT event will be generated by enet_host_service() - once the disconnection is complete. -*/ -void -enet_peer_disconnect (ENetPeer * peer, enet_uint32 data) -{ - ENetProtocol command; - - if (peer -> state == ENET_PEER_STATE_DISCONNECTING || - peer -> state == ENET_PEER_STATE_DISCONNECTED || - peer -> state == ENET_PEER_STATE_ACKNOWLEDGING_DISCONNECT || - peer -> state == ENET_PEER_STATE_ZOMBIE) - return; - - enet_peer_reset_queues (peer); - - command.header.command = ENET_PROTOCOL_COMMAND_DISCONNECT; - command.header.channelID = 0xFF; - command.disconnect.data = ENET_HOST_TO_NET_32 (data); - - if (peer -> state == ENET_PEER_STATE_CONNECTED || peer -> state == ENET_PEER_STATE_DISCONNECT_LATER) - command.header.command |= ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE; - else - command.header.command |= ENET_PROTOCOL_COMMAND_FLAG_UNSEQUENCED; - - enet_peer_queue_outgoing_command (peer, & command, NULL, 0, 0); - - if (peer -> state == ENET_PEER_STATE_CONNECTED || peer -> state == ENET_PEER_STATE_DISCONNECT_LATER) - { - enet_peer_on_disconnect (peer); - - peer -> state = ENET_PEER_STATE_DISCONNECTING; - } - else - { - enet_host_flush (peer -> host); - enet_peer_reset (peer); - } -} - -/** Request a disconnection from a peer, but only after all queued outgoing packets are sent. - @param peer peer to request a disconnection - @param data data describing the disconnection - @remarks An ENET_EVENT_DISCONNECT event will be generated by enet_host_service() - once the disconnection is complete. -*/ -void -enet_peer_disconnect_later (ENetPeer * peer, enet_uint32 data) -{ - if ((peer -> state == ENET_PEER_STATE_CONNECTED || peer -> state == ENET_PEER_STATE_DISCONNECT_LATER) && - ! (enet_list_empty (& peer -> outgoingCommands) && - enet_list_empty (& peer -> sentReliableCommands))) - { - peer -> state = ENET_PEER_STATE_DISCONNECT_LATER; - peer -> eventData = data; - } - else - enet_peer_disconnect (peer, data); -} - -ENetAcknowledgement * -enet_peer_queue_acknowledgement (ENetPeer * peer, const ENetProtocol * command, enet_uint16 sentTime) -{ - ENetAcknowledgement * acknowledgement; - - if (command -> header.channelID < peer -> channelCount) - { - ENetChannel * channel = & peer -> channels [command -> header.channelID]; - enet_uint16 reliableWindow = command -> header.reliableSequenceNumber / ENET_PEER_RELIABLE_WINDOW_SIZE, - currentWindow = channel -> incomingReliableSequenceNumber / ENET_PEER_RELIABLE_WINDOW_SIZE; - - if (command -> header.reliableSequenceNumber < channel -> incomingReliableSequenceNumber) - reliableWindow += ENET_PEER_RELIABLE_WINDOWS; - - if (reliableWindow >= currentWindow + ENET_PEER_FREE_RELIABLE_WINDOWS - 1 && reliableWindow <= currentWindow + ENET_PEER_FREE_RELIABLE_WINDOWS) - return NULL; - } - - acknowledgement = (ENetAcknowledgement *) enet_malloc (sizeof (ENetAcknowledgement)); - if (acknowledgement == NULL) - return NULL; - - peer -> outgoingDataTotal += sizeof (ENetProtocolAcknowledge); - - acknowledgement -> sentTime = sentTime; - acknowledgement -> command = * command; - - enet_list_insert (enet_list_end (& peer -> acknowledgements), acknowledgement); - - return acknowledgement; -} - -void -enet_peer_setup_outgoing_command (ENetPeer * peer, ENetOutgoingCommand * outgoingCommand) -{ - ENetChannel * channel = & peer -> channels [outgoingCommand -> command.header.channelID]; - - peer -> outgoingDataTotal += enet_protocol_command_size (outgoingCommand -> command.header.command) + outgoingCommand -> fragmentLength; - - if (outgoingCommand -> command.header.channelID == 0xFF) - { - ++ peer -> outgoingReliableSequenceNumber; - - outgoingCommand -> reliableSequenceNumber = peer -> outgoingReliableSequenceNumber; - outgoingCommand -> unreliableSequenceNumber = 0; - } - else - if (outgoingCommand -> command.header.command & ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE) - { - ++ channel -> outgoingReliableSequenceNumber; - channel -> outgoingUnreliableSequenceNumber = 0; - - outgoingCommand -> reliableSequenceNumber = channel -> outgoingReliableSequenceNumber; - outgoingCommand -> unreliableSequenceNumber = 0; - } - else - if (outgoingCommand -> command.header.command & ENET_PROTOCOL_COMMAND_FLAG_UNSEQUENCED) - { - ++ peer -> outgoingUnsequencedGroup; - - outgoingCommand -> reliableSequenceNumber = 0; - outgoingCommand -> unreliableSequenceNumber = 0; - } - else - { - if (outgoingCommand -> fragmentOffset == 0) - ++ channel -> outgoingUnreliableSequenceNumber; - - outgoingCommand -> reliableSequenceNumber = channel -> outgoingReliableSequenceNumber; - outgoingCommand -> unreliableSequenceNumber = channel -> outgoingUnreliableSequenceNumber; - } - - outgoingCommand -> sendAttempts = 0; - outgoingCommand -> sentTime = 0; - outgoingCommand -> roundTripTimeout = 0; - outgoingCommand -> roundTripTimeoutLimit = 0; - outgoingCommand -> command.header.reliableSequenceNumber = ENET_HOST_TO_NET_16 (outgoingCommand -> reliableSequenceNumber); - - switch (outgoingCommand -> command.header.command & ENET_PROTOCOL_COMMAND_MASK) - { - case ENET_PROTOCOL_COMMAND_SEND_UNRELIABLE: - outgoingCommand -> command.sendUnreliable.unreliableSequenceNumber = ENET_HOST_TO_NET_16 (outgoingCommand -> unreliableSequenceNumber); - break; - - case ENET_PROTOCOL_COMMAND_SEND_UNSEQUENCED: - outgoingCommand -> command.sendUnsequenced.unsequencedGroup = ENET_HOST_TO_NET_16 (peer -> outgoingUnsequencedGroup); - break; - - default: - break; - } - - enet_list_insert (enet_list_end (& peer -> outgoingCommands), outgoingCommand); -} - -ENetOutgoingCommand * -enet_peer_queue_outgoing_command (ENetPeer * peer, const ENetProtocol * command, ENetPacket * packet, enet_uint32 offset, enet_uint16 length) -{ - ENetOutgoingCommand * outgoingCommand = (ENetOutgoingCommand *) enet_malloc (sizeof (ENetOutgoingCommand)); - if (outgoingCommand == NULL) - return NULL; - - outgoingCommand -> command = * command; - outgoingCommand -> fragmentOffset = offset; - outgoingCommand -> fragmentLength = length; - outgoingCommand -> packet = packet; - if (packet != NULL) - ++ packet -> referenceCount; - - enet_peer_setup_outgoing_command (peer, outgoingCommand); - - return outgoingCommand; -} - -void -enet_peer_dispatch_incoming_unreliable_commands (ENetPeer * peer, ENetChannel * channel, ENetIncomingCommand * queuedCommand) -{ - ENetListIterator droppedCommand, startCommand, currentCommand; - - for (droppedCommand = startCommand = currentCommand = enet_list_begin (& channel -> incomingUnreliableCommands); - currentCommand != enet_list_end (& channel -> incomingUnreliableCommands); - currentCommand = enet_list_next (currentCommand)) - { - ENetIncomingCommand * incomingCommand = (ENetIncomingCommand *) currentCommand; - - if ((incomingCommand -> command.header.command & ENET_PROTOCOL_COMMAND_MASK) == ENET_PROTOCOL_COMMAND_SEND_UNSEQUENCED) - continue; - - if (incomingCommand -> reliableSequenceNumber == channel -> incomingReliableSequenceNumber) - { - if (incomingCommand -> fragmentsRemaining <= 0) - { - channel -> incomingUnreliableSequenceNumber = incomingCommand -> unreliableSequenceNumber; - continue; - } - - if (startCommand != currentCommand) - { - enet_list_move (enet_list_end (& peer -> dispatchedCommands), startCommand, enet_list_previous (currentCommand)); - - if (! (peer -> flags & ENET_PEER_FLAG_NEEDS_DISPATCH)) - { - enet_list_insert (enet_list_end (& peer -> host -> dispatchQueue), & peer -> dispatchList); - - peer -> flags |= ENET_PEER_FLAG_NEEDS_DISPATCH; - } - - droppedCommand = currentCommand; - } - else - if (droppedCommand != currentCommand) - droppedCommand = enet_list_previous (currentCommand); - } - else - { - enet_uint16 reliableWindow = incomingCommand -> reliableSequenceNumber / ENET_PEER_RELIABLE_WINDOW_SIZE, - currentWindow = channel -> incomingReliableSequenceNumber / ENET_PEER_RELIABLE_WINDOW_SIZE; - if (incomingCommand -> reliableSequenceNumber < channel -> incomingReliableSequenceNumber) - reliableWindow += ENET_PEER_RELIABLE_WINDOWS; - if (reliableWindow >= currentWindow && reliableWindow < currentWindow + ENET_PEER_FREE_RELIABLE_WINDOWS - 1) - break; - - droppedCommand = enet_list_next (currentCommand); - - if (startCommand != currentCommand) - { - enet_list_move (enet_list_end (& peer -> dispatchedCommands), startCommand, enet_list_previous (currentCommand)); - - if (! (peer -> flags & ENET_PEER_FLAG_NEEDS_DISPATCH)) - { - enet_list_insert (enet_list_end (& peer -> host -> dispatchQueue), & peer -> dispatchList); - - peer -> flags |= ENET_PEER_FLAG_NEEDS_DISPATCH; - } - } - } - - startCommand = enet_list_next (currentCommand); - } - - if (startCommand != currentCommand) - { - enet_list_move (enet_list_end (& peer -> dispatchedCommands), startCommand, enet_list_previous (currentCommand)); - - if (! (peer -> flags & ENET_PEER_FLAG_NEEDS_DISPATCH)) - { - enet_list_insert (enet_list_end (& peer -> host -> dispatchQueue), & peer -> dispatchList); - - peer -> flags |= ENET_PEER_FLAG_NEEDS_DISPATCH; - } - - droppedCommand = currentCommand; - } - - enet_peer_remove_incoming_commands (& channel -> incomingUnreliableCommands, enet_list_begin (& channel -> incomingUnreliableCommands), droppedCommand, queuedCommand); -} - -void -enet_peer_dispatch_incoming_reliable_commands (ENetPeer * peer, ENetChannel * channel, ENetIncomingCommand * queuedCommand) -{ - ENetListIterator currentCommand; - - for (currentCommand = enet_list_begin (& channel -> incomingReliableCommands); - currentCommand != enet_list_end (& channel -> incomingReliableCommands); - currentCommand = enet_list_next (currentCommand)) - { - ENetIncomingCommand * incomingCommand = (ENetIncomingCommand *) currentCommand; - - if (incomingCommand -> fragmentsRemaining > 0 || - incomingCommand -> reliableSequenceNumber != (enet_uint16) (channel -> incomingReliableSequenceNumber + 1)) - break; - - channel -> incomingReliableSequenceNumber = incomingCommand -> reliableSequenceNumber; - - if (incomingCommand -> fragmentCount > 0) - channel -> incomingReliableSequenceNumber += incomingCommand -> fragmentCount - 1; - } - - if (currentCommand == enet_list_begin (& channel -> incomingReliableCommands)) - return; - - channel -> incomingUnreliableSequenceNumber = 0; - - enet_list_move (enet_list_end (& peer -> dispatchedCommands), enet_list_begin (& channel -> incomingReliableCommands), enet_list_previous (currentCommand)); - - if (! (peer -> flags & ENET_PEER_FLAG_NEEDS_DISPATCH)) - { - enet_list_insert (enet_list_end (& peer -> host -> dispatchQueue), & peer -> dispatchList); - - peer -> flags |= ENET_PEER_FLAG_NEEDS_DISPATCH; - } - - if (! enet_list_empty (& channel -> incomingUnreliableCommands)) - enet_peer_dispatch_incoming_unreliable_commands (peer, channel, queuedCommand); -} - -ENetIncomingCommand * -enet_peer_queue_incoming_command (ENetPeer * peer, const ENetProtocol * command, const void * data, size_t dataLength, enet_uint32 flags, enet_uint32 fragmentCount) -{ - static ENetIncomingCommand dummyCommand; - - ENetChannel * channel = & peer -> channels [command -> header.channelID]; - enet_uint32 unreliableSequenceNumber = 0, reliableSequenceNumber = 0; - enet_uint16 reliableWindow, currentWindow; - ENetIncomingCommand * incomingCommand; - ENetListIterator currentCommand; - ENetPacket * packet = NULL; - - if (peer -> state == ENET_PEER_STATE_DISCONNECT_LATER) - goto discardCommand; - - if ((command -> header.command & ENET_PROTOCOL_COMMAND_MASK) != ENET_PROTOCOL_COMMAND_SEND_UNSEQUENCED) - { - reliableSequenceNumber = command -> header.reliableSequenceNumber; - reliableWindow = reliableSequenceNumber / ENET_PEER_RELIABLE_WINDOW_SIZE; - currentWindow = channel -> incomingReliableSequenceNumber / ENET_PEER_RELIABLE_WINDOW_SIZE; - - if (reliableSequenceNumber < channel -> incomingReliableSequenceNumber) - reliableWindow += ENET_PEER_RELIABLE_WINDOWS; - - if (reliableWindow < currentWindow || reliableWindow >= currentWindow + ENET_PEER_FREE_RELIABLE_WINDOWS - 1) - goto discardCommand; - } - - switch (command -> header.command & ENET_PROTOCOL_COMMAND_MASK) - { - case ENET_PROTOCOL_COMMAND_SEND_FRAGMENT: - case ENET_PROTOCOL_COMMAND_SEND_RELIABLE: - if (reliableSequenceNumber == channel -> incomingReliableSequenceNumber) - goto discardCommand; - - for (currentCommand = enet_list_previous (enet_list_end (& channel -> incomingReliableCommands)); - currentCommand != enet_list_end (& channel -> incomingReliableCommands); - currentCommand = enet_list_previous (currentCommand)) - { - incomingCommand = (ENetIncomingCommand *) currentCommand; - - if (reliableSequenceNumber >= channel -> incomingReliableSequenceNumber) - { - if (incomingCommand -> reliableSequenceNumber < channel -> incomingReliableSequenceNumber) - continue; - } - else - if (incomingCommand -> reliableSequenceNumber >= channel -> incomingReliableSequenceNumber) - break; - - if (incomingCommand -> reliableSequenceNumber <= reliableSequenceNumber) - { - if (incomingCommand -> reliableSequenceNumber < reliableSequenceNumber) - break; - - goto discardCommand; - } - } - break; - - case ENET_PROTOCOL_COMMAND_SEND_UNRELIABLE: - case ENET_PROTOCOL_COMMAND_SEND_UNRELIABLE_FRAGMENT: - unreliableSequenceNumber = ENET_NET_TO_HOST_16 (command -> sendUnreliable.unreliableSequenceNumber); - - if (reliableSequenceNumber == channel -> incomingReliableSequenceNumber && - unreliableSequenceNumber <= channel -> incomingUnreliableSequenceNumber) - goto discardCommand; - - for (currentCommand = enet_list_previous (enet_list_end (& channel -> incomingUnreliableCommands)); - currentCommand != enet_list_end (& channel -> incomingUnreliableCommands); - currentCommand = enet_list_previous (currentCommand)) - { - incomingCommand = (ENetIncomingCommand *) currentCommand; - - if ((command -> header.command & ENET_PROTOCOL_COMMAND_MASK) == ENET_PROTOCOL_COMMAND_SEND_UNSEQUENCED) - continue; - - if (reliableSequenceNumber >= channel -> incomingReliableSequenceNumber) - { - if (incomingCommand -> reliableSequenceNumber < channel -> incomingReliableSequenceNumber) - continue; - } - else - if (incomingCommand -> reliableSequenceNumber >= channel -> incomingReliableSequenceNumber) - break; - - if (incomingCommand -> reliableSequenceNumber < reliableSequenceNumber) - break; - - if (incomingCommand -> reliableSequenceNumber > reliableSequenceNumber) - continue; - - if (incomingCommand -> unreliableSequenceNumber <= unreliableSequenceNumber) - { - if (incomingCommand -> unreliableSequenceNumber < unreliableSequenceNumber) - break; - - goto discardCommand; - } - } - break; - - case ENET_PROTOCOL_COMMAND_SEND_UNSEQUENCED: - currentCommand = enet_list_end (& channel -> incomingUnreliableCommands); - break; - - default: - goto discardCommand; - } - - if (peer -> totalWaitingData >= peer -> host -> maximumWaitingData) - goto notifyError; - - packet = enet_packet_create (data, dataLength, flags); - if (packet == NULL) - goto notifyError; - - incomingCommand = (ENetIncomingCommand *) enet_malloc (sizeof (ENetIncomingCommand)); - if (incomingCommand == NULL) - goto notifyError; - - incomingCommand -> reliableSequenceNumber = command -> header.reliableSequenceNumber; - incomingCommand -> unreliableSequenceNumber = unreliableSequenceNumber & 0xFFFF; - incomingCommand -> command = * command; - incomingCommand -> fragmentCount = fragmentCount; - incomingCommand -> fragmentsRemaining = fragmentCount; - incomingCommand -> packet = packet; - incomingCommand -> fragments = NULL; - - if (fragmentCount > 0) - { - if (fragmentCount <= ENET_PROTOCOL_MAXIMUM_FRAGMENT_COUNT) - incomingCommand -> fragments = (enet_uint32 *) enet_malloc ((fragmentCount + 31) / 32 * sizeof (enet_uint32)); - if (incomingCommand -> fragments == NULL) - { - enet_free (incomingCommand); - - goto notifyError; - } - memset (incomingCommand -> fragments, 0, (fragmentCount + 31) / 32 * sizeof (enet_uint32)); - } - - if (packet != NULL) - { - ++ packet -> referenceCount; - - peer -> totalWaitingData += packet -> dataLength; - } - - enet_list_insert (enet_list_next (currentCommand), incomingCommand); - - switch (command -> header.command & ENET_PROTOCOL_COMMAND_MASK) - { - case ENET_PROTOCOL_COMMAND_SEND_FRAGMENT: - case ENET_PROTOCOL_COMMAND_SEND_RELIABLE: - enet_peer_dispatch_incoming_reliable_commands (peer, channel, incomingCommand); - break; - - default: - enet_peer_dispatch_incoming_unreliable_commands (peer, channel, incomingCommand); - break; - } - - return incomingCommand; - -discardCommand: - if (fragmentCount > 0) - goto notifyError; - - if (packet != NULL && packet -> referenceCount == 0) - enet_packet_destroy (packet); - - return & dummyCommand; - -notifyError: - if (packet != NULL && packet -> referenceCount == 0) - enet_packet_destroy (packet); - - return NULL; -} - -/** @} */ diff --git a/include/enet/premake4.lua b/include/enet/premake4.lua deleted file mode 100644 index 0e6e7ad..0000000 --- a/include/enet/premake4.lua +++ /dev/null @@ -1,59 +0,0 @@ -solution "enet" - configurations { "Debug", "Release" } - platforms { "x32", "x64" } - - project "enet_static" - kind "StaticLib" - language "C" - - files { "*.c" } - - includedirs { "include/" } - - configuration "Debug" - targetsuffix "d" - - defines({ "DEBUG" }) - - flags { "Symbols" } - - configuration "Release" - defines({ "NDEBUG" }) - - flags { "Optimize" } - - configuration { "Debug", "x64" } - targetsuffix "64d" - - configuration { "Release", "x64" } - targetsuffix "64" - - project "enet" - kind "SharedLib" - language "C" - - files { "*.c" } - - includedirs { "include/" } - - defines({"ENET_DLL=1" }) - - configuration "Debug" - targetsuffix "d" - - defines({ "DEBUG" }) - - flags { "Symbols" } - - configuration "Release" - defines({ "NDEBUG" }) - - flags { "Optimize" } - - configuration { "Debug", "x64" } - targetsuffix "64d" - - configuration { "Release", "x64" } - targetsuffix "64" - - \ No newline at end of file diff --git a/include/enet/protocol.c b/include/enet/protocol.c deleted file mode 100644 index 9dde122..0000000 --- a/include/enet/protocol.c +++ /dev/null @@ -1,1877 +0,0 @@ -/** - @file protocol.c - @brief ENet protocol functions -*/ -#include -#include -#define ENET_BUILDING_LIB 1 -#include "enet/utility.h" -#include "enet/time.h" -#include "enet/enet.h" - -static size_t commandSizes [ENET_PROTOCOL_COMMAND_COUNT] = -{ - 0, - sizeof (ENetProtocolAcknowledge), - sizeof (ENetProtocolConnect), - sizeof (ENetProtocolVerifyConnect), - sizeof (ENetProtocolDisconnect), - sizeof (ENetProtocolPing), - sizeof (ENetProtocolSendReliable), - sizeof (ENetProtocolSendUnreliable), - sizeof (ENetProtocolSendFragment), - sizeof (ENetProtocolSendUnsequenced), - sizeof (ENetProtocolBandwidthLimit), - sizeof (ENetProtocolThrottleConfigure), - sizeof (ENetProtocolSendFragment) -}; - -size_t -enet_protocol_command_size (enet_uint8 commandNumber) -{ - return commandSizes [commandNumber & ENET_PROTOCOL_COMMAND_MASK]; -} - -static void -enet_protocol_change_state (ENetHost * host, ENetPeer * peer, ENetPeerState state) -{ - if (state == ENET_PEER_STATE_CONNECTED || state == ENET_PEER_STATE_DISCONNECT_LATER) - enet_peer_on_connect (peer); - else - enet_peer_on_disconnect (peer); - - peer -> state = state; -} - -static void -enet_protocol_dispatch_state (ENetHost * host, ENetPeer * peer, ENetPeerState state) -{ - enet_protocol_change_state (host, peer, state); - - if (! (peer -> flags & ENET_PEER_FLAG_NEEDS_DISPATCH)) - { - enet_list_insert (enet_list_end (& host -> dispatchQueue), & peer -> dispatchList); - - peer -> flags |= ENET_PEER_FLAG_NEEDS_DISPATCH; - } -} - -static int -enet_protocol_dispatch_incoming_commands (ENetHost * host, ENetEvent * event) -{ - while (! enet_list_empty (& host -> dispatchQueue)) - { - ENetPeer * peer = (ENetPeer *) enet_list_remove (enet_list_begin (& host -> dispatchQueue)); - - peer -> flags &= ~ ENET_PEER_FLAG_NEEDS_DISPATCH; - - switch (peer -> state) - { - case ENET_PEER_STATE_CONNECTION_PENDING: - case ENET_PEER_STATE_CONNECTION_SUCCEEDED: - enet_protocol_change_state (host, peer, ENET_PEER_STATE_CONNECTED); - - event -> type = ENET_EVENT_TYPE_CONNECT; - event -> peer = peer; - event -> data = peer -> eventData; - - return 1; - - case ENET_PEER_STATE_ZOMBIE: - host -> recalculateBandwidthLimits = 1; - - event -> type = ENET_EVENT_TYPE_DISCONNECT; - event -> peer = peer; - event -> data = peer -> eventData; - - enet_peer_reset (peer); - - return 1; - - case ENET_PEER_STATE_CONNECTED: - if (enet_list_empty (& peer -> dispatchedCommands)) - continue; - - event -> packet = enet_peer_receive (peer, & event -> channelID); - if (event -> packet == NULL) - continue; - - event -> type = ENET_EVENT_TYPE_RECEIVE; - event -> peer = peer; - - if (! enet_list_empty (& peer -> dispatchedCommands)) - { - peer -> flags |= ENET_PEER_FLAG_NEEDS_DISPATCH; - - enet_list_insert (enet_list_end (& host -> dispatchQueue), & peer -> dispatchList); - } - - return 1; - - default: - break; - } - } - - return 0; -} - -static void -enet_protocol_notify_connect (ENetHost * host, ENetPeer * peer, ENetEvent * event) -{ - host -> recalculateBandwidthLimits = 1; - - if (event != NULL) - { - enet_protocol_change_state (host, peer, ENET_PEER_STATE_CONNECTED); - - event -> type = ENET_EVENT_TYPE_CONNECT; - event -> peer = peer; - event -> data = peer -> eventData; - } - else - enet_protocol_dispatch_state (host, peer, peer -> state == ENET_PEER_STATE_CONNECTING ? ENET_PEER_STATE_CONNECTION_SUCCEEDED : ENET_PEER_STATE_CONNECTION_PENDING); -} - -static void -enet_protocol_notify_disconnect (ENetHost * host, ENetPeer * peer, ENetEvent * event) -{ - if (peer -> state >= ENET_PEER_STATE_CONNECTION_PENDING) - host -> recalculateBandwidthLimits = 1; - - if (peer -> state != ENET_PEER_STATE_CONNECTING && peer -> state < ENET_PEER_STATE_CONNECTION_SUCCEEDED) - enet_peer_reset (peer); - else - if (event != NULL) - { - event -> type = ENET_EVENT_TYPE_DISCONNECT; - event -> peer = peer; - event -> data = 0; - - enet_peer_reset (peer); - } - else - { - peer -> eventData = 0; - - enet_protocol_dispatch_state (host, peer, ENET_PEER_STATE_ZOMBIE); - } -} - -static void -enet_protocol_remove_sent_unreliable_commands (ENetPeer * peer) -{ - ENetOutgoingCommand * outgoingCommand; - - if (enet_list_empty (& peer -> sentUnreliableCommands)) - return; - - do - { - outgoingCommand = (ENetOutgoingCommand *) enet_list_front (& peer -> sentUnreliableCommands); - - enet_list_remove (& outgoingCommand -> outgoingCommandList); - - if (outgoingCommand -> packet != NULL) - { - -- outgoingCommand -> packet -> referenceCount; - - if (outgoingCommand -> packet -> referenceCount == 0) - { - outgoingCommand -> packet -> flags |= ENET_PACKET_FLAG_SENT; - - enet_packet_destroy (outgoingCommand -> packet); - } - } - - enet_free (outgoingCommand); - } while (! enet_list_empty (& peer -> sentUnreliableCommands)); - - if (peer -> state == ENET_PEER_STATE_DISCONNECT_LATER && - enet_list_empty (& peer -> outgoingCommands) && - enet_list_empty (& peer -> sentReliableCommands)) - enet_peer_disconnect (peer, peer -> eventData); -} - -static ENetProtocolCommand -enet_protocol_remove_sent_reliable_command (ENetPeer * peer, enet_uint16 reliableSequenceNumber, enet_uint8 channelID) -{ - ENetOutgoingCommand * outgoingCommand = NULL; - ENetListIterator currentCommand; - ENetProtocolCommand commandNumber; - int wasSent = 1; - - for (currentCommand = enet_list_begin (& peer -> sentReliableCommands); - currentCommand != enet_list_end (& peer -> sentReliableCommands); - currentCommand = enet_list_next (currentCommand)) - { - outgoingCommand = (ENetOutgoingCommand *) currentCommand; - - if (outgoingCommand -> reliableSequenceNumber == reliableSequenceNumber && - outgoingCommand -> command.header.channelID == channelID) - break; - } - - if (currentCommand == enet_list_end (& peer -> sentReliableCommands)) - { - for (currentCommand = enet_list_begin (& peer -> outgoingCommands); - currentCommand != enet_list_end (& peer -> outgoingCommands); - currentCommand = enet_list_next (currentCommand)) - { - outgoingCommand = (ENetOutgoingCommand *) currentCommand; - - if (! (outgoingCommand -> command.header.command & ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE)) - continue; - - if (outgoingCommand -> sendAttempts < 1) return ENET_PROTOCOL_COMMAND_NONE; - - if (outgoingCommand -> reliableSequenceNumber == reliableSequenceNumber && - outgoingCommand -> command.header.channelID == channelID) - break; - } - - if (currentCommand == enet_list_end (& peer -> outgoingCommands)) - return ENET_PROTOCOL_COMMAND_NONE; - - wasSent = 0; - } - - if (outgoingCommand == NULL) - return ENET_PROTOCOL_COMMAND_NONE; - - if (channelID < peer -> channelCount) - { - ENetChannel * channel = & peer -> channels [channelID]; - enet_uint16 reliableWindow = reliableSequenceNumber / ENET_PEER_RELIABLE_WINDOW_SIZE; - if (channel -> reliableWindows [reliableWindow] > 0) - { - -- channel -> reliableWindows [reliableWindow]; - if (! channel -> reliableWindows [reliableWindow]) - channel -> usedReliableWindows &= ~ (1 << reliableWindow); - } - } - - commandNumber = (ENetProtocolCommand) (outgoingCommand -> command.header.command & ENET_PROTOCOL_COMMAND_MASK); - - enet_list_remove (& outgoingCommand -> outgoingCommandList); - - if (outgoingCommand -> packet != NULL) - { - if (wasSent) - peer -> reliableDataInTransit -= outgoingCommand -> fragmentLength; - - -- outgoingCommand -> packet -> referenceCount; - - if (outgoingCommand -> packet -> referenceCount == 0) - { - outgoingCommand -> packet -> flags |= ENET_PACKET_FLAG_SENT; - - enet_packet_destroy (outgoingCommand -> packet); - } - } - - enet_free (outgoingCommand); - - if (enet_list_empty (& peer -> sentReliableCommands)) - return commandNumber; - - outgoingCommand = (ENetOutgoingCommand *) enet_list_front (& peer -> sentReliableCommands); - - peer -> nextTimeout = outgoingCommand -> sentTime + outgoingCommand -> roundTripTimeout; - - return commandNumber; -} - -static ENetPeer * -enet_protocol_handle_connect (ENetHost * host, ENetProtocolHeader * header, ENetProtocol * command) -{ - enet_uint8 incomingSessionID, outgoingSessionID; - enet_uint32 mtu, windowSize; - ENetChannel * channel; - size_t channelCount, duplicatePeers = 0; - ENetPeer * currentPeer, * peer = NULL; - ENetProtocol verifyCommand; - - channelCount = ENET_NET_TO_HOST_32 (command -> connect.channelCount); - - if (channelCount < ENET_PROTOCOL_MINIMUM_CHANNEL_COUNT || - channelCount > ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT) - return NULL; - - for (currentPeer = host -> peers; - currentPeer < & host -> peers [host -> peerCount]; - ++ currentPeer) - { - if (currentPeer -> state == ENET_PEER_STATE_DISCONNECTED) - { - if (peer == NULL) - peer = currentPeer; - } - else - if (currentPeer -> state != ENET_PEER_STATE_CONNECTING && - currentPeer -> address.host == host -> receivedAddress.host) - { - if (currentPeer -> address.port == host -> receivedAddress.port && - currentPeer -> connectID == command -> connect.connectID) - return NULL; - - ++ duplicatePeers; - } - } - - if (peer == NULL || duplicatePeers >= host -> duplicatePeers) - return NULL; - - if (channelCount > host -> channelLimit) - channelCount = host -> channelLimit; - peer -> channels = (ENetChannel *) enet_malloc (channelCount * sizeof (ENetChannel)); - if (peer -> channels == NULL) - return NULL; - peer -> channelCount = channelCount; - peer -> state = ENET_PEER_STATE_ACKNOWLEDGING_CONNECT; - peer -> connectID = command -> connect.connectID; - peer -> address = host -> receivedAddress; - peer -> outgoingPeerID = ENET_NET_TO_HOST_16 (command -> connect.outgoingPeerID); - peer -> incomingBandwidth = ENET_NET_TO_HOST_32 (command -> connect.incomingBandwidth); - peer -> outgoingBandwidth = ENET_NET_TO_HOST_32 (command -> connect.outgoingBandwidth); - peer -> packetThrottleInterval = ENET_NET_TO_HOST_32 (command -> connect.packetThrottleInterval); - peer -> packetThrottleAcceleration = ENET_NET_TO_HOST_32 (command -> connect.packetThrottleAcceleration); - peer -> packetThrottleDeceleration = ENET_NET_TO_HOST_32 (command -> connect.packetThrottleDeceleration); - peer -> eventData = ENET_NET_TO_HOST_32 (command -> connect.data); - - incomingSessionID = command -> connect.incomingSessionID == 0xFF ? peer -> outgoingSessionID : command -> connect.incomingSessionID; - incomingSessionID = (incomingSessionID + 1) & (ENET_PROTOCOL_HEADER_SESSION_MASK >> ENET_PROTOCOL_HEADER_SESSION_SHIFT); - if (incomingSessionID == peer -> outgoingSessionID) - incomingSessionID = (incomingSessionID + 1) & (ENET_PROTOCOL_HEADER_SESSION_MASK >> ENET_PROTOCOL_HEADER_SESSION_SHIFT); - peer -> outgoingSessionID = incomingSessionID; - - outgoingSessionID = command -> connect.outgoingSessionID == 0xFF ? peer -> incomingSessionID : command -> connect.outgoingSessionID; - outgoingSessionID = (outgoingSessionID + 1) & (ENET_PROTOCOL_HEADER_SESSION_MASK >> ENET_PROTOCOL_HEADER_SESSION_SHIFT); - if (outgoingSessionID == peer -> incomingSessionID) - outgoingSessionID = (outgoingSessionID + 1) & (ENET_PROTOCOL_HEADER_SESSION_MASK >> ENET_PROTOCOL_HEADER_SESSION_SHIFT); - peer -> incomingSessionID = outgoingSessionID; - - for (channel = peer -> channels; - channel < & peer -> channels [channelCount]; - ++ channel) - { - channel -> outgoingReliableSequenceNumber = 0; - channel -> outgoingUnreliableSequenceNumber = 0; - channel -> incomingReliableSequenceNumber = 0; - channel -> incomingUnreliableSequenceNumber = 0; - - enet_list_clear (& channel -> incomingReliableCommands); - enet_list_clear (& channel -> incomingUnreliableCommands); - - channel -> usedReliableWindows = 0; - memset (channel -> reliableWindows, 0, sizeof (channel -> reliableWindows)); - } - - mtu = ENET_NET_TO_HOST_32 (command -> connect.mtu); - - if (mtu < ENET_PROTOCOL_MINIMUM_MTU) - mtu = ENET_PROTOCOL_MINIMUM_MTU; - else - if (mtu > ENET_PROTOCOL_MAXIMUM_MTU) - mtu = ENET_PROTOCOL_MAXIMUM_MTU; - - peer -> mtu = mtu; - - if (host -> outgoingBandwidth == 0 && - peer -> incomingBandwidth == 0) - peer -> windowSize = ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE; - else - if (host -> outgoingBandwidth == 0 || - peer -> incomingBandwidth == 0) - peer -> windowSize = (ENET_MAX (host -> outgoingBandwidth, peer -> incomingBandwidth) / - ENET_PEER_WINDOW_SIZE_SCALE) * - ENET_PROTOCOL_MINIMUM_WINDOW_SIZE; - else - peer -> windowSize = (ENET_MIN (host -> outgoingBandwidth, peer -> incomingBandwidth) / - ENET_PEER_WINDOW_SIZE_SCALE) * - ENET_PROTOCOL_MINIMUM_WINDOW_SIZE; - - if (peer -> windowSize < ENET_PROTOCOL_MINIMUM_WINDOW_SIZE) - peer -> windowSize = ENET_PROTOCOL_MINIMUM_WINDOW_SIZE; - else - if (peer -> windowSize > ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE) - peer -> windowSize = ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE; - - if (host -> incomingBandwidth == 0) - windowSize = ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE; - else - windowSize = (host -> incomingBandwidth / ENET_PEER_WINDOW_SIZE_SCALE) * - ENET_PROTOCOL_MINIMUM_WINDOW_SIZE; - - if (windowSize > ENET_NET_TO_HOST_32 (command -> connect.windowSize)) - windowSize = ENET_NET_TO_HOST_32 (command -> connect.windowSize); - - if (windowSize < ENET_PROTOCOL_MINIMUM_WINDOW_SIZE) - windowSize = ENET_PROTOCOL_MINIMUM_WINDOW_SIZE; - else - if (windowSize > ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE) - windowSize = ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE; - - verifyCommand.header.command = ENET_PROTOCOL_COMMAND_VERIFY_CONNECT | ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE; - verifyCommand.header.channelID = 0xFF; - verifyCommand.verifyConnect.outgoingPeerID = ENET_HOST_TO_NET_16 (peer -> incomingPeerID); - verifyCommand.verifyConnect.incomingSessionID = incomingSessionID; - verifyCommand.verifyConnect.outgoingSessionID = outgoingSessionID; - verifyCommand.verifyConnect.mtu = ENET_HOST_TO_NET_32 (peer -> mtu); - verifyCommand.verifyConnect.windowSize = ENET_HOST_TO_NET_32 (windowSize); - verifyCommand.verifyConnect.channelCount = ENET_HOST_TO_NET_32 (channelCount); - verifyCommand.verifyConnect.incomingBandwidth = ENET_HOST_TO_NET_32 (host -> incomingBandwidth); - verifyCommand.verifyConnect.outgoingBandwidth = ENET_HOST_TO_NET_32 (host -> outgoingBandwidth); - verifyCommand.verifyConnect.packetThrottleInterval = ENET_HOST_TO_NET_32 (peer -> packetThrottleInterval); - verifyCommand.verifyConnect.packetThrottleAcceleration = ENET_HOST_TO_NET_32 (peer -> packetThrottleAcceleration); - verifyCommand.verifyConnect.packetThrottleDeceleration = ENET_HOST_TO_NET_32 (peer -> packetThrottleDeceleration); - verifyCommand.verifyConnect.connectID = peer -> connectID; - - enet_peer_queue_outgoing_command (peer, & verifyCommand, NULL, 0, 0); - - return peer; -} - -static int -enet_protocol_handle_send_reliable (ENetHost * host, ENetPeer * peer, const ENetProtocol * command, enet_uint8 ** currentData) -{ - size_t dataLength; - - if (command -> header.channelID >= peer -> channelCount || - (peer -> state != ENET_PEER_STATE_CONNECTED && peer -> state != ENET_PEER_STATE_DISCONNECT_LATER)) - return -1; - - dataLength = ENET_NET_TO_HOST_16 (command -> sendReliable.dataLength); - * currentData += dataLength; - if (dataLength > host -> maximumPacketSize || - * currentData < host -> receivedData || - * currentData > & host -> receivedData [host -> receivedDataLength]) - return -1; - - if (enet_peer_queue_incoming_command (peer, command, (const enet_uint8 *) command + sizeof (ENetProtocolSendReliable), dataLength, ENET_PACKET_FLAG_RELIABLE, 0) == NULL) - return -1; - - return 0; -} - -static int -enet_protocol_handle_send_unsequenced (ENetHost * host, ENetPeer * peer, const ENetProtocol * command, enet_uint8 ** currentData) -{ - enet_uint32 unsequencedGroup, index; - size_t dataLength; - - if (command -> header.channelID >= peer -> channelCount || - (peer -> state != ENET_PEER_STATE_CONNECTED && peer -> state != ENET_PEER_STATE_DISCONNECT_LATER)) - return -1; - - dataLength = ENET_NET_TO_HOST_16 (command -> sendUnsequenced.dataLength); - * currentData += dataLength; - if (dataLength > host -> maximumPacketSize || - * currentData < host -> receivedData || - * currentData > & host -> receivedData [host -> receivedDataLength]) - return -1; - - unsequencedGroup = ENET_NET_TO_HOST_16 (command -> sendUnsequenced.unsequencedGroup); - index = unsequencedGroup % ENET_PEER_UNSEQUENCED_WINDOW_SIZE; - - if (unsequencedGroup < peer -> incomingUnsequencedGroup) - unsequencedGroup += 0x10000; - - if (unsequencedGroup >= (enet_uint32) peer -> incomingUnsequencedGroup + ENET_PEER_FREE_UNSEQUENCED_WINDOWS * ENET_PEER_UNSEQUENCED_WINDOW_SIZE) - return 0; - - unsequencedGroup &= 0xFFFF; - - if (unsequencedGroup - index != peer -> incomingUnsequencedGroup) - { - peer -> incomingUnsequencedGroup = unsequencedGroup - index; - - memset (peer -> unsequencedWindow, 0, sizeof (peer -> unsequencedWindow)); - } - else - if (peer -> unsequencedWindow [index / 32] & (1 << (index % 32))) - return 0; - - if (enet_peer_queue_incoming_command (peer, command, (const enet_uint8 *) command + sizeof (ENetProtocolSendUnsequenced), dataLength, ENET_PACKET_FLAG_UNSEQUENCED, 0) == NULL) - return -1; - - peer -> unsequencedWindow [index / 32] |= 1 << (index % 32); - - return 0; -} - -static int -enet_protocol_handle_send_unreliable (ENetHost * host, ENetPeer * peer, const ENetProtocol * command, enet_uint8 ** currentData) -{ - size_t dataLength; - - if (command -> header.channelID >= peer -> channelCount || - (peer -> state != ENET_PEER_STATE_CONNECTED && peer -> state != ENET_PEER_STATE_DISCONNECT_LATER)) - return -1; - - dataLength = ENET_NET_TO_HOST_16 (command -> sendUnreliable.dataLength); - * currentData += dataLength; - if (dataLength > host -> maximumPacketSize || - * currentData < host -> receivedData || - * currentData > & host -> receivedData [host -> receivedDataLength]) - return -1; - - if (enet_peer_queue_incoming_command (peer, command, (const enet_uint8 *) command + sizeof (ENetProtocolSendUnreliable), dataLength, 0, 0) == NULL) - return -1; - - return 0; -} - -static int -enet_protocol_handle_send_fragment (ENetHost * host, ENetPeer * peer, const ENetProtocol * command, enet_uint8 ** currentData) -{ - enet_uint32 fragmentNumber, - fragmentCount, - fragmentOffset, - fragmentLength, - startSequenceNumber, - totalLength; - ENetChannel * channel; - enet_uint16 startWindow, currentWindow; - ENetListIterator currentCommand; - ENetIncomingCommand * startCommand = NULL; - - if (command -> header.channelID >= peer -> channelCount || - (peer -> state != ENET_PEER_STATE_CONNECTED && peer -> state != ENET_PEER_STATE_DISCONNECT_LATER)) - return -1; - - fragmentLength = ENET_NET_TO_HOST_16 (command -> sendFragment.dataLength); - * currentData += fragmentLength; - if (fragmentLength > host -> maximumPacketSize || - * currentData < host -> receivedData || - * currentData > & host -> receivedData [host -> receivedDataLength]) - return -1; - - channel = & peer -> channels [command -> header.channelID]; - startSequenceNumber = ENET_NET_TO_HOST_16 (command -> sendFragment.startSequenceNumber); - startWindow = startSequenceNumber / ENET_PEER_RELIABLE_WINDOW_SIZE; - currentWindow = channel -> incomingReliableSequenceNumber / ENET_PEER_RELIABLE_WINDOW_SIZE; - - if (startSequenceNumber < channel -> incomingReliableSequenceNumber) - startWindow += ENET_PEER_RELIABLE_WINDOWS; - - if (startWindow < currentWindow || startWindow >= currentWindow + ENET_PEER_FREE_RELIABLE_WINDOWS - 1) - return 0; - - fragmentNumber = ENET_NET_TO_HOST_32 (command -> sendFragment.fragmentNumber); - fragmentCount = ENET_NET_TO_HOST_32 (command -> sendFragment.fragmentCount); - fragmentOffset = ENET_NET_TO_HOST_32 (command -> sendFragment.fragmentOffset); - totalLength = ENET_NET_TO_HOST_32 (command -> sendFragment.totalLength); - - if (fragmentCount > ENET_PROTOCOL_MAXIMUM_FRAGMENT_COUNT || - fragmentNumber >= fragmentCount || - totalLength > host -> maximumPacketSize || - fragmentOffset >= totalLength || - fragmentLength > totalLength - fragmentOffset) - return -1; - - for (currentCommand = enet_list_previous (enet_list_end (& channel -> incomingReliableCommands)); - currentCommand != enet_list_end (& channel -> incomingReliableCommands); - currentCommand = enet_list_previous (currentCommand)) - { - ENetIncomingCommand * incomingCommand = (ENetIncomingCommand *) currentCommand; - - if (startSequenceNumber >= channel -> incomingReliableSequenceNumber) - { - if (incomingCommand -> reliableSequenceNumber < channel -> incomingReliableSequenceNumber) - continue; - } - else - if (incomingCommand -> reliableSequenceNumber >= channel -> incomingReliableSequenceNumber) - break; - - if (incomingCommand -> reliableSequenceNumber <= startSequenceNumber) - { - if (incomingCommand -> reliableSequenceNumber < startSequenceNumber) - break; - - if ((incomingCommand -> command.header.command & ENET_PROTOCOL_COMMAND_MASK) != ENET_PROTOCOL_COMMAND_SEND_FRAGMENT || - totalLength != incomingCommand -> packet -> dataLength || - fragmentCount != incomingCommand -> fragmentCount) - return -1; - - startCommand = incomingCommand; - break; - } - } - - if (startCommand == NULL) - { - ENetProtocol hostCommand = * command; - - hostCommand.header.reliableSequenceNumber = startSequenceNumber; - - startCommand = enet_peer_queue_incoming_command (peer, & hostCommand, NULL, totalLength, ENET_PACKET_FLAG_RELIABLE, fragmentCount); - if (startCommand == NULL) - return -1; - } - - if ((startCommand -> fragments [fragmentNumber / 32] & (1 << (fragmentNumber % 32))) == 0) - { - -- startCommand -> fragmentsRemaining; - - startCommand -> fragments [fragmentNumber / 32] |= (1 << (fragmentNumber % 32)); - - if (fragmentOffset + fragmentLength > startCommand -> packet -> dataLength) - fragmentLength = startCommand -> packet -> dataLength - fragmentOffset; - - memcpy (startCommand -> packet -> data + fragmentOffset, - (enet_uint8 *) command + sizeof (ENetProtocolSendFragment), - fragmentLength); - - if (startCommand -> fragmentsRemaining <= 0) - enet_peer_dispatch_incoming_reliable_commands (peer, channel, NULL); - } - - return 0; -} - -static int -enet_protocol_handle_send_unreliable_fragment (ENetHost * host, ENetPeer * peer, const ENetProtocol * command, enet_uint8 ** currentData) -{ - enet_uint32 fragmentNumber, - fragmentCount, - fragmentOffset, - fragmentLength, - reliableSequenceNumber, - startSequenceNumber, - totalLength; - enet_uint16 reliableWindow, currentWindow; - ENetChannel * channel; - ENetListIterator currentCommand; - ENetIncomingCommand * startCommand = NULL; - - if (command -> header.channelID >= peer -> channelCount || - (peer -> state != ENET_PEER_STATE_CONNECTED && peer -> state != ENET_PEER_STATE_DISCONNECT_LATER)) - return -1; - - fragmentLength = ENET_NET_TO_HOST_16 (command -> sendFragment.dataLength); - * currentData += fragmentLength; - if (fragmentLength > host -> maximumPacketSize || - * currentData < host -> receivedData || - * currentData > & host -> receivedData [host -> receivedDataLength]) - return -1; - - channel = & peer -> channels [command -> header.channelID]; - reliableSequenceNumber = command -> header.reliableSequenceNumber; - startSequenceNumber = ENET_NET_TO_HOST_16 (command -> sendFragment.startSequenceNumber); - - reliableWindow = reliableSequenceNumber / ENET_PEER_RELIABLE_WINDOW_SIZE; - currentWindow = channel -> incomingReliableSequenceNumber / ENET_PEER_RELIABLE_WINDOW_SIZE; - - if (reliableSequenceNumber < channel -> incomingReliableSequenceNumber) - reliableWindow += ENET_PEER_RELIABLE_WINDOWS; - - if (reliableWindow < currentWindow || reliableWindow >= currentWindow + ENET_PEER_FREE_RELIABLE_WINDOWS - 1) - return 0; - - if (reliableSequenceNumber == channel -> incomingReliableSequenceNumber && - startSequenceNumber <= channel -> incomingUnreliableSequenceNumber) - return 0; - - fragmentNumber = ENET_NET_TO_HOST_32 (command -> sendFragment.fragmentNumber); - fragmentCount = ENET_NET_TO_HOST_32 (command -> sendFragment.fragmentCount); - fragmentOffset = ENET_NET_TO_HOST_32 (command -> sendFragment.fragmentOffset); - totalLength = ENET_NET_TO_HOST_32 (command -> sendFragment.totalLength); - - if (fragmentCount > ENET_PROTOCOL_MAXIMUM_FRAGMENT_COUNT || - fragmentNumber >= fragmentCount || - totalLength > host -> maximumPacketSize || - fragmentOffset >= totalLength || - fragmentLength > totalLength - fragmentOffset) - return -1; - - for (currentCommand = enet_list_previous (enet_list_end (& channel -> incomingUnreliableCommands)); - currentCommand != enet_list_end (& channel -> incomingUnreliableCommands); - currentCommand = enet_list_previous (currentCommand)) - { - ENetIncomingCommand * incomingCommand = (ENetIncomingCommand *) currentCommand; - - if (reliableSequenceNumber >= channel -> incomingReliableSequenceNumber) - { - if (incomingCommand -> reliableSequenceNumber < channel -> incomingReliableSequenceNumber) - continue; - } - else - if (incomingCommand -> reliableSequenceNumber >= channel -> incomingReliableSequenceNumber) - break; - - if (incomingCommand -> reliableSequenceNumber < reliableSequenceNumber) - break; - - if (incomingCommand -> reliableSequenceNumber > reliableSequenceNumber) - continue; - - if (incomingCommand -> unreliableSequenceNumber <= startSequenceNumber) - { - if (incomingCommand -> unreliableSequenceNumber < startSequenceNumber) - break; - - if ((incomingCommand -> command.header.command & ENET_PROTOCOL_COMMAND_MASK) != ENET_PROTOCOL_COMMAND_SEND_UNRELIABLE_FRAGMENT || - totalLength != incomingCommand -> packet -> dataLength || - fragmentCount != incomingCommand -> fragmentCount) - return -1; - - startCommand = incomingCommand; - break; - } - } - - if (startCommand == NULL) - { - startCommand = enet_peer_queue_incoming_command (peer, command, NULL, totalLength, ENET_PACKET_FLAG_UNRELIABLE_FRAGMENT, fragmentCount); - if (startCommand == NULL) - return -1; - } - - if ((startCommand -> fragments [fragmentNumber / 32] & (1 << (fragmentNumber % 32))) == 0) - { - -- startCommand -> fragmentsRemaining; - - startCommand -> fragments [fragmentNumber / 32] |= (1 << (fragmentNumber % 32)); - - if (fragmentOffset + fragmentLength > startCommand -> packet -> dataLength) - fragmentLength = startCommand -> packet -> dataLength - fragmentOffset; - - memcpy (startCommand -> packet -> data + fragmentOffset, - (enet_uint8 *) command + sizeof (ENetProtocolSendFragment), - fragmentLength); - - if (startCommand -> fragmentsRemaining <= 0) - enet_peer_dispatch_incoming_unreliable_commands (peer, channel, NULL); - } - - return 0; -} - -static int -enet_protocol_handle_ping (ENetHost * host, ENetPeer * peer, const ENetProtocol * command) -{ - if (peer -> state != ENET_PEER_STATE_CONNECTED && peer -> state != ENET_PEER_STATE_DISCONNECT_LATER) - return -1; - - return 0; -} - -static int -enet_protocol_handle_bandwidth_limit (ENetHost * host, ENetPeer * peer, const ENetProtocol * command) -{ - if (peer -> state != ENET_PEER_STATE_CONNECTED && peer -> state != ENET_PEER_STATE_DISCONNECT_LATER) - return -1; - - if (peer -> incomingBandwidth != 0) - -- host -> bandwidthLimitedPeers; - - peer -> incomingBandwidth = ENET_NET_TO_HOST_32 (command -> bandwidthLimit.incomingBandwidth); - peer -> outgoingBandwidth = ENET_NET_TO_HOST_32 (command -> bandwidthLimit.outgoingBandwidth); - - if (peer -> incomingBandwidth != 0) - ++ host -> bandwidthLimitedPeers; - - if (peer -> incomingBandwidth == 0 && host -> outgoingBandwidth == 0) - peer -> windowSize = ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE; - else - if (peer -> incomingBandwidth == 0 || host -> outgoingBandwidth == 0) - peer -> windowSize = (ENET_MAX (peer -> incomingBandwidth, host -> outgoingBandwidth) / - ENET_PEER_WINDOW_SIZE_SCALE) * ENET_PROTOCOL_MINIMUM_WINDOW_SIZE; - else - peer -> windowSize = (ENET_MIN (peer -> incomingBandwidth, host -> outgoingBandwidth) / - ENET_PEER_WINDOW_SIZE_SCALE) * ENET_PROTOCOL_MINIMUM_WINDOW_SIZE; - - if (peer -> windowSize < ENET_PROTOCOL_MINIMUM_WINDOW_SIZE) - peer -> windowSize = ENET_PROTOCOL_MINIMUM_WINDOW_SIZE; - else - if (peer -> windowSize > ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE) - peer -> windowSize = ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE; - - return 0; -} - -static int -enet_protocol_handle_throttle_configure (ENetHost * host, ENetPeer * peer, const ENetProtocol * command) -{ - if (peer -> state != ENET_PEER_STATE_CONNECTED && peer -> state != ENET_PEER_STATE_DISCONNECT_LATER) - return -1; - - peer -> packetThrottleInterval = ENET_NET_TO_HOST_32 (command -> throttleConfigure.packetThrottleInterval); - peer -> packetThrottleAcceleration = ENET_NET_TO_HOST_32 (command -> throttleConfigure.packetThrottleAcceleration); - peer -> packetThrottleDeceleration = ENET_NET_TO_HOST_32 (command -> throttleConfigure.packetThrottleDeceleration); - - return 0; -} - -static int -enet_protocol_handle_disconnect (ENetHost * host, ENetPeer * peer, const ENetProtocol * command) -{ - if (peer -> state == ENET_PEER_STATE_DISCONNECTED || peer -> state == ENET_PEER_STATE_ZOMBIE || peer -> state == ENET_PEER_STATE_ACKNOWLEDGING_DISCONNECT) - return 0; - - enet_peer_reset_queues (peer); - - if (peer -> state == ENET_PEER_STATE_CONNECTION_SUCCEEDED || peer -> state == ENET_PEER_STATE_DISCONNECTING || peer -> state == ENET_PEER_STATE_CONNECTING) - enet_protocol_dispatch_state (host, peer, ENET_PEER_STATE_ZOMBIE); - else - if (peer -> state != ENET_PEER_STATE_CONNECTED && peer -> state != ENET_PEER_STATE_DISCONNECT_LATER) - { - if (peer -> state == ENET_PEER_STATE_CONNECTION_PENDING) host -> recalculateBandwidthLimits = 1; - - enet_peer_reset (peer); - } - else - if (command -> header.command & ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE) - enet_protocol_change_state (host, peer, ENET_PEER_STATE_ACKNOWLEDGING_DISCONNECT); - else - enet_protocol_dispatch_state (host, peer, ENET_PEER_STATE_ZOMBIE); - - if (peer -> state != ENET_PEER_STATE_DISCONNECTED) - peer -> eventData = ENET_NET_TO_HOST_32 (command -> disconnect.data); - - return 0; -} - -static int -enet_protocol_handle_acknowledge (ENetHost * host, ENetEvent * event, ENetPeer * peer, const ENetProtocol * command) -{ - enet_uint32 roundTripTime, - receivedSentTime, - receivedReliableSequenceNumber; - ENetProtocolCommand commandNumber; - - if (peer -> state == ENET_PEER_STATE_DISCONNECTED || peer -> state == ENET_PEER_STATE_ZOMBIE) - return 0; - - receivedSentTime = ENET_NET_TO_HOST_16 (command -> acknowledge.receivedSentTime); - receivedSentTime |= host -> serviceTime & 0xFFFF0000; - if ((receivedSentTime & 0x8000) > (host -> serviceTime & 0x8000)) - receivedSentTime -= 0x10000; - - if (ENET_TIME_LESS (host -> serviceTime, receivedSentTime)) - return 0; - - roundTripTime = ENET_TIME_DIFFERENCE (host -> serviceTime, receivedSentTime); - roundTripTime = ENET_MAX (roundTripTime, 1); - - if (peer -> lastReceiveTime > 0) - { - enet_peer_throttle (peer, roundTripTime); - - peer -> roundTripTimeVariance -= peer -> roundTripTimeVariance / 4; - - if (roundTripTime >= peer -> roundTripTime) - { - enet_uint32 diff = roundTripTime - peer -> roundTripTime; - peer -> roundTripTimeVariance += diff / 4; - peer -> roundTripTime += diff / 8; - } - else - { - enet_uint32 diff = peer -> roundTripTime - roundTripTime; - peer -> roundTripTimeVariance += diff / 4; - peer -> roundTripTime -= diff / 8; - } - } - else - { - peer -> roundTripTime = roundTripTime; - peer -> roundTripTimeVariance = (roundTripTime + 1) / 2; - } - - if (peer -> roundTripTime < peer -> lowestRoundTripTime) - peer -> lowestRoundTripTime = peer -> roundTripTime; - - if (peer -> roundTripTimeVariance > peer -> highestRoundTripTimeVariance) - peer -> highestRoundTripTimeVariance = peer -> roundTripTimeVariance; - - if (peer -> packetThrottleEpoch == 0 || - ENET_TIME_DIFFERENCE (host -> serviceTime, peer -> packetThrottleEpoch) >= peer -> packetThrottleInterval) - { - peer -> lastRoundTripTime = peer -> lowestRoundTripTime; - peer -> lastRoundTripTimeVariance = ENET_MAX (peer -> highestRoundTripTimeVariance, 1); - peer -> lowestRoundTripTime = peer -> roundTripTime; - peer -> highestRoundTripTimeVariance = peer -> roundTripTimeVariance; - peer -> packetThrottleEpoch = host -> serviceTime; - } - - peer -> lastReceiveTime = ENET_MAX (host -> serviceTime, 1); - peer -> earliestTimeout = 0; - - receivedReliableSequenceNumber = ENET_NET_TO_HOST_16 (command -> acknowledge.receivedReliableSequenceNumber); - - commandNumber = enet_protocol_remove_sent_reliable_command (peer, receivedReliableSequenceNumber, command -> header.channelID); - - switch (peer -> state) - { - case ENET_PEER_STATE_ACKNOWLEDGING_CONNECT: - if (commandNumber != ENET_PROTOCOL_COMMAND_VERIFY_CONNECT) - return -1; - - enet_protocol_notify_connect (host, peer, event); - break; - - case ENET_PEER_STATE_DISCONNECTING: - if (commandNumber != ENET_PROTOCOL_COMMAND_DISCONNECT) - return -1; - - enet_protocol_notify_disconnect (host, peer, event); - break; - - case ENET_PEER_STATE_DISCONNECT_LATER: - if (enet_list_empty (& peer -> outgoingCommands) && - enet_list_empty (& peer -> sentReliableCommands)) - enet_peer_disconnect (peer, peer -> eventData); - break; - - default: - break; - } - - return 0; -} - -static int -enet_protocol_handle_verify_connect (ENetHost * host, ENetEvent * event, ENetPeer * peer, const ENetProtocol * command) -{ - enet_uint32 mtu, windowSize; - size_t channelCount; - - if (peer -> state != ENET_PEER_STATE_CONNECTING) - return 0; - - channelCount = ENET_NET_TO_HOST_32 (command -> verifyConnect.channelCount); - - if (channelCount < ENET_PROTOCOL_MINIMUM_CHANNEL_COUNT || channelCount > ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT || - ENET_NET_TO_HOST_32 (command -> verifyConnect.packetThrottleInterval) != peer -> packetThrottleInterval || - ENET_NET_TO_HOST_32 (command -> verifyConnect.packetThrottleAcceleration) != peer -> packetThrottleAcceleration || - ENET_NET_TO_HOST_32 (command -> verifyConnect.packetThrottleDeceleration) != peer -> packetThrottleDeceleration || - command -> verifyConnect.connectID != peer -> connectID) - { - peer -> eventData = 0; - - enet_protocol_dispatch_state (host, peer, ENET_PEER_STATE_ZOMBIE); - - return -1; - } - - enet_protocol_remove_sent_reliable_command (peer, 1, 0xFF); - - if (channelCount < peer -> channelCount) - peer -> channelCount = channelCount; - - peer -> outgoingPeerID = ENET_NET_TO_HOST_16 (command -> verifyConnect.outgoingPeerID); - peer -> incomingSessionID = command -> verifyConnect.incomingSessionID; - peer -> outgoingSessionID = command -> verifyConnect.outgoingSessionID; - - mtu = ENET_NET_TO_HOST_32 (command -> verifyConnect.mtu); - - if (mtu < ENET_PROTOCOL_MINIMUM_MTU) - mtu = ENET_PROTOCOL_MINIMUM_MTU; - else - if (mtu > ENET_PROTOCOL_MAXIMUM_MTU) - mtu = ENET_PROTOCOL_MAXIMUM_MTU; - - if (mtu < peer -> mtu) - peer -> mtu = mtu; - - windowSize = ENET_NET_TO_HOST_32 (command -> verifyConnect.windowSize); - - if (windowSize < ENET_PROTOCOL_MINIMUM_WINDOW_SIZE) - windowSize = ENET_PROTOCOL_MINIMUM_WINDOW_SIZE; - - if (windowSize > ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE) - windowSize = ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE; - - if (windowSize < peer -> windowSize) - peer -> windowSize = windowSize; - - peer -> incomingBandwidth = ENET_NET_TO_HOST_32 (command -> verifyConnect.incomingBandwidth); - peer -> outgoingBandwidth = ENET_NET_TO_HOST_32 (command -> verifyConnect.outgoingBandwidth); - - enet_protocol_notify_connect (host, peer, event); - return 0; -} - -static int -enet_protocol_handle_incoming_commands (ENetHost * host, ENetEvent * event) -{ - ENetProtocolHeader * header; - ENetProtocol * command; - ENetPeer * peer; - enet_uint8 * currentData; - size_t headerSize; - enet_uint16 peerID, flags; - enet_uint8 sessionID; - - if (host -> receivedDataLength < (size_t) & ((ENetProtocolHeader *) 0) -> sentTime) - return 0; - - header = (ENetProtocolHeader *) host -> receivedData; - - peerID = ENET_NET_TO_HOST_16 (header -> peerID); - sessionID = (peerID & ENET_PROTOCOL_HEADER_SESSION_MASK) >> ENET_PROTOCOL_HEADER_SESSION_SHIFT; - flags = peerID & ENET_PROTOCOL_HEADER_FLAG_MASK; - peerID &= ~ (ENET_PROTOCOL_HEADER_FLAG_MASK | ENET_PROTOCOL_HEADER_SESSION_MASK); - - headerSize = (flags & ENET_PROTOCOL_HEADER_FLAG_SENT_TIME ? sizeof (ENetProtocolHeader) : (size_t) & ((ENetProtocolHeader *) 0) -> sentTime); - if (host -> checksum != NULL) - headerSize += sizeof (enet_uint32); - - if (peerID == ENET_PROTOCOL_MAXIMUM_PEER_ID) - peer = NULL; - else - if (peerID >= host -> peerCount) - return 0; - else - { - peer = & host -> peers [peerID]; - - if (peer -> state == ENET_PEER_STATE_DISCONNECTED || - peer -> state == ENET_PEER_STATE_ZOMBIE || - ((host -> receivedAddress.host != peer -> address.host || - host -> receivedAddress.port != peer -> address.port) && - peer -> address.host != ENET_HOST_BROADCAST) || - (peer -> outgoingPeerID < ENET_PROTOCOL_MAXIMUM_PEER_ID && - sessionID != peer -> incomingSessionID)) - return 0; - } - - if (flags & ENET_PROTOCOL_HEADER_FLAG_COMPRESSED) - { - size_t originalSize; - if (host -> compressor.context == NULL || host -> compressor.decompress == NULL) - return 0; - - originalSize = host -> compressor.decompress (host -> compressor.context, - host -> receivedData + headerSize, - host -> receivedDataLength - headerSize, - host -> packetData [1] + headerSize, - sizeof (host -> packetData [1]) - headerSize); - if (originalSize <= 0 || originalSize > sizeof (host -> packetData [1]) - headerSize) - return 0; - - memcpy (host -> packetData [1], header, headerSize); - host -> receivedData = host -> packetData [1]; - host -> receivedDataLength = headerSize + originalSize; - } - - if (host -> checksum != NULL) - { - enet_uint32 * checksum = (enet_uint32 *) & host -> receivedData [headerSize - sizeof (enet_uint32)], - desiredChecksum = * checksum; - ENetBuffer buffer; - - * checksum = peer != NULL ? peer -> connectID : 0; - - buffer.data = host -> receivedData; - buffer.dataLength = host -> receivedDataLength; - - if (host -> checksum (& buffer, 1) != desiredChecksum) - return 0; - } - - if (peer != NULL) - { - peer -> address.host = host -> receivedAddress.host; - peer -> address.port = host -> receivedAddress.port; - peer -> incomingDataTotal += host -> receivedDataLength; - } - - currentData = host -> receivedData + headerSize; - - while (currentData < & host -> receivedData [host -> receivedDataLength]) - { - enet_uint8 commandNumber; - size_t commandSize; - - command = (ENetProtocol *) currentData; - - if (currentData + sizeof (ENetProtocolCommandHeader) > & host -> receivedData [host -> receivedDataLength]) - break; - - commandNumber = command -> header.command & ENET_PROTOCOL_COMMAND_MASK; - if (commandNumber >= ENET_PROTOCOL_COMMAND_COUNT) - break; - - commandSize = commandSizes [commandNumber]; - if (commandSize == 0 || currentData + commandSize > & host -> receivedData [host -> receivedDataLength]) - break; - - currentData += commandSize; - - if (peer == NULL && commandNumber != ENET_PROTOCOL_COMMAND_CONNECT) - break; - - command -> header.reliableSequenceNumber = ENET_NET_TO_HOST_16 (command -> header.reliableSequenceNumber); - - switch (commandNumber) - { - case ENET_PROTOCOL_COMMAND_ACKNOWLEDGE: - if (enet_protocol_handle_acknowledge (host, event, peer, command)) - goto commandError; - break; - - case ENET_PROTOCOL_COMMAND_CONNECT: - if (peer != NULL) - goto commandError; - peer = enet_protocol_handle_connect (host, header, command); - if (peer == NULL) - goto commandError; - break; - - case ENET_PROTOCOL_COMMAND_VERIFY_CONNECT: - if (enet_protocol_handle_verify_connect (host, event, peer, command)) - goto commandError; - break; - - case ENET_PROTOCOL_COMMAND_DISCONNECT: - if (enet_protocol_handle_disconnect (host, peer, command)) - goto commandError; - break; - - case ENET_PROTOCOL_COMMAND_PING: - if (enet_protocol_handle_ping (host, peer, command)) - goto commandError; - break; - - case ENET_PROTOCOL_COMMAND_SEND_RELIABLE: - if (enet_protocol_handle_send_reliable (host, peer, command, & currentData)) - goto commandError; - break; - - case ENET_PROTOCOL_COMMAND_SEND_UNRELIABLE: - if (enet_protocol_handle_send_unreliable (host, peer, command, & currentData)) - goto commandError; - break; - - case ENET_PROTOCOL_COMMAND_SEND_UNSEQUENCED: - if (enet_protocol_handle_send_unsequenced (host, peer, command, & currentData)) - goto commandError; - break; - - case ENET_PROTOCOL_COMMAND_SEND_FRAGMENT: - if (enet_protocol_handle_send_fragment (host, peer, command, & currentData)) - goto commandError; - break; - - case ENET_PROTOCOL_COMMAND_BANDWIDTH_LIMIT: - if (enet_protocol_handle_bandwidth_limit (host, peer, command)) - goto commandError; - break; - - case ENET_PROTOCOL_COMMAND_THROTTLE_CONFIGURE: - if (enet_protocol_handle_throttle_configure (host, peer, command)) - goto commandError; - break; - - case ENET_PROTOCOL_COMMAND_SEND_UNRELIABLE_FRAGMENT: - if (enet_protocol_handle_send_unreliable_fragment (host, peer, command, & currentData)) - goto commandError; - break; - - default: - goto commandError; - } - - if (peer != NULL && - (command -> header.command & ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE) != 0) - { - enet_uint16 sentTime; - - if (! (flags & ENET_PROTOCOL_HEADER_FLAG_SENT_TIME)) - break; - - sentTime = ENET_NET_TO_HOST_16 (header -> sentTime); - - switch (peer -> state) - { - case ENET_PEER_STATE_DISCONNECTING: - case ENET_PEER_STATE_ACKNOWLEDGING_CONNECT: - case ENET_PEER_STATE_DISCONNECTED: - case ENET_PEER_STATE_ZOMBIE: - break; - - case ENET_PEER_STATE_ACKNOWLEDGING_DISCONNECT: - if ((command -> header.command & ENET_PROTOCOL_COMMAND_MASK) == ENET_PROTOCOL_COMMAND_DISCONNECT) - enet_peer_queue_acknowledgement (peer, command, sentTime); - break; - - default: - enet_peer_queue_acknowledgement (peer, command, sentTime); - break; - } - } - } - -commandError: - if (event != NULL && event -> type != ENET_EVENT_TYPE_NONE) - return 1; - - return 0; -} - -static int -enet_protocol_receive_incoming_commands (ENetHost * host, ENetEvent * event) -{ - int packets; - - for (packets = 0; packets < 256; ++ packets) - { - int receivedLength; - ENetBuffer buffer; - - buffer.data = host -> packetData [0]; - buffer.dataLength = sizeof (host -> packetData [0]); - - receivedLength = enet_socket_receive (host -> socket, - & host -> receivedAddress, - & buffer, - 1); - - if (receivedLength < 0) - return -1; - - if (receivedLength == 0) - return 0; - - host -> receivedData = host -> packetData [0]; - host -> receivedDataLength = receivedLength; - - host -> totalReceivedData += receivedLength; - host -> totalReceivedPackets ++; - - if (host -> intercept != NULL) - { - switch (host -> intercept (host, event)) - { - case 1: - if (event != NULL && event -> type != ENET_EVENT_TYPE_NONE) - return 1; - - continue; - - case -1: - return -1; - - default: - break; - } - } - - switch (enet_protocol_handle_incoming_commands (host, event)) - { - case 1: - return 1; - - case -1: - return -1; - - default: - break; - } - } - - return 0; -} - -static void -enet_protocol_send_acknowledgements (ENetHost * host, ENetPeer * peer) -{ - ENetProtocol * command = & host -> commands [host -> commandCount]; - ENetBuffer * buffer = & host -> buffers [host -> bufferCount]; - ENetAcknowledgement * acknowledgement; - ENetListIterator currentAcknowledgement; - enet_uint16 reliableSequenceNumber; - - currentAcknowledgement = enet_list_begin (& peer -> acknowledgements); - - while (currentAcknowledgement != enet_list_end (& peer -> acknowledgements)) - { - if (command >= & host -> commands [sizeof (host -> commands) / sizeof (ENetProtocol)] || - buffer >= & host -> buffers [sizeof (host -> buffers) / sizeof (ENetBuffer)] || - peer -> mtu - host -> packetSize < sizeof (ENetProtocolAcknowledge)) - { - host -> continueSending = 1; - - break; - } - - acknowledgement = (ENetAcknowledgement *) currentAcknowledgement; - - currentAcknowledgement = enet_list_next (currentAcknowledgement); - - buffer -> data = command; - buffer -> dataLength = sizeof (ENetProtocolAcknowledge); - - host -> packetSize += buffer -> dataLength; - - reliableSequenceNumber = ENET_HOST_TO_NET_16 (acknowledgement -> command.header.reliableSequenceNumber); - - command -> header.command = ENET_PROTOCOL_COMMAND_ACKNOWLEDGE; - command -> header.channelID = acknowledgement -> command.header.channelID; - command -> header.reliableSequenceNumber = reliableSequenceNumber; - command -> acknowledge.receivedReliableSequenceNumber = reliableSequenceNumber; - command -> acknowledge.receivedSentTime = ENET_HOST_TO_NET_16 (acknowledgement -> sentTime); - - if ((acknowledgement -> command.header.command & ENET_PROTOCOL_COMMAND_MASK) == ENET_PROTOCOL_COMMAND_DISCONNECT) - enet_protocol_dispatch_state (host, peer, ENET_PEER_STATE_ZOMBIE); - - enet_list_remove (& acknowledgement -> acknowledgementList); - enet_free (acknowledgement); - - ++ command; - ++ buffer; - } - - host -> commandCount = command - host -> commands; - host -> bufferCount = buffer - host -> buffers; -} - -static int -enet_protocol_check_timeouts (ENetHost * host, ENetPeer * peer, ENetEvent * event) -{ - ENetOutgoingCommand * outgoingCommand; - ENetListIterator currentCommand, insertPosition; - - currentCommand = enet_list_begin (& peer -> sentReliableCommands); - insertPosition = enet_list_begin (& peer -> outgoingCommands); - - while (currentCommand != enet_list_end (& peer -> sentReliableCommands)) - { - outgoingCommand = (ENetOutgoingCommand *) currentCommand; - - currentCommand = enet_list_next (currentCommand); - - if (ENET_TIME_DIFFERENCE (host -> serviceTime, outgoingCommand -> sentTime) < outgoingCommand -> roundTripTimeout) - continue; - - if (peer -> earliestTimeout == 0 || - ENET_TIME_LESS (outgoingCommand -> sentTime, peer -> earliestTimeout)) - peer -> earliestTimeout = outgoingCommand -> sentTime; - - if (peer -> earliestTimeout != 0 && - (ENET_TIME_DIFFERENCE (host -> serviceTime, peer -> earliestTimeout) >= peer -> timeoutMaximum || - (outgoingCommand -> roundTripTimeout >= outgoingCommand -> roundTripTimeoutLimit && - ENET_TIME_DIFFERENCE (host -> serviceTime, peer -> earliestTimeout) >= peer -> timeoutMinimum))) - { - enet_protocol_notify_disconnect (host, peer, event); - - return 1; - } - - if (outgoingCommand -> packet != NULL) - peer -> reliableDataInTransit -= outgoingCommand -> fragmentLength; - - ++ peer -> packetsLost; - - outgoingCommand -> roundTripTimeout *= 2; - - enet_list_insert (insertPosition, enet_list_remove (& outgoingCommand -> outgoingCommandList)); - - if (currentCommand == enet_list_begin (& peer -> sentReliableCommands) && - ! enet_list_empty (& peer -> sentReliableCommands)) - { - outgoingCommand = (ENetOutgoingCommand *) currentCommand; - - peer -> nextTimeout = outgoingCommand -> sentTime + outgoingCommand -> roundTripTimeout; - } - } - - return 0; -} - -static int -enet_protocol_check_outgoing_commands (ENetHost * host, ENetPeer * peer) -{ - ENetProtocol * command = & host -> commands [host -> commandCount]; - ENetBuffer * buffer = & host -> buffers [host -> bufferCount]; - ENetOutgoingCommand * outgoingCommand; - ENetListIterator currentCommand; - ENetChannel *channel; - enet_uint16 reliableWindow; - size_t commandSize; - int windowExceeded = 0, windowWrap = 0, canPing = 1; - - currentCommand = enet_list_begin (& peer -> outgoingCommands); - - while (currentCommand != enet_list_end (& peer -> outgoingCommands)) - { - outgoingCommand = (ENetOutgoingCommand *) currentCommand; - - if (outgoingCommand -> command.header.command & ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE) - { - channel = outgoingCommand -> command.header.channelID < peer -> channelCount ? & peer -> channels [outgoingCommand -> command.header.channelID] : NULL; - reliableWindow = outgoingCommand -> reliableSequenceNumber / ENET_PEER_RELIABLE_WINDOW_SIZE; - if (channel != NULL) - { - if (! windowWrap && - outgoingCommand -> sendAttempts < 1 && - ! (outgoingCommand -> reliableSequenceNumber % ENET_PEER_RELIABLE_WINDOW_SIZE) && - (channel -> reliableWindows [(reliableWindow + ENET_PEER_RELIABLE_WINDOWS - 1) % ENET_PEER_RELIABLE_WINDOWS] >= ENET_PEER_RELIABLE_WINDOW_SIZE || - channel -> usedReliableWindows & ((((1 << ENET_PEER_FREE_RELIABLE_WINDOWS) - 1) << reliableWindow) | - (((1 << ENET_PEER_FREE_RELIABLE_WINDOWS) - 1) >> (ENET_PEER_RELIABLE_WINDOWS - reliableWindow))))) - windowWrap = 1; - if (windowWrap) - { - currentCommand = enet_list_next (currentCommand); - - continue; - } - } - - if (outgoingCommand -> packet != NULL) - { - if (! windowExceeded) - { - enet_uint32 windowSize = (peer -> packetThrottle * peer -> windowSize) / ENET_PEER_PACKET_THROTTLE_SCALE; - - if (peer -> reliableDataInTransit + outgoingCommand -> fragmentLength > ENET_MAX (windowSize, peer -> mtu)) - windowExceeded = 1; - } - if (windowExceeded) - { - currentCommand = enet_list_next (currentCommand); - - continue; - } - } - - canPing = 0; - } - - commandSize = commandSizes [outgoingCommand -> command.header.command & ENET_PROTOCOL_COMMAND_MASK]; - if (command >= & host -> commands [sizeof (host -> commands) / sizeof (ENetProtocol)] || - buffer + 1 >= & host -> buffers [sizeof (host -> buffers) / sizeof (ENetBuffer)] || - peer -> mtu - host -> packetSize < commandSize || - (outgoingCommand -> packet != NULL && - (enet_uint16) (peer -> mtu - host -> packetSize) < (enet_uint16) (commandSize + outgoingCommand -> fragmentLength))) - { - host -> continueSending = 1; - - break; - } - - currentCommand = enet_list_next (currentCommand); - - if (outgoingCommand -> command.header.command & ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE) - { - if (channel != NULL && outgoingCommand -> sendAttempts < 1) - { - channel -> usedReliableWindows |= 1 << reliableWindow; - ++ channel -> reliableWindows [reliableWindow]; - } - - ++ outgoingCommand -> sendAttempts; - - if (outgoingCommand -> roundTripTimeout == 0) - { - outgoingCommand -> roundTripTimeout = peer -> roundTripTime + 4 * peer -> roundTripTimeVariance; - outgoingCommand -> roundTripTimeoutLimit = peer -> timeoutLimit * outgoingCommand -> roundTripTimeout; - } - - if (enet_list_empty (& peer -> sentReliableCommands)) - peer -> nextTimeout = host -> serviceTime + outgoingCommand -> roundTripTimeout; - - enet_list_insert (enet_list_end (& peer -> sentReliableCommands), - enet_list_remove (& outgoingCommand -> outgoingCommandList)); - - outgoingCommand -> sentTime = host -> serviceTime; - - host -> headerFlags |= ENET_PROTOCOL_HEADER_FLAG_SENT_TIME; - - peer -> reliableDataInTransit += outgoingCommand -> fragmentLength; - } - else - { - if (outgoingCommand -> packet != NULL && outgoingCommand -> fragmentOffset == 0) - { - peer -> packetThrottleCounter += ENET_PEER_PACKET_THROTTLE_COUNTER; - peer -> packetThrottleCounter %= ENET_PEER_PACKET_THROTTLE_SCALE; - - if (peer -> packetThrottleCounter > peer -> packetThrottle) - { - enet_uint16 reliableSequenceNumber = outgoingCommand -> reliableSequenceNumber, - unreliableSequenceNumber = outgoingCommand -> unreliableSequenceNumber; - for (;;) - { - -- outgoingCommand -> packet -> referenceCount; - - if (outgoingCommand -> packet -> referenceCount == 0) - enet_packet_destroy (outgoingCommand -> packet); - - enet_list_remove (& outgoingCommand -> outgoingCommandList); - enet_free (outgoingCommand); - - if (currentCommand == enet_list_end (& peer -> outgoingCommands)) - break; - - outgoingCommand = (ENetOutgoingCommand *) currentCommand; - if (outgoingCommand -> reliableSequenceNumber != reliableSequenceNumber || - outgoingCommand -> unreliableSequenceNumber != unreliableSequenceNumber) - break; - - currentCommand = enet_list_next (currentCommand); - } - - continue; - } - } - - enet_list_remove (& outgoingCommand -> outgoingCommandList); - - if (outgoingCommand -> packet != NULL) - enet_list_insert (enet_list_end (& peer -> sentUnreliableCommands), outgoingCommand); - } - - buffer -> data = command; - buffer -> dataLength = commandSize; - - host -> packetSize += buffer -> dataLength; - - * command = outgoingCommand -> command; - - if (outgoingCommand -> packet != NULL) - { - ++ buffer; - - buffer -> data = outgoingCommand -> packet -> data + outgoingCommand -> fragmentOffset; - buffer -> dataLength = outgoingCommand -> fragmentLength; - - host -> packetSize += outgoingCommand -> fragmentLength; - } - else - if (! (outgoingCommand -> command.header.command & ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE)) - enet_free (outgoingCommand); - - ++ peer -> packetsSent; - - ++ command; - ++ buffer; - } - - host -> commandCount = command - host -> commands; - host -> bufferCount = buffer - host -> buffers; - - if (peer -> state == ENET_PEER_STATE_DISCONNECT_LATER && - enet_list_empty (& peer -> outgoingCommands) && - enet_list_empty (& peer -> sentReliableCommands) && - enet_list_empty (& peer -> sentUnreliableCommands)) - enet_peer_disconnect (peer, peer -> eventData); - - return canPing; -} - -static int -enet_protocol_send_outgoing_commands (ENetHost * host, ENetEvent * event, int checkForTimeouts) -{ - enet_uint8 headerData [sizeof (ENetProtocolHeader) + sizeof (enet_uint32)]; - ENetProtocolHeader * header = (ENetProtocolHeader *) headerData; - ENetPeer * currentPeer; - int sentLength; - size_t shouldCompress = 0; - - host -> continueSending = 1; - - while (host -> continueSending) - for (host -> continueSending = 0, - currentPeer = host -> peers; - currentPeer < & host -> peers [host -> peerCount]; - ++ currentPeer) - { - if (currentPeer -> state == ENET_PEER_STATE_DISCONNECTED || - currentPeer -> state == ENET_PEER_STATE_ZOMBIE) - continue; - - host -> headerFlags = 0; - host -> commandCount = 0; - host -> bufferCount = 1; - host -> packetSize = sizeof (ENetProtocolHeader); - - if (! enet_list_empty (& currentPeer -> acknowledgements)) - enet_protocol_send_acknowledgements (host, currentPeer); - - if (checkForTimeouts != 0 && - ! enet_list_empty (& currentPeer -> sentReliableCommands) && - ENET_TIME_GREATER_EQUAL (host -> serviceTime, currentPeer -> nextTimeout) && - enet_protocol_check_timeouts (host, currentPeer, event) == 1) - { - if (event != NULL && event -> type != ENET_EVENT_TYPE_NONE) - return 1; - else - continue; - } - - if ((enet_list_empty (& currentPeer -> outgoingCommands) || - enet_protocol_check_outgoing_commands (host, currentPeer)) && - enet_list_empty (& currentPeer -> sentReliableCommands) && - ENET_TIME_DIFFERENCE (host -> serviceTime, currentPeer -> lastReceiveTime) >= currentPeer -> pingInterval && - currentPeer -> mtu - host -> packetSize >= sizeof (ENetProtocolPing)) - { - enet_peer_ping (currentPeer); - enet_protocol_check_outgoing_commands (host, currentPeer); - } - - if (host -> commandCount == 0) - continue; - - if (currentPeer -> packetLossEpoch == 0) - currentPeer -> packetLossEpoch = host -> serviceTime; - else - if (ENET_TIME_DIFFERENCE (host -> serviceTime, currentPeer -> packetLossEpoch) >= ENET_PEER_PACKET_LOSS_INTERVAL && - currentPeer -> packetsSent > 0) - { - enet_uint32 packetLoss = currentPeer -> packetsLost * ENET_PEER_PACKET_LOSS_SCALE / currentPeer -> packetsSent; - -#ifdef ENET_DEBUG - printf ("peer %u: %f%%+-%f%% packet loss, %u+-%u ms round trip time, %f%% throttle, %u outgoing, %u/%u incoming\n", currentPeer -> incomingPeerID, currentPeer -> packetLoss / (float) ENET_PEER_PACKET_LOSS_SCALE, currentPeer -> packetLossVariance / (float) ENET_PEER_PACKET_LOSS_SCALE, currentPeer -> roundTripTime, currentPeer -> roundTripTimeVariance, currentPeer -> packetThrottle / (float) ENET_PEER_PACKET_THROTTLE_SCALE, enet_list_size (& currentPeer -> outgoingCommands), currentPeer -> channels != NULL ? enet_list_size (& currentPeer -> channels -> incomingReliableCommands) : 0, currentPeer -> channels != NULL ? enet_list_size (& currentPeer -> channels -> incomingUnreliableCommands) : 0); -#endif - - currentPeer -> packetLossVariance = (currentPeer -> packetLossVariance * 3 + ENET_DIFFERENCE (packetLoss, currentPeer -> packetLoss)) / 4; - currentPeer -> packetLoss = (currentPeer -> packetLoss * 7 + packetLoss) / 8; - - currentPeer -> packetLossEpoch = host -> serviceTime; - currentPeer -> packetsSent = 0; - currentPeer -> packetsLost = 0; - } - - host -> buffers -> data = headerData; - if (host -> headerFlags & ENET_PROTOCOL_HEADER_FLAG_SENT_TIME) - { - header -> sentTime = ENET_HOST_TO_NET_16 (host -> serviceTime & 0xFFFF); - - host -> buffers -> dataLength = sizeof (ENetProtocolHeader); - } - else - host -> buffers -> dataLength = (size_t) & ((ENetProtocolHeader *) 0) -> sentTime; - - shouldCompress = 0; - if (host -> compressor.context != NULL && host -> compressor.compress != NULL) - { - size_t originalSize = host -> packetSize - sizeof(ENetProtocolHeader), - compressedSize = host -> compressor.compress (host -> compressor.context, - & host -> buffers [1], host -> bufferCount - 1, - originalSize, - host -> packetData [1], - originalSize); - if (compressedSize > 0 && compressedSize < originalSize) - { - host -> headerFlags |= ENET_PROTOCOL_HEADER_FLAG_COMPRESSED; - shouldCompress = compressedSize; -#ifdef ENET_DEBUG_COMPRESS - printf ("peer %u: compressed %u -> %u (%u%%)\n", currentPeer -> incomingPeerID, originalSize, compressedSize, (compressedSize * 100) / originalSize); -#endif - } - } - - if (currentPeer -> outgoingPeerID < ENET_PROTOCOL_MAXIMUM_PEER_ID) - host -> headerFlags |= currentPeer -> outgoingSessionID << ENET_PROTOCOL_HEADER_SESSION_SHIFT; - header -> peerID = ENET_HOST_TO_NET_16 (currentPeer -> outgoingPeerID | host -> headerFlags); - if (host -> checksum != NULL) - { - enet_uint32 * checksum = (enet_uint32 *) & headerData [host -> buffers -> dataLength]; - * checksum = currentPeer -> outgoingPeerID < ENET_PROTOCOL_MAXIMUM_PEER_ID ? currentPeer -> connectID : 0; - host -> buffers -> dataLength += sizeof (enet_uint32); - * checksum = host -> checksum (host -> buffers, host -> bufferCount); - } - - if (shouldCompress > 0) - { - host -> buffers [1].data = host -> packetData [1]; - host -> buffers [1].dataLength = shouldCompress; - host -> bufferCount = 2; - } - - currentPeer -> lastSendTime = host -> serviceTime; - - sentLength = enet_socket_send (host -> socket, & currentPeer -> address, host -> buffers, host -> bufferCount); - - enet_protocol_remove_sent_unreliable_commands (currentPeer); - - if (sentLength < 0) - return -1; - - host -> totalSentData += sentLength; - host -> totalSentPackets ++; - } - - return 0; -} - -/** Sends any queued packets on the host specified to its designated peers. - - @param host host to flush - @remarks this function need only be used in circumstances where one wishes to send queued packets earlier than in a call to enet_host_service(). - @ingroup host -*/ -void -enet_host_flush (ENetHost * host) -{ - host -> serviceTime = enet_time_get (); - - enet_protocol_send_outgoing_commands (host, NULL, 0); -} - -/** Checks for any queued events on the host and dispatches one if available. - - @param host host to check for events - @param event an event structure where event details will be placed if available - @retval > 0 if an event was dispatched - @retval 0 if no events are available - @retval < 0 on failure - @ingroup host -*/ -int -enet_host_check_events (ENetHost * host, ENetEvent * event) -{ - if (event == NULL) return -1; - - event -> type = ENET_EVENT_TYPE_NONE; - event -> peer = NULL; - event -> packet = NULL; - - return enet_protocol_dispatch_incoming_commands (host, event); -} - -/** Waits for events on the host specified and shuttles packets between - the host and its peers. - - @param host host to service - @param event an event structure where event details will be placed if one occurs - if event == NULL then no events will be delivered - @param timeout number of milliseconds that ENet should wait for events - @retval > 0 if an event occurred within the specified time limit - @retval 0 if no event occurred - @retval < 0 on failure - @remarks enet_host_service should be called fairly regularly for adequate performance - @ingroup host -*/ -int -enet_host_service (ENetHost * host, ENetEvent * event, enet_uint32 timeout) -{ - enet_uint32 waitCondition; - - if (event != NULL) - { - event -> type = ENET_EVENT_TYPE_NONE; - event -> peer = NULL; - event -> packet = NULL; - - switch (enet_protocol_dispatch_incoming_commands (host, event)) - { - case 1: - return 1; - - case -1: -#ifdef ENET_DEBUG - perror ("Error dispatching incoming packets"); -#endif - - return -1; - - default: - break; - } - } - - host -> serviceTime = enet_time_get (); - - timeout += host -> serviceTime; - - do - { - if (ENET_TIME_DIFFERENCE (host -> serviceTime, host -> bandwidthThrottleEpoch) >= ENET_HOST_BANDWIDTH_THROTTLE_INTERVAL) - enet_host_bandwidth_throttle (host); - - switch (enet_protocol_send_outgoing_commands (host, event, 1)) - { - case 1: - return 1; - - case -1: -#ifdef ENET_DEBUG - perror ("Error sending outgoing packets"); -#endif - - return -1; - - default: - break; - } - - switch (enet_protocol_receive_incoming_commands (host, event)) - { - case 1: - return 1; - - case -1: -#ifdef ENET_DEBUG - perror ("Error receiving incoming packets"); -#endif - - return -1; - - default: - break; - } - - switch (enet_protocol_send_outgoing_commands (host, event, 1)) - { - case 1: - return 1; - - case -1: -#ifdef ENET_DEBUG - perror ("Error sending outgoing packets"); -#endif - - return -1; - - default: - break; - } - - if (event != NULL) - { - switch (enet_protocol_dispatch_incoming_commands (host, event)) - { - case 1: - return 1; - - case -1: -#ifdef ENET_DEBUG - perror ("Error dispatching incoming packets"); -#endif - - return -1; - - default: - break; - } - } - - if (ENET_TIME_GREATER_EQUAL (host -> serviceTime, timeout)) - return 0; - - do - { - host -> serviceTime = enet_time_get (); - - if (ENET_TIME_GREATER_EQUAL (host -> serviceTime, timeout)) - return 0; - - waitCondition = ENET_SOCKET_WAIT_RECEIVE | ENET_SOCKET_WAIT_INTERRUPT; - - if (enet_socket_wait (host -> socket, & waitCondition, ENET_TIME_DIFFERENCE (timeout, host -> serviceTime)) != 0) - return -1; - } - while (waitCondition & ENET_SOCKET_WAIT_INTERRUPT); - - host -> serviceTime = enet_time_get (); - } while (waitCondition & ENET_SOCKET_WAIT_RECEIVE); - - return 0; -} - diff --git a/include/enet/unix.c b/include/enet/unix.c deleted file mode 100644 index a636f03..0000000 --- a/include/enet/unix.c +++ /dev/null @@ -1,615 +0,0 @@ -/** - @file unix.c - @brief ENet Unix system specific functions -*/ -#ifndef _WIN32 - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define ENET_BUILDING_LIB 1 -#include "enet/enet.h" - -#ifdef __APPLE__ -#ifdef HAS_POLL -#undef HAS_POLL -#endif -#ifndef HAS_FCNTL -#define HAS_FCNTL 1 -#endif -#ifndef HAS_INET_PTON -#define HAS_INET_PTON 1 -#endif -#ifndef HAS_INET_NTOP -#define HAS_INET_NTOP 1 -#endif -#ifndef HAS_MSGHDR_FLAGS -#define HAS_MSGHDR_FLAGS 1 -#endif -#ifndef HAS_SOCKLEN_T -#define HAS_SOCKLEN_T 1 -#endif -#ifndef HAS_GETADDRINFO -#define HAS_GETADDRINFO 1 -#endif -#ifndef HAS_GETNAMEINFO -#define HAS_GETNAMEINFO 1 -#endif -#endif - -#ifdef HAS_FCNTL -#include -#endif - -#ifdef HAS_POLL -#include -#endif - -#ifndef HAS_SOCKLEN_T -typedef int socklen_t; -#endif - -#ifndef MSG_NOSIGNAL -#define MSG_NOSIGNAL 0 -#endif - -static enet_uint32 timeBase = 0; - -int -enet_initialize (void) -{ - return 0; -} - -void -enet_deinitialize (void) -{ -} - -enet_uint32 -enet_host_random_seed (void) -{ - return (enet_uint32) time (NULL); -} - -enet_uint32 -enet_time_get (void) -{ - struct timeval timeVal; - - gettimeofday (& timeVal, NULL); - - return timeVal.tv_sec * 1000 + timeVal.tv_usec / 1000 - timeBase; -} - -void -enet_time_set (enet_uint32 newTimeBase) -{ - struct timeval timeVal; - - gettimeofday (& timeVal, NULL); - - timeBase = timeVal.tv_sec * 1000 + timeVal.tv_usec / 1000 - newTimeBase; -} - -int -enet_address_set_host_ip (ENetAddress * address, const char * name) -{ -#ifdef HAS_INET_PTON - if (! inet_pton (AF_INET, name, & address -> host)) -#else - if (! inet_aton (name, (struct in_addr *) & address -> host)) -#endif - return -1; - - return 0; -} - -int -enet_address_set_host (ENetAddress * address, const char * name) -{ -#ifdef HAS_GETADDRINFO - struct addrinfo hints, * resultList = NULL, * result = NULL; - - memset (& hints, 0, sizeof (hints)); - hints.ai_family = AF_INET; - - if (getaddrinfo (name, NULL, NULL, & resultList) != 0) - return -1; - - for (result = resultList; result != NULL; result = result -> ai_next) - { - if (result -> ai_family == AF_INET && result -> ai_addr != NULL && result -> ai_addrlen >= sizeof (struct sockaddr_in)) - { - struct sockaddr_in * sin = (struct sockaddr_in *) result -> ai_addr; - - address -> host = sin -> sin_addr.s_addr; - - freeaddrinfo (resultList); - - return 0; - } - } - - if (resultList != NULL) - freeaddrinfo (resultList); -#else - struct hostent * hostEntry = NULL; -#ifdef HAS_GETHOSTBYNAME_R - struct hostent hostData; - char buffer [2048]; - int errnum; - -#if defined(linux) || defined(__linux) || defined(__linux__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__) - gethostbyname_r (name, & hostData, buffer, sizeof (buffer), & hostEntry, & errnum); -#else - hostEntry = gethostbyname_r (name, & hostData, buffer, sizeof (buffer), & errnum); -#endif -#else - hostEntry = gethostbyname (name); -#endif - - if (hostEntry != NULL && hostEntry -> h_addrtype == AF_INET) - { - address -> host = * (enet_uint32 *) hostEntry -> h_addr_list [0]; - - return 0; - } -#endif - - return enet_address_set_host_ip (address, name); -} - -int -enet_address_get_host_ip (const ENetAddress * address, char * name, size_t nameLength) -{ -#ifdef HAS_INET_NTOP - if (inet_ntop (AF_INET, & address -> host, name, nameLength) == NULL) -#else - char * addr = inet_ntoa (* (struct in_addr *) & address -> host); - if (addr != NULL) - { - size_t addrLen = strlen(addr); - if (addrLen >= nameLength) - return -1; - memcpy (name, addr, addrLen + 1); - } - else -#endif - return -1; - return 0; -} - -int -enet_address_get_host (const ENetAddress * address, char * name, size_t nameLength) -{ -#ifdef HAS_GETNAMEINFO - struct sockaddr_in sin; - int err; - - memset (& sin, 0, sizeof (struct sockaddr_in)); - - sin.sin_family = AF_INET; - sin.sin_port = ENET_HOST_TO_NET_16 (address -> port); - sin.sin_addr.s_addr = address -> host; - - err = getnameinfo ((struct sockaddr *) & sin, sizeof (sin), name, nameLength, NULL, 0, NI_NAMEREQD); - if (! err) - { - if (name != NULL && nameLength > 0 && ! memchr (name, '\0', nameLength)) - return -1; - return 0; - } - if (err != EAI_NONAME) - return -1; -#else - struct in_addr in; - struct hostent * hostEntry = NULL; -#ifdef HAS_GETHOSTBYADDR_R - struct hostent hostData; - char buffer [2048]; - int errnum; - - in.s_addr = address -> host; - -#if defined(linux) || defined(__linux) || defined(__linux__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__) - gethostbyaddr_r ((char *) & in, sizeof (struct in_addr), AF_INET, & hostData, buffer, sizeof (buffer), & hostEntry, & errnum); -#else - hostEntry = gethostbyaddr_r ((char *) & in, sizeof (struct in_addr), AF_INET, & hostData, buffer, sizeof (buffer), & errnum); -#endif -#else - in.s_addr = address -> host; - - hostEntry = gethostbyaddr ((char *) & in, sizeof (struct in_addr), AF_INET); -#endif - - if (hostEntry != NULL) - { - size_t hostLen = strlen (hostEntry -> h_name); - if (hostLen >= nameLength) - return -1; - memcpy (name, hostEntry -> h_name, hostLen + 1); - return 0; - } -#endif - - return enet_address_get_host_ip (address, name, nameLength); -} - -int -enet_socket_bind (ENetSocket socket, const ENetAddress * address) -{ - struct sockaddr_in sin; - - memset (& sin, 0, sizeof (struct sockaddr_in)); - - sin.sin_family = AF_INET; - - if (address != NULL) - { - sin.sin_port = ENET_HOST_TO_NET_16 (address -> port); - sin.sin_addr.s_addr = address -> host; - } - else - { - sin.sin_port = 0; - sin.sin_addr.s_addr = INADDR_ANY; - } - - return bind (socket, - (struct sockaddr *) & sin, - sizeof (struct sockaddr_in)); -} - -int -enet_socket_get_address (ENetSocket socket, ENetAddress * address) -{ - struct sockaddr_in sin; - socklen_t sinLength = sizeof (struct sockaddr_in); - - if (getsockname (socket, (struct sockaddr *) & sin, & sinLength) == -1) - return -1; - - address -> host = (enet_uint32) sin.sin_addr.s_addr; - address -> port = ENET_NET_TO_HOST_16 (sin.sin_port); - - return 0; -} - -int -enet_socket_listen (ENetSocket socket, int backlog) -{ - return listen (socket, backlog < 0 ? SOMAXCONN : backlog); -} - -ENetSocket -enet_socket_create (ENetSocketType type) -{ - return socket (PF_INET, type == ENET_SOCKET_TYPE_DATAGRAM ? SOCK_DGRAM : SOCK_STREAM, 0); -} - -int -enet_socket_set_option (ENetSocket socket, ENetSocketOption option, int value) -{ - int result = -1; - switch (option) - { - case ENET_SOCKOPT_NONBLOCK: -#ifdef HAS_FCNTL - result = fcntl (socket, F_SETFL, (value ? O_NONBLOCK : 0) | (fcntl (socket, F_GETFL) & ~O_NONBLOCK)); -#else - result = ioctl (socket, FIONBIO, & value); -#endif - break; - - case ENET_SOCKOPT_BROADCAST: - result = setsockopt (socket, SOL_SOCKET, SO_BROADCAST, (char *) & value, sizeof (int)); - break; - - case ENET_SOCKOPT_REUSEADDR: - result = setsockopt (socket, SOL_SOCKET, SO_REUSEADDR, (char *) & value, sizeof (int)); - break; - - case ENET_SOCKOPT_RCVBUF: - result = setsockopt (socket, SOL_SOCKET, SO_RCVBUF, (char *) & value, sizeof (int)); - break; - - case ENET_SOCKOPT_SNDBUF: - result = setsockopt (socket, SOL_SOCKET, SO_SNDBUF, (char *) & value, sizeof (int)); - break; - - case ENET_SOCKOPT_RCVTIMEO: - { - struct timeval timeVal; - timeVal.tv_sec = value / 1000; - timeVal.tv_usec = (value % 1000) * 1000; - result = setsockopt (socket, SOL_SOCKET, SO_RCVTIMEO, (char *) & timeVal, sizeof (struct timeval)); - break; - } - - case ENET_SOCKOPT_SNDTIMEO: - { - struct timeval timeVal; - timeVal.tv_sec = value / 1000; - timeVal.tv_usec = (value % 1000) * 1000; - result = setsockopt (socket, SOL_SOCKET, SO_SNDTIMEO, (char *) & timeVal, sizeof (struct timeval)); - break; - } - - case ENET_SOCKOPT_NODELAY: - result = setsockopt (socket, IPPROTO_TCP, TCP_NODELAY, (char *) & value, sizeof (int)); - break; - - default: - break; - } - return result == -1 ? -1 : 0; -} - -int -enet_socket_get_option (ENetSocket socket, ENetSocketOption option, int * value) -{ - int result = -1; - socklen_t len; - switch (option) - { - case ENET_SOCKOPT_ERROR: - len = sizeof (int); - result = getsockopt (socket, SOL_SOCKET, SO_ERROR, value, & len); - break; - - default: - break; - } - return result == -1 ? -1 : 0; -} - -int -enet_socket_connect (ENetSocket socket, const ENetAddress * address) -{ - struct sockaddr_in sin; - int result; - - memset (& sin, 0, sizeof (struct sockaddr_in)); - - sin.sin_family = AF_INET; - sin.sin_port = ENET_HOST_TO_NET_16 (address -> port); - sin.sin_addr.s_addr = address -> host; - - result = connect (socket, (struct sockaddr *) & sin, sizeof (struct sockaddr_in)); - if (result == -1 && errno == EINPROGRESS) - return 0; - - return result; -} - -ENetSocket -enet_socket_accept (ENetSocket socket, ENetAddress * address) -{ - int result; - struct sockaddr_in sin; - socklen_t sinLength = sizeof (struct sockaddr_in); - - result = accept (socket, - address != NULL ? (struct sockaddr *) & sin : NULL, - address != NULL ? & sinLength : NULL); - - if (result == -1) - return ENET_SOCKET_NULL; - - if (address != NULL) - { - address -> host = (enet_uint32) sin.sin_addr.s_addr; - address -> port = ENET_NET_TO_HOST_16 (sin.sin_port); - } - - return result; -} - -int -enet_socket_shutdown (ENetSocket socket, ENetSocketShutdown how) -{ - return shutdown (socket, (int) how); -} - -void -enet_socket_destroy (ENetSocket socket) -{ - if (socket != -1) - close (socket); -} - -int -enet_socket_send (ENetSocket socket, - const ENetAddress * address, - const ENetBuffer * buffers, - size_t bufferCount) -{ - struct msghdr msgHdr; - struct sockaddr_in sin; - int sentLength; - - memset (& msgHdr, 0, sizeof (struct msghdr)); - - if (address != NULL) - { - memset (& sin, 0, sizeof (struct sockaddr_in)); - - sin.sin_family = AF_INET; - sin.sin_port = ENET_HOST_TO_NET_16 (address -> port); - sin.sin_addr.s_addr = address -> host; - - msgHdr.msg_name = & sin; - msgHdr.msg_namelen = sizeof (struct sockaddr_in); - } - - msgHdr.msg_iov = (struct iovec *) buffers; - msgHdr.msg_iovlen = bufferCount; - - sentLength = sendmsg (socket, & msgHdr, MSG_NOSIGNAL); - - if (sentLength == -1) - { - if (errno == EWOULDBLOCK) - return 0; - - return -1; - } - - return sentLength; -} - -int -enet_socket_receive (ENetSocket socket, - ENetAddress * address, - ENetBuffer * buffers, - size_t bufferCount) -{ - struct msghdr msgHdr; - struct sockaddr_in sin; - int recvLength; - - memset (& msgHdr, 0, sizeof (struct msghdr)); - - if (address != NULL) - { - msgHdr.msg_name = & sin; - msgHdr.msg_namelen = sizeof (struct sockaddr_in); - } - - msgHdr.msg_iov = (struct iovec *) buffers; - msgHdr.msg_iovlen = bufferCount; - - recvLength = recvmsg (socket, & msgHdr, MSG_NOSIGNAL); - - if (recvLength == -1) - { - if (errno == EWOULDBLOCK) - return 0; - - return -1; - } - -#ifdef HAS_MSGHDR_FLAGS - if (msgHdr.msg_flags & MSG_TRUNC) - return -1; -#endif - - if (address != NULL) - { - address -> host = (enet_uint32) sin.sin_addr.s_addr; - address -> port = ENET_NET_TO_HOST_16 (sin.sin_port); - } - - return recvLength; -} - -int -enet_socketset_select (ENetSocket maxSocket, ENetSocketSet * readSet, ENetSocketSet * writeSet, enet_uint32 timeout) -{ - struct timeval timeVal; - - timeVal.tv_sec = timeout / 1000; - timeVal.tv_usec = (timeout % 1000) * 1000; - - return select (maxSocket + 1, readSet, writeSet, NULL, & timeVal); -} - -int -enet_socket_wait (ENetSocket socket, enet_uint32 * condition, enet_uint32 timeout) -{ -#ifdef HAS_POLL - struct pollfd pollSocket; - int pollCount; - - pollSocket.fd = socket; - pollSocket.events = 0; - - if (* condition & ENET_SOCKET_WAIT_SEND) - pollSocket.events |= POLLOUT; - - if (* condition & ENET_SOCKET_WAIT_RECEIVE) - pollSocket.events |= POLLIN; - - pollCount = poll (& pollSocket, 1, timeout); - - if (pollCount < 0) - { - if (errno == EINTR && * condition & ENET_SOCKET_WAIT_INTERRUPT) - { - * condition = ENET_SOCKET_WAIT_INTERRUPT; - - return 0; - } - - return -1; - } - - * condition = ENET_SOCKET_WAIT_NONE; - - if (pollCount == 0) - return 0; - - if (pollSocket.revents & POLLOUT) - * condition |= ENET_SOCKET_WAIT_SEND; - - if (pollSocket.revents & POLLIN) - * condition |= ENET_SOCKET_WAIT_RECEIVE; - - return 0; -#else - fd_set readSet, writeSet; - struct timeval timeVal; - int selectCount; - - timeVal.tv_sec = timeout / 1000; - timeVal.tv_usec = (timeout % 1000) * 1000; - - FD_ZERO (& readSet); - FD_ZERO (& writeSet); - - if (* condition & ENET_SOCKET_WAIT_SEND) - FD_SET (socket, & writeSet); - - if (* condition & ENET_SOCKET_WAIT_RECEIVE) - FD_SET (socket, & readSet); - - selectCount = select (socket + 1, & readSet, & writeSet, NULL, & timeVal); - - if (selectCount < 0) - { - if (errno == EINTR && * condition & ENET_SOCKET_WAIT_INTERRUPT) - { - * condition = ENET_SOCKET_WAIT_INTERRUPT; - - return 0; - } - - return -1; - } - - * condition = ENET_SOCKET_WAIT_NONE; - - if (selectCount == 0) - return 0; - - if (FD_ISSET (socket, & writeSet)) - * condition |= ENET_SOCKET_WAIT_SEND; - - if (FD_ISSET (socket, & readSet)) - * condition |= ENET_SOCKET_WAIT_RECEIVE; - - return 0; -#endif -} - -#endif - diff --git a/include/enet/win32.c b/include/enet/win32.c deleted file mode 100644 index eebdb03..0000000 --- a/include/enet/win32.c +++ /dev/null @@ -1,442 +0,0 @@ -/** - @file win32.c - @brief ENet Win32 system specific functions -*/ -#ifdef _WIN32 - -#define ENET_BUILDING_LIB 1 -#include "enet/enet.h" -#include -#include - -static enet_uint32 timeBase = 0; - -int -enet_initialize (void) -{ - WORD versionRequested = MAKEWORD (1, 1); - WSADATA wsaData; - - if (WSAStartup (versionRequested, & wsaData)) - return -1; - - if (LOBYTE (wsaData.wVersion) != 1|| - HIBYTE (wsaData.wVersion) != 1) - { - WSACleanup (); - - return -1; - } - - timeBeginPeriod (1); - - return 0; -} - -void -enet_deinitialize (void) -{ - timeEndPeriod (1); - - WSACleanup (); -} - -enet_uint32 -enet_host_random_seed (void) -{ - return (enet_uint32) timeGetTime (); -} - -enet_uint32 -enet_time_get (void) -{ - return (enet_uint32) timeGetTime () - timeBase; -} - -void -enet_time_set (enet_uint32 newTimeBase) -{ - timeBase = (enet_uint32) timeGetTime () - newTimeBase; -} - -int -enet_address_set_host_ip (ENetAddress * address, const char * name) -{ - enet_uint8 vals [4] = { 0, 0, 0, 0 }; - int i; - - for (i = 0; i < 4; ++ i) - { - const char * next = name + 1; - if (* name != '0') - { - long val = strtol (name, (char **) & next, 10); - if (val < 0 || val > 255 || next == name || next - name > 3) - return -1; - vals [i] = (enet_uint8) val; - } - - if (* next != (i < 3 ? '.' : '\0')) - return -1; - name = next + 1; - } - - memcpy (& address -> host, vals, sizeof (enet_uint32)); - return 0; -} - -int -enet_address_set_host (ENetAddress * address, const char * name) -{ - struct hostent * hostEntry; - - hostEntry = gethostbyname (name); - if (hostEntry == NULL || - hostEntry -> h_addrtype != AF_INET) - return enet_address_set_host_ip (address, name); - - address -> host = * (enet_uint32 *) hostEntry -> h_addr_list [0]; - - return 0; -} - -int -enet_address_get_host_ip (const ENetAddress * address, char * name, size_t nameLength) -{ - char * addr = inet_ntoa (* (struct in_addr *) & address -> host); - if (addr == NULL) - return -1; - else - { - size_t addrLen = strlen(addr); - if (addrLen >= nameLength) - return -1; - memcpy (name, addr, addrLen + 1); - } - return 0; -} - -int -enet_address_get_host (const ENetAddress * address, char * name, size_t nameLength) -{ - struct in_addr in; - struct hostent * hostEntry; - - in.s_addr = address -> host; - - hostEntry = gethostbyaddr ((char *) & in, sizeof (struct in_addr), AF_INET); - if (hostEntry == NULL) - return enet_address_get_host_ip (address, name, nameLength); - else - { - size_t hostLen = strlen (hostEntry -> h_name); - if (hostLen >= nameLength) - return -1; - memcpy (name, hostEntry -> h_name, hostLen + 1); - } - - return 0; -} - -int -enet_socket_bind (ENetSocket socket, const ENetAddress * address) -{ - struct sockaddr_in sin; - - memset (& sin, 0, sizeof (struct sockaddr_in)); - - sin.sin_family = AF_INET; - - if (address != NULL) - { - sin.sin_port = ENET_HOST_TO_NET_16 (address -> port); - sin.sin_addr.s_addr = address -> host; - } - else - { - sin.sin_port = 0; - sin.sin_addr.s_addr = INADDR_ANY; - } - - return bind (socket, - (struct sockaddr *) & sin, - sizeof (struct sockaddr_in)) == SOCKET_ERROR ? -1 : 0; -} - -int -enet_socket_get_address (ENetSocket socket, ENetAddress * address) -{ - struct sockaddr_in sin; - int sinLength = sizeof (struct sockaddr_in); - - if (getsockname (socket, (struct sockaddr *) & sin, & sinLength) == -1) - return -1; - - address -> host = (enet_uint32) sin.sin_addr.s_addr; - address -> port = ENET_NET_TO_HOST_16 (sin.sin_port); - - return 0; -} - -int -enet_socket_listen (ENetSocket socket, int backlog) -{ - return listen (socket, backlog < 0 ? SOMAXCONN : backlog) == SOCKET_ERROR ? -1 : 0; -} - -ENetSocket -enet_socket_create (ENetSocketType type) -{ - return socket (PF_INET, type == ENET_SOCKET_TYPE_DATAGRAM ? SOCK_DGRAM : SOCK_STREAM, 0); -} - -int -enet_socket_set_option (ENetSocket socket, ENetSocketOption option, int value) -{ - int result = SOCKET_ERROR; - switch (option) - { - case ENET_SOCKOPT_NONBLOCK: - { - u_long nonBlocking = (u_long) value; - result = ioctlsocket (socket, FIONBIO, & nonBlocking); - break; - } - - case ENET_SOCKOPT_BROADCAST: - result = setsockopt (socket, SOL_SOCKET, SO_BROADCAST, (char *) & value, sizeof (int)); - break; - - case ENET_SOCKOPT_REUSEADDR: - result = setsockopt (socket, SOL_SOCKET, SO_REUSEADDR, (char *) & value, sizeof (int)); - break; - - case ENET_SOCKOPT_RCVBUF: - result = setsockopt (socket, SOL_SOCKET, SO_RCVBUF, (char *) & value, sizeof (int)); - break; - - case ENET_SOCKOPT_SNDBUF: - result = setsockopt (socket, SOL_SOCKET, SO_SNDBUF, (char *) & value, sizeof (int)); - break; - - case ENET_SOCKOPT_RCVTIMEO: - result = setsockopt (socket, SOL_SOCKET, SO_RCVTIMEO, (char *) & value, sizeof (int)); - break; - - case ENET_SOCKOPT_SNDTIMEO: - result = setsockopt (socket, SOL_SOCKET, SO_SNDTIMEO, (char *) & value, sizeof (int)); - break; - - case ENET_SOCKOPT_NODELAY: - result = setsockopt (socket, IPPROTO_TCP, TCP_NODELAY, (char *) & value, sizeof (int)); - break; - - default: - break; - } - return result == SOCKET_ERROR ? -1 : 0; -} - -int -enet_socket_get_option (ENetSocket socket, ENetSocketOption option, int * value) -{ - int result = SOCKET_ERROR, len; - switch (option) - { - case ENET_SOCKOPT_ERROR: - len = sizeof(int); - result = getsockopt (socket, SOL_SOCKET, SO_ERROR, (char *) value, & len); - break; - - default: - break; - } - return result == SOCKET_ERROR ? -1 : 0; -} - -int -enet_socket_connect (ENetSocket socket, const ENetAddress * address) -{ - struct sockaddr_in sin; - int result; - - memset (& sin, 0, sizeof (struct sockaddr_in)); - - sin.sin_family = AF_INET; - sin.sin_port = ENET_HOST_TO_NET_16 (address -> port); - sin.sin_addr.s_addr = address -> host; - - result = connect (socket, (struct sockaddr *) & sin, sizeof (struct sockaddr_in)); - if (result == SOCKET_ERROR && WSAGetLastError () != WSAEWOULDBLOCK) - return -1; - - return 0; -} - -ENetSocket -enet_socket_accept (ENetSocket socket, ENetAddress * address) -{ - SOCKET result; - struct sockaddr_in sin; - int sinLength = sizeof (struct sockaddr_in); - - result = accept (socket, - address != NULL ? (struct sockaddr *) & sin : NULL, - address != NULL ? & sinLength : NULL); - - if (result == INVALID_SOCKET) - return ENET_SOCKET_NULL; - - if (address != NULL) - { - address -> host = (enet_uint32) sin.sin_addr.s_addr; - address -> port = ENET_NET_TO_HOST_16 (sin.sin_port); - } - - return result; -} - -int -enet_socket_shutdown (ENetSocket socket, ENetSocketShutdown how) -{ - return shutdown (socket, (int) how) == SOCKET_ERROR ? -1 : 0; -} - -void -enet_socket_destroy (ENetSocket socket) -{ - if (socket != INVALID_SOCKET) - closesocket (socket); -} - -int -enet_socket_send (ENetSocket socket, - const ENetAddress * address, - const ENetBuffer * buffers, - size_t bufferCount) -{ - struct sockaddr_in sin; - DWORD sentLength = 0; - - if (address != NULL) - { - memset (& sin, 0, sizeof (struct sockaddr_in)); - - sin.sin_family = AF_INET; - sin.sin_port = ENET_HOST_TO_NET_16 (address -> port); - sin.sin_addr.s_addr = address -> host; - } - - if (WSASendTo (socket, - (LPWSABUF) buffers, - (DWORD) bufferCount, - & sentLength, - 0, - address != NULL ? (struct sockaddr *) & sin : NULL, - address != NULL ? sizeof (struct sockaddr_in) : 0, - NULL, - NULL) == SOCKET_ERROR) - { - if (WSAGetLastError () == WSAEWOULDBLOCK) - return 0; - - return -1; - } - - return (int) sentLength; -} - -int -enet_socket_receive (ENetSocket socket, - ENetAddress * address, - ENetBuffer * buffers, - size_t bufferCount) -{ - INT sinLength = sizeof (struct sockaddr_in); - DWORD flags = 0, - recvLength = 0; - struct sockaddr_in sin; - - if (WSARecvFrom (socket, - (LPWSABUF) buffers, - (DWORD) bufferCount, - & recvLength, - & flags, - address != NULL ? (struct sockaddr *) & sin : NULL, - address != NULL ? & sinLength : NULL, - NULL, - NULL) == SOCKET_ERROR) - { - switch (WSAGetLastError ()) - { - case WSAEWOULDBLOCK: - case WSAECONNRESET: - return 0; - } - - return -1; - } - - if (flags & MSG_PARTIAL) - return -1; - - if (address != NULL) - { - address -> host = (enet_uint32) sin.sin_addr.s_addr; - address -> port = ENET_NET_TO_HOST_16 (sin.sin_port); - } - - return (int) recvLength; -} - -int -enet_socketset_select (ENetSocket maxSocket, ENetSocketSet * readSet, ENetSocketSet * writeSet, enet_uint32 timeout) -{ - struct timeval timeVal; - - timeVal.tv_sec = timeout / 1000; - timeVal.tv_usec = (timeout % 1000) * 1000; - - return select (maxSocket + 1, readSet, writeSet, NULL, & timeVal); -} - -int -enet_socket_wait (ENetSocket socket, enet_uint32 * condition, enet_uint32 timeout) -{ - fd_set readSet, writeSet; - struct timeval timeVal; - int selectCount; - - timeVal.tv_sec = timeout / 1000; - timeVal.tv_usec = (timeout % 1000) * 1000; - - FD_ZERO (& readSet); - FD_ZERO (& writeSet); - - if (* condition & ENET_SOCKET_WAIT_SEND) - FD_SET (socket, & writeSet); - - if (* condition & ENET_SOCKET_WAIT_RECEIVE) - FD_SET (socket, & readSet); - - selectCount = select (socket + 1, & readSet, & writeSet, NULL, & timeVal); - - if (selectCount < 0) - return -1; - - * condition = ENET_SOCKET_WAIT_NONE; - - if (selectCount == 0) - return 0; - - if (FD_ISSET (socket, & writeSet)) - * condition |= ENET_SOCKET_WAIT_SEND; - - if (FD_ISSET (socket, & readSet)) - * condition |= ENET_SOCKET_WAIT_RECEIVE; - - return 0; -} - -#endif - diff --git a/resource/content/cert.pem b/resource/content/cert.pem new file mode 100644 index 0000000..f94c873 --- /dev/null +++ b/resource/content/cert.pem @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDKzCCAhOgAwIBAgIBADANBgkqhkiG9w0BAQsFADAaMRgwFgYDVQQDEw9waWNv +dGxzIHRlc3QgY2EwHhcNMTgwMjIzMDIzODEyWhcNMjgwMjIxMDIzODEyWjAbMRkw +FwYDVQQDExB0ZXN0LmV4YW1wbGUuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A +MIIBCgKCAQEA5soWzSG7iyawQlHM1yaX2dUAATUkhpbg2WPFOEem7E3zYzc6A/Z+ +bViFlfEgL37cbDUb4pnOAHrrsjGgkyBYh5i9iCTVfCk+H6SOHZJORO1Tq8X9C7Wc +NcshpSdm2Pa8hmv9hsHbLSeoPNeg8NkTPwMVaMZ2GpdmiyAmhzSZ2H9mzNI7ntPW +/XCchVf+ax2yt9haZ+mQE2NPYwHDjqCtdGkP5ZXXnYhJSBzSEhxfGckIiKDyOxiN +kLFLvUdT4ERSFBjauP2cSI0XoOUsiBxJNwHH310AU8jZbveSTcXGYgEuu2MIuDo7 +Vhkq5+TCqXsIFNbjy0taOoPRvUbPsbqFlQIDAQABo3sweTAJBgNVHRMEAjAAMCwG +CWCGSAGG+EIBDQQfFh1PcGVuU1NMIEdlbmVyYXRlZCBDZXJ0aWZpY2F0ZTAdBgNV +HQ4EFgQUE1vXDjBT8j2etP4brfHQ9DeKnpgwHwYDVR0jBBgwFoAUv3nKl7JgeCCW +qkZXnN+nsiP1JWMwDQYJKoZIhvcNAQELBQADggEBAKwARsxOCiGPXU1xhvs+pq9I +63mLi4rfnssOGzGnnAfuEaxggpozf3fOSgfyTaDbACdRPTZEStjQ5HMCcHvY7CH0 +8EYA+lkmFbuXXL8uHby1JBTzbTGf8pkRUsuF/Ie0SLChoDgt8oF3mY5pyU4HUaAw +Zp6HBpIRMdmbwGcwm25bl9MQYTrTX3dBfp3XPzfXbVwjJ7bsiTwAGq+dKwzwOQeM +2ZMZt4BQBoevsNopPrqG0S6kGUmJOIax0t13bKwDj21+Hp/O90HTFVCtAaDxRC56 +k0O8Q62ZxzjGJ7Zw6K3azXlH/BYE+CajxTUF+FKRRkkWL1GrFVUsYd9KLDAVry0= +-----END CERTIFICATE----- diff --git a/resource/content/key.pem b/resource/content/key.pem new file mode 100644 index 0000000..b46db68 --- /dev/null +++ b/resource/content/key.pem @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDmyhbNIbuLJrBC +UczXJpfZ1QABNSSGluDZY8U4R6bsTfNjNzoD9n5tWIWV8SAvftxsNRvimc4Aeuuy +MaCTIFiHmL2IJNV8KT4fpI4dkk5E7VOrxf0LtZw1yyGlJ2bY9ryGa/2GwdstJ6g8 +16Dw2RM/AxVoxnYal2aLICaHNJnYf2bM0jue09b9cJyFV/5rHbK32Fpn6ZATY09j +AcOOoK10aQ/lldediElIHNISHF8ZyQiIoPI7GI2QsUu9R1PgRFIUGNq4/ZxIjReg +5SyIHEk3AcffXQBTyNlu95JNxcZiAS67Ywi4OjtWGSrn5MKpewgU1uPLS1o6g9G9 +Rs+xuoWVAgMBAAECggEBAJZxS/XCNH/b43AH5LCnbrtH1u3yl3HIrp/nIquyQYSu +t6aIXKAysW1UFBiPCz0KxGMhJ6FKQ3gaqMQLB7KAllUl4v75i9SZCe8UlLOAKNdT +oYRK1s4oP8DtPmxrR+bMyE4T3TtX6SkBPfETWs1Fo/8iYnVfUaO559VvSs4+Ir91 +6EVBZ+xSQ6+H4nc9TvrByd7HHMmDGFXF19eo1rtQOxbNMiZSNwch+eFdzUr+hIqT +oeMiZCL+gTiWCglQIQNpRurSdc2szQSZNO85DlNU/kt2z2nxQMRrnl5mWUZdqbTM +qOEMq2RD/lR+fGwC/Zp1jly4f5c3QTF8oPzAhnmog20CgYEA9FtAZlmQ/8c5gDhh +n0dgBRDAVL0hmtDxjsL9RsvTVeWLXecpqUfFd0bpm/pi/QUIkoDc33jmUSh/2vCu +LCG5lvKsDVNwqiT+zsdHwGFyybYPNEPk6ILccr5fL7p8Fox5t9+oE+nct+P+VI82 +3TD/wN2soFPD9wDLjSDSfXMS7TsCgYEA8clXgh9AGJuUKoOHvsV9BFQ1HiFVtuXT +Qa+KKCLDnreeTuzF2s4nV51PJM5QEe0zfOABP2xjPYJRe+zIQ+rMwMA1Yxv17ZH/ +81w/poziTtZBGfCRmSnRY4gf3lTajUwcJhzmZ4AdV/ZQyX7rjFTZl9jeuMyY8uNY +y3SEpiw6a28CgYAKUhBWQlIte2yiTb9RyuHzVNHKwnI457pMHVA1PUafyiIoxSqt +S6q7bvNO8zRbG2tRRMAPcDvKEbvUs3Wnx4TfK0C5D10i0o0wjpopNfRzMI1T18pD +R8On1QKQMYAsM6KwcXHX5Xi9C5QiXiojDX6/1p0D6IXOWOo/+7LoOYQDIQKBgDJY +KB5x/1igXHOVu5gfau6R0hWZ/0z8Acb1lCDTTEQqG453gqMSteJqYOZbBxUUfNoN +knTwTqGqFulk3jY2F7gyzWr7kXOMKO01UhON1jlwJ1INY2Ou72h4GZqjtHYjWOEe +t2LprDJ6mUu7X7RynnQdthJol5hLeluywUQQhYGFAoGAbfGV4PhKD2sbY5ZDPkiu +8sWD8fLthbK6yFV7PrWRl4sOe32Iza6bvqT/EU0hbRr54vZXFFMJtev3PzqGNz/o +J5IkcpgVCXjOxIBmRognT4duuEM0EBiH1vaZF1f44x1ntnkncaW3wQ0VGp7xGURI +ykArGNH50gPRuPACNWvYoKE= +-----END PRIVATE KEY----- diff --git a/src/client.cpp b/src/client.cpp index 243de25..752f41f 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -26,14 +26,10 @@ int main(int argc, char *argv[]){ #if TRACY_ENABLE tracy::SetThreadName("Main"); #endif - - net::Setup(); auto client = Client(options.get()); client.run(nullptr); - net::Destroy(); - options.save(); return 0; diff --git a/src/client/Client.cpp b/src/client/Client.cpp index e62010f..7d5f7e9 100644 --- a/src/client/Client.cpp +++ b/src/client/Client.cpp @@ -215,7 +215,7 @@ void Client::run(server_handle* const localHandle) { } window.waitTargetFPS(); - } while (!(inputs.isDown(Input::Quit) || window.shouldClose() || world->isDisconnected())); + } while (!(inputs.isDown(Input::Quit) || window.shouldClose() || !world->isRunning())); options.contouring = state.contouring->getOptions(); world.reset(); diff --git a/src/client/config.hpp b/src/client/config.hpp index 82943d8..2439bf5 100644 --- a/src/client/config.hpp +++ b/src/client/config.hpp @@ -73,7 +73,7 @@ public: if(!useLocal) { world::client::Universe::connection ct; - ct.address = config["connection"]["address"].value_or(ct.address); + ct.host = config["connection"]["host"].value_or(ct.host); ct.port = config["connection"]["port"].value_or(ct.port); connection = ct; } @@ -147,7 +147,7 @@ public: #ifndef STANDALONE {"use_local", false}, #endif - {"address", ct.address}, + {"host", ct.host}, {"port", ct.port} })); } else { diff --git a/src/client/net/Client.cpp b/src/client/net/Client.cpp new file mode 100644 index 0000000..14b9848 --- /dev/null +++ b/src/client/net/Client.cpp @@ -0,0 +1,188 @@ +#include "Client.hpp" + +#include + +using namespace net::client; + +namespace net::client { + int connection_callback(picoquic_cnx_t* cnx, + uint64_t stream_id, uint8_t* bytes, size_t length, + picoquic_call_back_event_t fin_or_event, void* callback_ctx, void* v_stream_ctx) + { + auto client = (Client*)callback_ctx; + assert(cnx == nullptr || client->contains(cnx) || fin_or_event == picoquic_callback_close); + (void)cnx; + assert(v_stream_ctx == NULL || ((net::stream_ctx*)v_stream_ctx)->stream_id == stream_id); + return client->connectionCallback(stream_id, bytes, length, fin_or_event, v_stream_ctx); + } +} + +Client::Client(const address& ct, + std::function onPacket): + Context(nullptr, nullptr), Connection(nullptr, true, queue::count), onPacket(onPacket) +{ + const char *sni = NULL; + sockaddr_storage server_address; + if (const auto addr = ct.toAddress()) { + server_address = addr.value().first; + if (addr.value().second) { + sni = ct.host.c_str(); + } + } else { + FATAL("Invalid server address format"); + } + + LOG_D("Connecting to " << ct); + Connection::setup(Context::getHandle(), (struct sockaddr*) &server_address, sni, connection_callback, this); + + openSockets(0, server_address.ss_family); + + while (!connected && !disconnected) { + pull(); + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + } + if (!connected || disconnected) { + FATAL("Could not contact server at " << ct); + } +} +Client::~Client() { + Connection::release((uint16_t)disconnect_reason::QUIT); + while (connected && !disconnected) { + pull(); + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + } +} + +int Client::connectionCallback(uint64_t stream_id, uint8_t* bytes, size_t length, + picoquic_call_back_event_t fin_or_event, void* v_stream_ctx) { + + switch (fin_or_event) { + case picoquic_callback_stream_data: + case picoquic_callback_stream_fin: { + assert(stream_ctx::IsServerId(stream_id)); + auto stream_ctx = (in_stream_ctx *)v_stream_ctx; + + /* Data arrival on stream #x, maybe with fin mark */ + if (stream_ctx == NULL) { // New stream from server + stream_ctx = receive(stream_id); + } + + if (length > 0) { + stream_ctx->buffer.write(bytes, length); + } + + if (fin_or_event == picoquic_callback_stream_fin) { + if (onPacket(data::out_view(stream_ctx->buffer.data.data(), stream_ctx->buffer.data.size()), PacketFlags::NONE)) { + close(stream_ctx); + } else { + LOG_E("??"); + return -1; + } + } + break; + } + + case picoquic_callback_datagram: + if(!onPacket(data::out_view(bytes, length), PacketFlags::DATAGRAM)) { + return -1; + } + break; + + case picoquic_callback_stop_sending: /* Should not happen, treated as reset */ + /* Mark stream as abandoned, close the file, etc. */ + Connection::reset(stream_id); + /* Fall through */ + case picoquic_callback_stream_reset: /* Server reset stream #x */ { + assert(stream_ctx::IsUnidirId(stream_id)); + + // auto remote_error = picoquic_get_remote_stream_error(cnx, stream_id); + //FIXME: if remote_error callback onError(remote_error) + if (stream_ctx::IsClientId(stream_id)) { + auto stream_ctx = (out_stream_ctx*)v_stream_ctx; + if (stream_ctx == NULL) { + close(stream_ctx); + } + } else { + auto stream_ctx = (in_stream_ctx*)v_stream_ctx; + if (stream_ctx == NULL) { + close(stream_ctx); + } + } + break; + } + + case picoquic_callback_stateless_reset: + case picoquic_callback_close: /* Received connection close */ + case picoquic_callback_application_close: /* Received application close */ { + struct quit_notify { server_packet_type type; bool is_application; uint16_t reason; }; + const auto is_app = fin_or_event == picoquic_callback_application_close; + const auto quit = quit_notify{server_packet_type::QUIT, is_app, getErrorCode(is_app)}; + if(onPacket(data::out_view((const uint8_t*)&quit, sizeof(quit)), PacketFlags::DATAGRAM)) { + LOG_W("Connection closed"); + disconnected = true; + setCallback(NULL, NULL); + } + break; + } + case picoquic_callback_version_negotiation: + /* The client did not get the right version. + * TODO: some form of negotiation? + */ + LOG_W("Bad protocol version"); + break; + case picoquic_callback_stream_gap: + /* This callback is never used. */ + break; + case picoquic_callback_prepare_to_send: { + /* Active sending API */ + assert(stream_ctx::IsClientId(stream_id)); + + auto stream_ctx = (out_stream_ctx*)v_stream_ctx; + if (stream_ctx == NULL) { + return -1; + } + + if (!stream_ctx->buffer.isDone()) { + size_t available = stream_ctx->buffer.remaining(); + int is_fin = 1; + + /* The length parameter marks the space available in the packet */ + if (available > length) { + available = length; + is_fin = 0; + } + uint8_t* buffer = picoquic_provide_stream_data_buffer(bytes, available, is_fin, !is_fin); + if (buffer == NULL) { + return -1; + } + + // MAYBE: try zero copy + stream_ctx->buffer.read(buffer, available); + } else { + LOG_W("Out stream reset"); + reset(stream_id); + } + if (stream_ctx->buffer.isDone()) { + //MAYBE: reuse + close(stream_ctx); + } + break; + } + case picoquic_callback_almost_ready: + LOG_D("Connection to the server completed, almost ready"); + break; + case picoquic_callback_ready: + /* TODO: Check that the transport parameters are what the sample expects */ + LOG_I("Connected to server"); + connected = true; + break; + default: + //MAYBE: picoquic_callback_request_alpn_list + //MAYBE: picoquic_callback_set_alpn + //MAYBE: picoquic_callback_pacing_changed + /* unexpected -- just ignore. */ + break; + } + + return 0; +} diff --git a/src/client/net/Client.hpp b/src/client/net/Client.hpp new file mode 100644 index 0000000..1d6a021 --- /dev/null +++ b/src/client/net/Client.hpp @@ -0,0 +1,36 @@ +#pragma once + +#include "../../core/net/Context.hpp" + +#include + +namespace net::client { + +enum queue: uint8_t { + GLOBAL = 0, + MOVE, + count +}; + +class Client final: public Context, public Connection { +public: + Client(const address& ct, + std::function onPacket); + ~Client(); + + bool isRunning() const override { return !disconnected; } + + /// Read-write on sockets and notify callbacks + void pull() { Context::pull(1000, 50, 20); } + + int connectionCallback(uint64_t stream_id, uint8_t *bytes, size_t length, + picoquic_call_back_event_t fin_or_event, void *v_stream_ctx); + +private: + bool connected = false; + bool disconnected = false; + + std::function onPacket; +}; + +} \ No newline at end of file diff --git a/src/client/world/DistantUniverse.cpp b/src/client/world/DistantUniverse.cpp index 6b9b094..b030849 100644 --- a/src/client/world/DistantUniverse.cpp +++ b/src/client/world/DistantUniverse.cpp @@ -5,14 +5,15 @@ #include "Area.hpp" #include "../contouring/Abstract.hpp" #include "../../core/world/raycast.hpp" - +#include "../../core/net/io.hpp" #include "../../core/utils/logger.hpp" #include "Chunk.hpp" using namespace world::client; -DistantUniverse::DistantUniverse(const connection& ct, const options& opt, const std::string& contouring): Universe(contouring), peer(ct), - loadDistance(opt.loadDistance), keepDistance(opt.keepDistance), serverDistance(0) { } +DistantUniverse::DistantUniverse(const connection& ct, const options& opt, const std::string& contouring): Universe(contouring), + loadDistance(opt.loadDistance), keepDistance(opt.keepDistance), serverDistance(0), + peer(ct, [&](const data::out_view& buf, net::PacketFlags flags){ return onPacket(buf, flags); }) { } DistantUniverse::~DistantUniverse() { } void DistantUniverse::update(voxel_pos pos, float deltaTime) { @@ -20,7 +21,10 @@ void DistantUniverse::update(voxel_pos pos, float deltaTime) { const auto chunkChange = cur_chunk != last_chunk; last_chunk = cur_chunk; - pullNetwork(pos); + { //NOTE: triggers onPacket + ZoneScopedN("Pull"); + peer.pull(); + } { // Update alive areas ZoneScopedN("World"); @@ -42,7 +46,7 @@ void DistantUniverse::update(voxel_pos pos, float deltaTime) { ++it_c; } } - if ((chunkChangeArea && (std::rand() & (1 << 4)-1) == 0) || mayQueryChunks) { // Request missing chunks + if (chunkChangeArea || mayQueryChunks) { // Request missing chunks ZoneScopedN("Missing"); std::vector missing; std::vector missingDist; @@ -78,11 +82,10 @@ void DistantUniverse::update(voxel_pos pos, float deltaTime) { } }}} if(!missing.empty()) { - auto packet = net::Client::makePacket(net::client_packet_type::MISSING_CHUNKS, NULL, - sizeof(area_id) + missing.size() * sizeof(chunk_pos), ENET_PACKET_FLAG_RELIABLE, peer.getSalt()); + auto packet = net::PacketWriter(net::client_packet_type::MISSING_CHUNKS, sizeof(area_id) + missing.size() * sizeof(chunk_pos)); packet.write(area.first); packet.write(missing.data(), missing.size() * sizeof(chunk_pos)); - peer.send(packet.get(), net::channel_type::RELIABLE); + peer.send(packet.finish()); } mayQueryChunks = false; } @@ -92,198 +95,195 @@ void DistantUniverse::update(voxel_pos pos, float deltaTime) { contouring->update(pos, areas); } -void DistantUniverse::pullNetwork(voxel_pos pos) { - ZoneScopedN("Pull"); +bool DistantUniverse::onPacket(const data::out_view& buf, net::PacketFlags) { using namespace net; - peer.pull( - [&](packet_t* packet, channel_type){ - const server_packet_type type = static_cast(*packet->data); - switch (type) { - case server_packet_type::CAPABILITIES: { - PacketReader(packet, true).read(serverDistance); - break; - } - case server_packet_type::COMPRESSION: { - if(dict.has_value()) - break; - - dict.emplace(packet->data + sizeof(server_packet_type), packet->dataLength - sizeof(server_packet_type)); - LOG_T("Compression dictionnary loaded"); - mayQueryChunks = true; - break; - } - - case server_packet_type::TELEPORT: { - auto reader = PacketReader(packet); - if (reader.read(playerId) && reader.read(pos)) - onTeleport(pos); - break; - } - - case server_packet_type::MESSAGE: { - const auto ref = PacketReader(packet, true).remaning(); - onMessage(std::string(static_cast(ref.data()), ref.size())); - break; - } - - case server_packet_type::AREAS: { - auto reader = PacketReader(packet, true); - while(!reader.isFull()) { - area_id id; - if(!reader.read(id)) - break; - world::Area::params p; - if(!reader.read(p)) - break; - - if (auto it = areas.find(id); it != areas.end()) { - std::dynamic_pointer_cast(it->second)->update(p); - } else { - areas.emplace(id, std::make_shared(p)); - } - } - break; - } - - case server_packet_type::CHUNK: { - ZoneScopedN("Chunk"); - if (!dict.has_value()) - break; - - auto reader = PacketReader(packet, true); - if (reader.isFull()) { - mayQueryChunks = true; - break; - } - - area_ pos; - if(!reader.read(pos)) - break; - - auto it = areas.find(pos.first); - if(it == areas.end()) { - LOG_W("Chunk area not found " << pos.first.index); - break; - } - if(!it->second->getChunks().inRange(pos.second)) { - LOG_W("Chunk out of area " << pos.first.index); - break; - } - - auto data = reader.remaning(); - std::vector buffer; - if(auto err = dict.value().decompress(data, buffer)) { - LOG_E("Corrupted chunk packet " << err.value()); - break; - } - vec_istream idata(buffer); - std::istream iss(&idata); - auto ck = std::make_shared(iss); - ck->invalidate(geometry::Faces::All); - auto ptr = std::dynamic_pointer_cast(ck); - auto &chunks = it->second->setChunks(); - if (auto it_c = chunks.find(pos.second); it_c != chunks.end()) { - it_c->second = ptr; - } else { - chunks.emplace(pos.second, ptr); - } - break; - } - - case server_packet_type::EDITS: { - ZoneScopedN("Edits"); - auto reader = PacketReader(packet, true); - area_id id; - if(!reader.read(id)) - break; - - auto it = areas.find(id); - if(it == areas.end()) { - LOG_W("Edit area not found " << id.index); - break; - } - - while(!reader.isFull()) { - chunk_pos pos = chunk_pos(INT_MAX); - reader.read(pos); - chunk_voxel_idx count = 0; - reader.read(count); - if (auto ptr = it->second->setChunks().findInRange(pos)) { - auto chunk = std::dynamic_pointer_cast(ptr.value()); - for (auto i = 0; i < count && !reader.isFull(); i++) { - Chunk::Edit edit; - reader.read(edit); - chunk->apply(edit); - } - } else { - reader.skip(count * sizeof(Chunk::Edit)); - } - } - break; - } - - case server_packet_type::ENTITY_TYPES: { - entities.clear(); - auto reader = PacketReader(packet, true); - while(!reader.isFull()) { - size_t index; - glm::vec3 size; - glm::vec3 scale; - if(reader.read(index) && reader.read(size) && reader.read(scale)) { - entities.set_emplace(index, size, scale); - } - } - break; - } - - case server_packet_type::ENTITIES: { - auto reader = PacketReader(packet, true); - while(!reader.isFull()) { - size_t entity_idx; - size_t count; - if (!(reader.read(entity_idx) && reader.read(count))) - break; - - if (auto entity = entities.get(entity_idx)) { - entity->instances.clear(); - for (size_t i = 0; i < count && !reader.isFull(); i++) { - size_t idx; - glm::ifvec3 pos; - glm::vec3 vel; - if (reader.read(idx) && reader.read(pos) && reader.read(vel)) - entity->instances.set_emplace(idx, Universe::Entity::Instance{pos, vel}); - } - } else { - LOG_W("Unknown entity type " << entity_idx); - reader.skip(count * (sizeof(size_t) + sizeof(glm::ifvec3) + sizeof(glm::vec3))); - } - } - break; - } - - default: - LOG_W("Bad packet from server"); - break; - } - }, - [](disconnect_reason reason){ - if (reason == disconnect_reason::UNEXPECTED) { - LOG_E("Connection to server lost"); + auto packet = PacketReader(buf); + server_packet_type type; + if(!packet.read(type)) { + LOG_D("Empty packet"); + return true; + } + switch (type) { + case server_packet_type::QUIT: { + bool is_app = false; + is_app &= packet.read(is_app); + uint16_t reason = 0; + packet.read(reason); + if (is_app) { + LOG_E("Disconnected from server (" << reason << ')'); } else { - LOG_W("Disconnected from server with " << (int)reason); + LOG_D("Connection lost (" << reason << ')'); } - }); + break; + } + + case server_packet_type::CAPABILITIES: + return packet.read(serverDistance); + + case server_packet_type::COMPRESSION: { + const auto remain = packet.readAll(); + dict.emplace(remain.data(), remain.size()); + LOG_T("Compression dictionnary loaded"); + mayQueryChunks = true; + break; + } + + case server_packet_type::TELEPORT: { + voxel_pos pos; + if (packet.read(playerId) && packet.read(pos)) + onTeleport(pos); + break; + } + + case server_packet_type::MESSAGE: { + const auto remain = packet.readAll(); + onMessage(std::string((const char*)remain.data(), remain.size())); + break; + } + + case server_packet_type::AREAS: { + while(!packet.isFull()) { + area_id id; + world::Area::params p; + if(!packet.read(id) || !packet.read(p)) + break; + + if (auto it = areas.find(id); it != areas.end()) { + std::dynamic_pointer_cast(it->second)->update(p); + } else { + areas.emplace(id, std::make_shared(p)); + } + } + break; + } + + case server_packet_type::CHUNK: { + ZoneScopedN("Chunk"); + if (!dict.has_value()) + break; + + if (packet.isFull()) { + mayQueryChunks = true; + break; + } + + area_ pos; + if(!packet.read(pos)) + break; + + auto it = areas.find(pos.first); + if(it == areas.end()) { + LOG_W("Chunk area not found " << pos.first.index); + break; + } + if(!it->second->getChunks().inRange(pos.second)) { + LOG_W("Chunk out of area " << pos.first.index); + break; + } + + std::vector buffer; + if(auto err = dict.value().decompress(packet.readAll(), buffer)) { + LOG_E("Corrupted chunk packet " << err.value()); + break; + } + data::vec_istream idata(buffer); + std::istream iss(&idata); + auto ck = std::make_shared(iss); + ck->invalidate(geometry::Faces::All); + auto ptr = std::dynamic_pointer_cast(ck); + auto &chunks = it->second->setChunks(); + if (auto it_c = chunks.find(pos.second); it_c != chunks.end()) { + it_c->second = ptr; + } else { + chunks.emplace(pos.second, ptr); + } + break; + } + + case server_packet_type::EDITS: { + ZoneScopedN("Edits"); + area_id id; + if(!packet.read(id)) + break; + + auto it = areas.find(id); + if(it == areas.end()) { + LOG_W("Edit area not found " << id.index); + break; + } + + while(!packet.isFull()) { + chunk_pos pos = chunk_pos(INT_MAX); + packet.read(pos); + chunk_voxel_idx count = 0; + packet.read(count); + if (auto ptr = it->second->setChunks().findInRange(pos)) { + auto chunk = std::dynamic_pointer_cast(ptr.value()); + for (auto i = 0; i < count && !packet.isFull(); i++) { + Chunk::Edit edit; + packet.read(edit); + chunk->apply(edit); + } + } else { + packet.skip(count * sizeof(Chunk::Edit)); + } + } + break; + } + + case server_packet_type::ENTITY_TYPES: { + entities.clear(); + while(!packet.isFull()) { + size_t index; + glm::vec3 size; + glm::vec3 scale; + if(packet.read(index) && packet.read(size) && packet.read(scale)) { + entities.set_emplace(index, size, scale); + } + } + break; + } + + case server_packet_type::ENTITIES: { + while(!packet.isFull()) { + size_t entity_idx; + size_t count; + if (!(packet.read(entity_idx) && packet.read(count))) + break; + + if (auto entity = entities.get(entity_idx)) { + entity->instances.clear(); + for (size_t i = 0; i < count && !packet.isFull(); i++) { + size_t idx; + glm::ifvec3 pos; + glm::vec3 vel; + if (packet.read(idx) && packet.read(pos) && packet.read(vel)) + entity->instances.set_emplace(idx, Universe::Entity::Instance{pos, vel}); + } + } else { + LOG_W("Unknown entity type " << entity_idx); + packet.skip(count * (sizeof(size_t) + sizeof(glm::ifvec3) + sizeof(glm::vec3))); + } + } + break; + } + + default: + LOG_W("Bad packet from server"); + break; + } + return true; } void DistantUniverse::emit(const action::packet &action) { if(const auto move = std::get_if(&action)) { - peer.send(net::client_packet_type::MOVE, move->pos, net::channel_type::NOTIFY); + peer.send(net::PacketWriter::Of(net::client_packet_type::MOVE, move->pos), net::client::queue::MOVE, 1); } else if(const auto message = std::get_if(&action)) { - peer.send(net::client_packet_type::MESSAGE, message->text.data(), message->text.size(), net::channel_type::RELIABLE); + peer.send(net::PacketWriter::Of(net::client_packet_type::MESSAGE, message->text.data(), message->text.size())); } else if(const auto fill = std::get_if(&action)) { - peer.send(net::client_packet_type::FILL_SHAPE, *fill, net::channel_type::RELIABLE); + peer.send(net::PacketWriter::Of(net::client_packet_type::FILL_SHAPE, *fill)); } else { LOG_W("Bad action " << action.index()); } diff --git a/src/client/world/DistantUniverse.hpp b/src/client/world/DistantUniverse.hpp index 2f616a4..c0a53b4 100644 --- a/src/client/world/DistantUniverse.hpp +++ b/src/client/world/DistantUniverse.hpp @@ -1,7 +1,7 @@ #pragma once #include "Universe.hpp" -#include "../../core/net/Client.hpp" +#include "../net/Client.hpp" #include "../../core/utils/zctx.hpp" namespace world::client { @@ -23,12 +23,12 @@ namespace world::client { void getEntitiesModels(const std::function&)>&, const std::optional& frustum, const glm::llvec3& offset, int density) override; - bool isDisconnected() const override { return peer.isDisconnected(); } + bool isRunning() const override { return peer.isRunning(); } size_t getPlayerId() const override { return playerId; } protected: - void pullNetwork(voxel_pos); + bool onPacket(const data::out_view&, net::PacketFlags); /// Alive areas containing chunks area_map areas; @@ -43,15 +43,16 @@ namespace world::client { data::generational::view_vector entities; /// Player entity instance index size_t playerId; - - net::Client peer; std::optional dict; chunk_pos last_chunk = chunk_pos(INT_MAX); bool mayQueryChunks = false; + uint64_t moveCounter = 0; ushort loadDistance; ushort keepDistance; ushort serverDistance; + + net::client::Client peer; }; } \ No newline at end of file diff --git a/src/client/world/LocalUniverse.hpp b/src/client/world/LocalUniverse.hpp index dfa9f7f..a0a7f7b 100644 --- a/src/client/world/LocalUniverse.hpp +++ b/src/client/world/LocalUniverse.hpp @@ -20,7 +20,7 @@ namespace world::client { void getEntitiesModels(const std::function&)>&, const std::optional& frustum, const glm::llvec3& offset, int density) override; - bool isDisconnected() const override { return !handle->running; } + bool isRunning() const override { return handle->running; } size_t getPlayerId() const override { return PLAYER_ENTITY_ID.index; } diff --git a/src/client/world/Universe.hpp b/src/client/world/Universe.hpp index fd6f601..8b22f1e 100644 --- a/src/client/world/Universe.hpp +++ b/src/client/world/Universe.hpp @@ -18,8 +18,8 @@ namespace world::client { Universe(const std::string& contouring); virtual ~Universe(); - struct connection: net::connection { - connection(): net::connection{"localhost", 4242} { } + struct connection: net::address { + connection(): net::address{"localhost", 4242} { } }; /// Update edits and contouring @@ -43,7 +43,7 @@ namespace world::client { virtual size_t getPlayerId() const = 0; - virtual bool isDisconnected() const = 0; + virtual bool isRunning() const = 0; protected: /// Contouring worker diff --git a/src/core/data/mem.hpp b/src/core/data/mem.hpp new file mode 100644 index 0000000..a542f65 --- /dev/null +++ b/src/core/data/mem.hpp @@ -0,0 +1,90 @@ +#pragma once + +#include +#include +#include + +namespace data { + +struct vec_istream: std::streambuf { + vec_istream(std::vector &vec) { + this->setg(&vec[0], &vec[0], &vec[0] + vec.size()); + } +}; + +struct view { + view(size_t size): siz(size) { } + + size_t siz; + size_t cur = 0; + + constexpr size_t size() const { return siz; } + constexpr bool isDone() const { return cur >= siz; } +}; +struct in_vector { + /// 512 Mébibits + static constexpr size_t MAX_SIZE = 1ul << 26; + + in_vector(size_t max_size = MAX_SIZE): max_size(max_size) {} + + std::vector data; + size_t max_size; + + uint8_t* writeTo(size_t len) { + const auto offset = data.size(); + assert(offset + len <= max_size); + data.resize(offset + len); + return data.data() + offset; + } + void write(const uint8_t* data, size_t len) { + memcpy(writeTo(len), data, len); + } +}; +struct in_view: view { + in_view(uint8_t *ptr, size_t size): view(size), ptr(ptr) { + assert(ptr != nullptr); + } + + uint8_t* ptr; + uint8_t* writeTo(size_t len) { + assert(cur + len <= siz); + auto p = ptr + cur; + cur += len; + return p; + } + void write(const uint8_t* data, size_t len) { + memcpy(writeTo(len), data, len); + } +}; +struct out_view: view { + out_view(): out_view(nullptr, 0) { } + out_view(const uint8_t *ptr, size_t size): view(size), ptr(ptr) { } + + const uint8_t* ptr; + const uint8_t* readFrom(size_t len) { + assert(cur + len <= siz); + auto p = ptr + cur; + cur += len; + return p; + } + void read(uint8_t* out, size_t len) { + memcpy(out, readFrom(len), len); + } + + constexpr const uint8_t* data() const { return ptr; } + constexpr size_t remaining() const { return size() - cur; } +}; +using handle_t = std::shared_ptr; +struct out_buffer: out_view { + out_buffer(): out_view() { } + out_buffer(const out_view& view, const handle_t& handle): + out_view(view), handle(handle) { } + /// Take view ownership + out_buffer(const out_view& view): + out_buffer(view, std::shared_ptr(view.ptr)) { } + /// Take ptr ownership + out_buffer(const uint8_t *ptr, size_t size): out_buffer(out_view(ptr, size)) { } + + handle_t handle = nullptr; +}; +} \ No newline at end of file diff --git a/src/core/net/Client.hpp b/src/core/net/Client.hpp deleted file mode 100644 index a162645..0000000 --- a/src/core/net/Client.hpp +++ /dev/null @@ -1,134 +0,0 @@ -#pragma once - -#include "data.hpp" -#include "PacketView.hpp" -#include - -namespace net { - -class Client { -public: - static constexpr auto HEADER_SIZE = sizeof(client_packet_type) + sizeof(salt_t); - - Client(const connection& ct) { - auto addr = [&] { - if(auto addr = ct.toAddress()) - return addr.value(); - - FATAL("Invalid server address format"); - }(); - - host = enet_host_create(NULL, 1, CHANNEL_COUNT, 0, 0); - if(host == nullptr) { - FATAL("Network client creation failed"); - } - - LOG_D("Connecting to " << ct); - salt = std::rand(); - peer = enet_host_connect(host, &addr, CHANNEL_COUNT, salt); - if(peer == nullptr) { - FATAL("Cannot contact server " << ct); - } - - if(ENetEvent event; enet_host_service(host, &event, TIMEOUT) <= 0 || event.type != ENET_EVENT_TYPE_CONNECT) { - enet_peer_reset(peer); - FATAL("Connection to server " << ct << " failed"); - } - - LOG_I("Connected to server " << ct); - } - ~Client() { - LOG_D("Leaving server"); - enet_peer_disconnect_now(peer, (enet_uint32)disconnect_reason::QUIT); - enet_host_destroy(host); - } - - template - void pull(R onData, D onDisconnect, int delay = 0, int count = 10) { - ENetEvent event; - for(int i = 0; i < count && enet_host_service(host, &event, delay) > 0; i++) { - switch(event.type) { - case ENET_EVENT_TYPE_CONNECT: - LOG_T("Client reconnected"); - break; - - case ENET_EVENT_TYPE_DISCONNECT: - LOG_D("Client disconnected with reason " << event.data); - ready = false; - onDisconnect((disconnect_reason)event.data); - break; - - case ENET_EVENT_TYPE_RECEIVE: { - if(event.packet->dataLength < sizeof(server_packet_type)) { - LOG_T("Empty packet from server"); - break; - } - const server_packet_type type = static_cast(*event.packet->data); - if(type < server_packet_type::BROADCASTED) { - if(event.packet->dataLength < sizeof(server_packet_type) + sizeof(salt)) { - LOG_T("Wrong salted packet size"); - break; - } - if(memcmp(&salt, event.packet->data + sizeof(server_packet_type), sizeof(salt)) != 0) { - LOG_T("Wrong server salt"); - break; - } - } - if(type == server_packet_type::CHALLENGE) { - if(event.packet->dataLength != sizeof(server_packet_type) + 2 * sizeof(salt)) { - LOG_T("Wrong challenge packet size"); - break; - } - - salt_t l; - PacketReader(event.packet).read(l); - salt ^= l; - LOG_T("Handshake done"); - ready = true; - break; - } - - onData(event.packet, (channel_type)event.channelID); - enet_packet_destroy(event.packet); - break; - } - - case ENET_EVENT_TYPE_NONE: - break; - } - } - TracyPlot("CltNetRTT", (int64_t)peer->roundTripTime); - } - - bool send(packet_t* packet, channel_type channel) { - return enet_peer_send(peer, (enet_uint8)channel, packet) == 0; - } - bool send(client_packet_type type, const void *data, size_t size, channel_type channel, std::optional flags = {}) { - return send(makePacket(type, data, size, flags.value_or(channel == channel_type::RELIABLE ? ENET_PACKET_FLAG_RELIABLE : 0), salt).get(), channel); - } - template - bool send(client_packet_type type, const D& data, channel_type channel, std::optional flags = {}) { - static_assert(!std::is_pointer::value); - return send(type, &data, sizeof(data), channel, flags); - } - - static PacketWriter makePacket(client_packet_type type, const void* data, size_t size, enet_uint32 flags, salt_t salt) { - auto packet = PacketWriter(sizeof(client_packet_type) + sizeof(salt_t) + size, flags); - packet.write(type); - packet.write(salt); - if (data != nullptr) - packet.write(data, size); - return packet; - } - - constexpr bool isReady() const { return ready; } - constexpr bool isDisconnected() const { return peer->state == ENET_PEER_STATE_DISCONNECTED; } - constexpr salt_t getSalt() const { return salt; } - -protected: - ENetHost *host; - ENetPeer *peer; - salt_t salt; - bool ready = false; -}; -} \ No newline at end of file diff --git a/src/core/net/Context.cpp b/src/core/net/Context.cpp new file mode 100644 index 0000000..2f9d156 --- /dev/null +++ b/src/core/net/Context.cpp @@ -0,0 +1,397 @@ +#include "Context.hpp" + +#ifdef _WINDOWS +#define WIN32_LEAN_AND_MEAN +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef SOCKET_TYPE +#define SOCKET_TYPE SOCKET +#endif +#ifndef SOCKET_CLOSE +#define SOCKET_CLOSE(x) closesocket(x) +#endif +#ifndef WSA_LAST_ERROR +#define WSA_LAST_ERROR(x) WSAGetLastError() +#endif +#ifndef socklen_t +#define socklen_t int +#endif + +#else /* Linux */ + +#include +#include +#include +#include +#include +#include +#include + +#ifndef __USE_XOPEN2K +#define __USE_XOPEN2K +#endif +#ifndef __USE_POSIX +#define __USE_POSIX +#endif +#include +#include +#include +#include +#include + +#ifndef SOCKET_TYPE +#define SOCKET_TYPE int +#endif +#ifndef INVALID_SOCKET +#define INVALID_SOCKET -1 +#endif +#ifndef SOCKET_CLOSE +#define SOCKET_CLOSE(x) close(x) +#endif +#ifndef WSA_LAST_ERROR +#define WSA_LAST_ERROR(x) ((long)(x)) +#endif +#endif + +#include +#include +//FIXME: Windows compat +//MAYBE: templatise callback switch + +using namespace net; + +bool stream_ctx::IsUnidirId(uint64_t id) { return !IS_BIDIR_STREAM_ID(id); } +bool stream_ctx::IsServerId(uint64_t id) { return IsUnidirId(id) && !IS_CLIENT_STREAM_ID(id); } +bool stream_ctx::IsClientId(uint64_t id) { return IsUnidirId(id) && IS_CLIENT_STREAM_ID(id); } + +Context::Context(uint32_t max_connections, char const *cert, char const *key, + picoquic_stream_data_cb_fn callback, void* callback_ctx, + char const *tickets, char const *tokens): + ticket_store_filename(tickets), token_store_filename(tokens) +{ + if (max_connections > 0) { + uint64_t current_time = picoquic_current_time(); + quic = picoquic_create(max_connections, cert, key, NULL, ALPN, callback, callback_ctx, + NULL, NULL, NULL, current_time, NULL, ticket_store_filename, NULL, 0); + if (quic == NULL) { + FATAL("Network context creation failed"); + } + + if (token_store_filename != nullptr) { + if (picoquic_load_retry_tokens(quic, token_store_filename) != 0) { + LOG_W("No token file present. Will create one as " << token_store_filename); + } + } + + picoquic_set_default_congestion_algorithm(quic, picoquic_bbr_algorithm); + //MAYBE: picoquic_set_cookie_mode(quic, 2); + +#if TRACE + picoquic_set_key_log_file_from_env(quic); + picoquic_set_log_level(quic, 1); + //MAYBE: picoquic_set_textlog +#endif + /* + FIXME: esni from ct.address + //MAYBE: multi universe + if (esni_key_file_name != NULL && esni_rr_file_name != NULL) { + ret = picoquic_esni_load_key(qserver, esni_key_file_name); + if (ret == 0) { + ret = picoquic_esni_server_setup(qserver, esni_rr_file_name); + } + } + */ + + } else { + quic = nullptr; + LOG_D("Local only context"); + } +} +Context::~Context() { + for (int i = 0; i < nb_sockets; i++) { + if (sockets[i] != INVALID_SOCKET) { + SOCKET_CLOSE(sockets[i]); + sockets[i] = INVALID_SOCKET; + } + } + + if (quic != NULL) { + if (ticket_store_filename != NULL) { + if (picoquic_save_session_tickets(quic, ticket_store_filename) != 0) { + LOG_E("Could not store the saved session tickets."); + } + } + if (token_store_filename != NULL) { + if (picoquic_save_retry_tokens(quic, token_store_filename) != 0) { + LOG_E("Could not save tokens to " << token_store_filename); + } + } + picoquic_free(quic); + } +} + +void Context::openSockets(int port, int family) { + assert(quic != nullptr); +#ifdef _WINDOWS + WSADATA wsaData = {0}; + (void)WSA_START(MAKEWORD(2, 2), &wsaData); +#endif + memset(sockets_family, 0, sizeof(sockets_family)); + + { + nb_sockets = (family == AF_UNSPEC) ? 2 : 1; + + /* Compute how many sockets are necessary */ + if (family == AF_UNSPEC) { + sockets_family[0] = AF_INET; + sockets_family[1] = AF_INET6; + } else if (family == AF_INET || family == AF_INET6) { + sockets_family[0] = family; + } else { + FATAL("Could not open socket. Unsupported AF " << family); + } + + for (int i = 0; i < nb_sockets; i++) { + int recv_set = 0; + int send_set = 0; + + if ((sockets[i] = socket(sockets_family[i], SOCK_DGRAM, IPPROTO_UDP)) == INVALID_SOCKET || + picoquic_socket_set_ecn_options(sockets[i], sockets_family[i], &recv_set, &send_set) != 0 || + picoquic_socket_set_pkt_info(sockets[i], sockets_family[i]) != 0 || + (port != 0 && picoquic_bind_to_port(sockets[i], sockets_family[i], port) != 0)) { + for (int j = 0; j < i; j++) { + if (sockets[i] != INVALID_SOCKET) { + SOCKET_CLOSE(sockets[i]); + sockets[i] = INVALID_SOCKET; + } + } + FATAL("Cannot setup socket. AF " << sockets_family[i] << ", port " << port); + } + } + } +} + +void Context::pull(const uint64_t MAX_DELAY, const uint64_t MAX_LOOPS, const uint64_t MAX_INNER_LOOPS) { + if (quic == nullptr) + return; + + const uint64_t start_time = picoquic_get_quic_time(quic); + uint64_t current_time = start_time; + uint64_t next_wake_time = current_time; + uint64_t nb_loops = 0; + + struct sockaddr_storage addr_from; + struct sockaddr_storage addr_to; + + int if_index_to; + uint8_t buffer[PICOQUIC_MAX_PACKET_SIZE]; + uint8_t send_buffer[PICOQUIC_MAX_PACKET_SIZE]; + size_t send_length = 0; + + //MAYBE: port migration + + while (isRunning() && nb_loops < MAX_LOOPS && next_wake_time - start_time < MAX_DELAY) { + /* Wait for packets */ + unsigned char received_ecn; + int socket_rank = -1; + if_index_to = 0; + int64_t wake_delay = next_wake_time - current_time; + int bytes_recv = picoquic_select_ex(sockets, nb_sockets, + &addr_from, &addr_to, + &if_index_to, &received_ecn, + buffer, sizeof(buffer), + wake_delay, &socket_rank, ¤t_time); + + nb_loops++; + + if (bytes_recv < 0) { + LOG_E("Socket pull error"); + break; + } + + uint64_t loop_time = current_time; + uint16_t current_recv_port = socket_port; + + if (bytes_recv > 0) { + /* track the local port value if not known yet */ + if (socket_port == 0 && nb_sockets == 1) { + struct sockaddr_storage local_address; + if (picoquic_get_local_address(sockets[0], &local_address) != 0) { + memset(&local_address, 0, sizeof(struct sockaddr_storage)); + LOG_E("Could not read local address"); + } else if (addr_to.ss_family == AF_INET6) { + socket_port = ((struct sockaddr_in6*) & local_address)->sin6_port; + } else if (addr_to.ss_family == AF_INET) { + socket_port = ((struct sockaddr_in*) & local_address)->sin_port; + } + current_recv_port = socket_port; + } + /* Document incoming port */ + if (addr_to.ss_family == AF_INET6) { + ((struct sockaddr_in6*) & addr_to)->sin6_port = current_recv_port; + } else if (addr_to.ss_family == AF_INET) { + ((struct sockaddr_in*) & addr_to)->sin_port = current_recv_port; + } + /* Read packet */ + (void)picoquic_incoming_packet(quic, buffer, + (size_t)bytes_recv, (struct sockaddr*) & addr_from, + (struct sockaddr*) & addr_to, if_index_to, received_ecn, + current_time); + } + + /* Emit packets */ + uint64_t nb_inner_loops = 0; + while (nb_inner_loops < MAX_INNER_LOOPS && loop_time - start_time < MAX_DELAY) { + nb_inner_loops++; + + struct sockaddr_storage peer_addr; + struct sockaddr_storage local_addr; + int if_index = 0; + int sock_err = 0; + + int ret = picoquic_prepare_next_packet(quic, loop_time, + send_buffer, sizeof(send_buffer), &send_length, + &peer_addr, &local_addr, &if_index, NULL, NULL); + if (ret != 0 || send_length <= 0) + break; + + SOCKET_TYPE send_socket = INVALID_SOCKET; + for (int i = 0; i < nb_sockets; i++) { + if (sockets_family[i] == peer_addr.ss_family) { + send_socket = sockets[i]; + break; + } + } + + int sock_ret = picoquic_send_through_socket(send_socket, + (struct sockaddr*) & peer_addr, (struct sockaddr*) & local_addr, if_index, + (const char*)send_buffer, (int)send_length, &sock_err); + if (sock_ret <= 0) { + LOG_E("Cannot send data through socket"); + } + } + next_wake_time = picoquic_get_next_wake_time(quic, current_time); + } +} + +Connection::~Connection() { + if (cnx != nullptr) + release(0); +} +void Connection::setup(picoquic_quic_t* ctx, sockaddr* addr, const char* sni, picoquic_stream_data_cb_fn cb_fn, void *cb_ctx) { + auto cnx = picoquic_create_cnx(ctx, picoquic_null_connection_id, picoquic_null_connection_id, + addr, picoquic_current_time(), 0, sni, ALPN, is_client); + if (cnx == NULL) { + FATAL("Network connection creation failed"); + } + setHandle(cnx); + setCallback(cb_fn, cb_ctx); + /* Client connection parameters could be set here, before starting the connection. */ + if (picoquic_start_client_cnx(cnx) < 0) { + FATAL("Could not activate connection"); + } +} +void Connection::setHandle(picoquic_cnx_t* c) { + assert(cnx == nullptr); + cnx = c; +#if TRACE + picoquic_connection_id_t icid = picoquic_get_initial_cnxid(cnx); + printf("QUIC connection ID: "); + for (uint8_t i = 0; i < icid.id_len; i++) { + printf("%02x", icid.id[i]); + } + printf("\n"); +#endif +} +void Connection::setCallback(picoquic_stream_data_cb_fn cb_fn, void *ctx) { + if (cnx != nullptr) + picoquic_set_callback(cnx, cb_fn, ctx); +} +void Connection::release(uint16_t reason) { + if (cnx != nullptr) { + picoquic_close(cnx, reason); + cnx = nullptr; + } +} + +uint16_t Connection::getErrorCode(bool is_app) { + return cnx != nullptr ? (is_app ? cnx->remote_application_error : cnx->remote_error) : 42; +} +std::string Connection::getAddress() { + auto str = std::string("?:?"); + if (cnx != nullptr) { + sockaddr* addr = NULL; + picoquic_get_peer_addr(cnx, &addr); + str.resize(128, '\0'); + picoquic_addr_text(addr, str.data(), str.size()); + } + return str; +} + +void Connection::sendCopy(const uint8_t* ptr, size_t len, uint8_t queue, size_t queue_size) { + auto data = (uint8_t*)malloc(len); + assert(data != nullptr); + memcpy(data, ptr, len); + send(data::out_buffer(data, len), queue, queue_size); +} +void Connection::send(const data::out_view &view, uint8_t queue, size_t queue_size) { + send(data::out_buffer(view), queue, queue_size); +} +void Connection::send(const data::out_buffer& buffer, uint8_t queue_id, size_t queue_size) { + if (cnx == nullptr) + return; + + assert(queue_id < outgoing.size()); + auto& queue = outgoing.at(queue_id); + if (queue_size == 0 || GetSize(queue.streams) < queue_size) { + auto stream = &queue.streams.emplace_front(nextOutgoingId(), queue_id, buffer); + auto res = picoquic_mark_active_stream(cnx, stream->stream_id, 1, stream); + if (res != 0) { + LOG_E("Cannot initialize stream " << res); + } + } else { + queue.pending = buffer; + } +} + +void Connection::emit(const uint8_t *ptr, size_t size) { + assert(cnx != nullptr); + auto res = picoquic_queue_datagram_frame(cnx, size, ptr); + if (res != 0) { + LOG_E("Cannot initialize datagram " << res); + } +} + +in_stream_ctx *Connection::receive(uint64_t streamId) { + auto stream = &incoming.emplace_front(streamId); + if (cnx != nullptr) { + picoquic_set_app_stream_ctx(cnx, streamId, stream); + } + return stream; +} + +void Connection::close(out_stream_ctx *str) { + auto& queue = outgoing.at(str->queue_id); + if (queue.pending.handle) { + send(queue.pending, str->queue_id); + queue.pending.handle = nullptr; + } + queue.streams.remove(*str); +} +void Connection::close(in_stream_ctx *str) { + reset(str->stream_id); + incoming.remove(*str); +} + +void Connection::reset(uint64_t id) { + if (cnx != nullptr) { + picoquic_reset_stream(cnx, id, 0); + } +} diff --git a/src/core/net/Context.hpp b/src/core/net/Context.hpp new file mode 100644 index 0000000..deecf2c --- /dev/null +++ b/src/core/net/Context.hpp @@ -0,0 +1,161 @@ +#pragma once + +#include "../../core/net/data.hpp" +#include "../../core/data/mem.hpp" +#include +#include + +namespace net { + +struct stream_ctx { + stream_ctx(uint64_t id): stream_id(id) { } + uint64_t stream_id; + + static bool IsUnidirId(uint64_t id); + static bool IsServerId(uint64_t id); + static bool IsClientId(uint64_t id); + + friend bool operator==(const stream_ctx &a, const stream_ctx &b) { return a.stream_id == b.stream_id; } +}; +struct out_stream_ctx: stream_ctx { + out_stream_ctx(uint64_t id, uint8_t queue_id, const data::out_buffer& buf): + stream_ctx(id), buffer(buf), queue_id(queue_id) { } + + data::out_buffer buffer; + uint8_t queue_id; +}; +struct in_stream_ctx: stream_ctx { + in_stream_ctx(uint64_t id): stream_ctx(id) { } + data::in_vector buffer; +}; +enum class PacketFlags { + NONE = 0, + DATAGRAM = 1 << 0, +}; + +/// Abstract QUIC context +class Context { +public: + static constexpr auto MAX_SOCKET_COUNT = 2; + + Context(uint32_t max_connections, char const *cert, char const *key, + picoquic_stream_data_cb_fn callback, void* callback_ctx, + char const *tickets, char const *tokens); + /// As client + Context(char const* tickets, char const* tokens): + Context(1, NULL, NULL, NULL, NULL, tickets, tokens) { } + /// As server + Context(uint32_t max_connections, char const *cert, char const *key, + picoquic_stream_data_cb_fn callback, void* callback_ctx): + Context(max_connections, cert, key, callback, callback_ctx, NULL, NULL) { } + ~Context(); + + /// Act as pull break + virtual bool isRunning() const = 0; + + constexpr picoquic_quic_t *getHandle() { + assert(quic != nullptr); + return quic; + } + +protected: + /// Start listening + void openSockets(int port, int family); + + /// Read-write on sockets and notify callbacks + void pull(uint64_t maxDelay, uint64_t maxIn, uint64_t maxOut); + +private: + picoquic_quic_t *quic = nullptr; + + char const* ticket_store_filename = nullptr; // MAYBE: for 0-RTT + char const* token_store_filename = nullptr; + + int nb_sockets = 0; + SOCKET_TYPE sockets[MAX_SOCKET_COUNT]; + int sockets_family[MAX_SOCKET_COUNT]; + uint16_t socket_port = 0; +}; + +/// Abstract QUIC connection +class Connection { +public: + Connection(picoquic_cnx_t *cnx, bool is_client, uint8_t queues): + cnx(cnx), is_client(is_client) { + outgoing.resize(queues); + } + ~Connection(); + + /// Setup client connection + void setup(picoquic_quic_t* ctx, sockaddr* addr, const char* sni, picoquic_stream_data_cb_fn cb_fn, void *cb_ctx); + void setHandle(picoquic_cnx_t *c); + + constexpr bool contains(picoquic_cnx_t *c) const { return cnx == c; } + constexpr bool operator==(const Connection &other) const { return other.contains(cnx); } + + /// Close connection + void release(uint16_t reason); + + void setCallback(picoquic_stream_data_cb_fn cb_fn, void *ctx); + + uint16_t getErrorCode(bool is_app); + std::string getAddress(); + + /// Send reliable data + /// ptr is memcpy + void sendCopy(const uint8_t* ptr, size_t size, uint8_t queue = 0, size_t queue_size = 0); + /// Send reliable data + /// take view ownership + void send(const data::out_view &view, uint8_t queue = 0, size_t queue_size = 0); + /// Send reliable data + /// buffer must stay valid until handle is freed + void send(const data::out_buffer& buffer, uint8_t queue = 0, size_t queue_size = 0); + + template + void emit(const D &data) { emit((const uint8_t*)&data, sizeof(D)); } + /// Send unreliable data + void emit(const uint8_t *ptr, size_t size); + + /// Register incoming stream + in_stream_ctx *receive(uint64_t streamId); + + /// Close outgoing stream + void close(out_stream_ctx *str); + /// Close incoming stream + void close(in_stream_ctx *str); + + /// Reset stream + void reset(uint64_t id); + + template + static constexpr size_t GetSize(const C& list) { + size_t res = 0; + for (auto it = list.begin(); it != list.end(); ++it) { + ++res; + }; + return res; + } + size_t queueSize(uint8_t id) const { return GetSize(outgoing.at(id).streams); } + +private: + picoquic_cnx_t *cnx = nullptr; + bool is_client; + + struct queue_ctx { + data::out_buffer pending = data::out_buffer(); + std::forward_list streams; + }; + std::vector outgoing; + uint64_t outgoingCounter = 0; + uint64_t nextOutgoingId() { + return (outgoingCounter++) * 4 + PICOQUIC_STREAM_ID_UNIDIR + (is_client ? + PICOQUIC_STREAM_ID_CLIENT_INITIATED : PICOQUIC_STREAM_ID_SERVER_INITIATED); + } + + std::forward_list incoming; + + // MAYBE: add bidirectional streams + // TODO: premanant streams: background, priority +}; + +} \ No newline at end of file diff --git a/src/core/net/PacketView.hpp b/src/core/net/PacketView.hpp deleted file mode 100644 index 0815a3b..0000000 --- a/src/core/net/PacketView.hpp +++ /dev/null @@ -1,94 +0,0 @@ -#pragma once - -#include "data.hpp" - -struct vec_istream: std::streambuf { - vec_istream(std::vector &vec) { - this->setg(&vec[0], &vec[0], &vec[0] + vec.size()); - } -}; - -namespace net { - -class PacketWriter final { -public: - PacketWriter(size_t size, enet_uint32 flags): p(enet_packet_create(NULL, size, flags)) { } - - void write(const void* data, size_t size) { - assert(i + size <= p->dataLength); - memcpy(p->data + i, data, size); - i += size; - } - template - void write(const D& d) { - write(&d, sizeof(d)); - } - - bool isFull() const { - return i >= p->dataLength; - } - - packet_t *get() { return p; } - -private: - packet_t *p; - size_t i = 0; -}; - -struct data_ref { - void* d; - size_t s; - - void *data() { return d; } - const void *data() const { return d; } - size_t size() const { return s; } -}; - -class PacketReader final { -public: - PacketReader(packet_t* ptr, size_t offset): p(ptr), i(offset) { } - PacketReader(packet_t* ptr, bool broadcasted = false): - PacketReader(ptr, sizeof(enet_uint8) + (broadcasted ? 0 : sizeof(salt_t))) { } - - template - const D* read() { - if (i + sizeof(D) > p->dataLength) - return nullptr; - - auto ptr = (const D *)(p->data + i); - i += sizeof(D); - return ptr; - } - template - bool read(D& out) { - const auto ptr = read(); - if (ptr == nullptr) - return false; - - out = *ptr; - return true; - } - - bool skip(size_t size) { - i += size; - return i < p->dataLength; - } - - data_ref remaning() { - auto res = data_ref{p->data + i, p->dataLength - i}; - i = p->dataLength; - return res; - } - - bool isFull() const { - return i >= p->dataLength; - } - - packet_t *get() { return p; } - -private: - packet_t *p; - size_t i = 0; -}; - -} \ No newline at end of file diff --git a/src/core/net/Server.hpp b/src/core/net/Server.hpp deleted file mode 100644 index 7f32440..0000000 --- a/src/core/net/Server.hpp +++ /dev/null @@ -1,163 +0,0 @@ -#pragma once - -#include "data.hpp" -#include "PacketView.hpp" - -namespace net { - -class Server { -public: - Server(const connection& ct, int connections) { - if(connections > 0) { - auto addr = [&] { - if (auto addr = ct.toAddress()) - return addr.value(); - - FATAL("Invalid ip address format"); - }(); - - host = enet_host_create(&addr, connections, CHANNEL_COUNT, 0, 0); - if(host == nullptr) { - FATAL("Network server creation failed"); - } - - LOG_I("Listening on " << ct); - } else { - host = nullptr; - LOG_D("Local only server"); - } - } - ~Server() { - enet_host_destroy(host); - } - - template - void pull(C onConnect, D onDisconnect, R onData, int delay = 10, int count = 10) { - if (host == nullptr) - return; - - ENetEvent event; - for(int i = 0; i < count && enet_host_service(host, &event, delay) > 0; i++) { - switch(event.type) { - case ENET_EVENT_TYPE_CONNECT: - onConnect(event.peer, event.data); - break; - - case ENET_EVENT_TYPE_DISCONNECT: - onDisconnect(event.peer, (disconnect_reason)event.data); - break; - - case ENET_EVENT_TYPE_RECEIVE: { - onData(event.peer, event.packet, (channel_type)event.channelID); - enet_packet_destroy(event.packet); - break; - } - - case ENET_EVENT_TYPE_NONE: - break; - } - } -#if TRACY_ENABLE - TracyPlot("SrvNetUpData", (int64_t)host->totalSentData); - host->totalSentData = 0; - TracyPlot("SrvNetUpPackets", (int64_t)host->totalSentPackets); - host->totalSentPackets = 0; - TracyPlot("SrvNetDownData", (int64_t)host->totalReceivedData); - host->totalReceivedData = 0; - TracyPlot("SrvNetDownPackets", (int64_t)host->totalReceivedPackets); - host->totalReceivedPackets = 0; - - int64_t throttling = 0; - int64_t peerCount = 0; - iterPeers([&](peer_t *peer) { - throttling += ENET_PEER_PACKET_THROTTLE_SCALE - peer->packetThrottle; - peerCount++; - }); - TracyPlot("SrvPeersCount", peerCount); - TracyPlot("SrvPeersThrottling", peerCount > 0 ? throttling * 100 / ENET_PEER_PACKET_THROTTLE_SCALE / peerCount : 0); -#endif - } - - void disconnect(peer_t *peer, disconnect_reason reason) const { - enet_peer_disconnect(peer, (enet_uint32)reason); - } - - template - static D* GetPeerData(peer_t* peer) { return (D*)peer->data; } - - template - void iterPeers(Call call) { - for (auto currentPeer = host->peers; - currentPeer < &host->peers[host->peerCount]; - ++currentPeer) - { - if (currentPeer->state == ENET_PEER_STATE_CONNECTED) - call(currentPeer); - } - } - - /// Send to single peer - /// Expect salt_t at peer->data - bool sendTo(peer_t* peer, server_packet_type type, const void *data, size_t size, channel_type channel, std::optional flags = {}) { - const auto salt = GetPeerData(peer); - if(salt == NULL) { - LOG_W("Contacting " << peer->address << " before handshake"); - return false; - } - return send(peer, makePacket(type, data, size, flags.value_or(channel == channel_type::RELIABLE ? ENET_PACKET_FLAG_RELIABLE : 0), *salt).get(), channel); - } - template - bool sendTo(peer_t* peer, server_packet_type type, const D& data, channel_type channel, std::optional flags = {}) { - static_assert(!std::is_pointer::value); - return sendTo(peer, type, &data, sizeof(data), channel, flags); - } - - /// Send unsalt to single peer - bool send(peer_t* peer, packet_t* packet, channel_type channel) { - return enet_peer_send(peer, (enet_uint8)channel, packet) == 0; - } - bool send(peer_t* peer, server_packet_type type, const void *data, size_t size, channel_type channel, std::optional flags = {}) { - return send(peer, makePacket(type, data, size, flags.value_or(channel == channel_type::RELIABLE ? ENET_PACKET_FLAG_RELIABLE : 0)).get(), channel); - } - template - bool send(peer_t* peer, server_packet_type type, const D& data, channel_type channel, std::optional flags = {}) { - static_assert(!std::is_pointer::value); - return send(peer, type, &data, sizeof(data), channel, flags); - } - - /// Send to all connected peers - void broadcast(packet_t* packet, channel_type channel) { - if (host != nullptr) - enet_host_broadcast(host, (enet_uint8)channel, packet); - } - void broadcast(server_packet_type type, const void *data, size_t size, channel_type channel, std::optional flags = {}) { - broadcast(makePacket(type, data, size, flags.value_or(channel == channel_type::RELIABLE ? ENET_PACKET_FLAG_RELIABLE : 0)).get(), channel); - } - template - void broadcast(server_packet_type type, const D& data, channel_type channel, std::optional flags = {}) { - static_assert(!std::is_pointer::value); - broadcast(type, &data, sizeof(data), channel, flags); - } - - static PacketWriter makePacket(server_packet_type type, const void* data, size_t size, enet_uint32 flags) { - assert(type >= server_packet_type::BROADCASTED); - auto packet = PacketWriter(sizeof(server_packet_type) + size, flags); - packet.write(type); - if (data != nullptr) - packet.write(data, size); - return packet; - } - static PacketWriter makePacket(server_packet_type type, const void* data, size_t size, enet_uint32 flags, salt_t salt) { - assert(type < server_packet_type::BROADCASTED); - auto packet = PacketWriter(sizeof(server_packet_type) + sizeof(salt_t) + size, flags); - packet.write(type); - packet.write(salt); - if (data != nullptr) - packet.write(data, size); - return packet; - } - -private: - ENetHost *host; -}; -} \ No newline at end of file diff --git a/src/core/net/data.hpp b/src/core/net/data.hpp index 3439285..3509f51 100644 --- a/src/core/net/data.hpp +++ b/src/core/net/data.hpp @@ -1,44 +1,42 @@ #pragma once #include #include -#include #include "../utils/logger.hpp" +#include namespace net { -constexpr auto TIMEOUT = 5000; -constexpr auto CHANNEL_COUNT = 2; -enum class channel_type: enet_uint8 { - RELIABLE = 0, - NOTIFY = 1, -}; +/// Application Protocol Negotiation +constexpr auto ALPN = "univerxel_quic"; -enum class disconnect_reason: enet_uint32 { +enum class disconnect_reason: uint16_t { UNEXPECTED = 0, + // Client quit QUIT = 1, - WRONG_SALT = 15, + // Server close + CLOSE = 2, + // Server full + FULL = 3, + PROTOCOL_VIOLATION = 15, }; -using salt_t = enet_uint32; -using peer_t = ENetPeer; -using packet_t = ENetPacket; +enum class server_packet_type: uint8_t { + /// MAYBE: HELLO = 0, + + /// Must close connection + /// bool(is_application) reason(uint16_t) + QUIT = UINT8_MAX, -enum class server_packet_type: enet_uint8 { - PERSONAL = 0, - /// Get server salt - /// reliable - CHALLENGE = 0, /// Set client position and instance id - /// size_t(playerId) voxel_pos reliable + /// size_t(playerId) voxel_pos TELEPORT = 1, - BROADCASTED = 16, /// List all areas - /// {area_id, world::Area::params}[] reliable + /// {area_id, world::Area::params}[] AREAS = 16, /// Full chunk update - /// {area_, zstd} reliable - /// empty: all sent reliable + /// {area_, zstd} + /// empty: all sent CHUNK = 17, /// Chunk changes /// {area_id, {chunk_pos, ushort(count), Chunk::Edit[]}[]} notify @@ -46,84 +44,69 @@ enum class server_packet_type: enet_uint8 { EDITS = 18, /// Declare entities types - /// {size_t(index), vec3(size), vec3(scale)} reliable + /// {size_t(index), vec3(size), vec3(scale)} /// TODO: zstd ENTITY_TYPES = 20, /// Update entities instances position and velocity - /// {size_t(entity), size_t(count), {size_t(index), ifvec3(pos), vec3(velocity)}[]}[] notify + /// {size_t(entity), size_t(count), {size_t(index), ifvec3(pos), vec3(velocity)}[]}[] + /// FIXME: fromat to fit PICOQUIC_MAX_PACKET_SIZE ENTITIES = 21, /// World compression dictionary - /// zstd dict reliable + /// zstd dict COMPRESSION = 24, /// Server capabilities - /// ushort(loadDistance), MAYBE: more reliable + /// ushort(loadDistance), MAYBE: more CAPABILITIES = 25, /// Public chat message - /// char[] (not null terminated) reliable + /// char[] (not null terminated) MESSAGE = 29, }; -enum class client_packet_type: enet_uint8 { +enum class client_packet_type: uint8_t { /// Interact with voxels - /// actions::FillShape reliable + /// actions::FillShape FILL_SHAPE = 0, /// Request missing chunks - /// area_id, chunk_pos[max: MAX_PENDING_CHUNK_COUNT] reliable + /// area_id, chunk_pos[max: MAX_PENDING_CHUNK_COUNT] MISSING_CHUNKS = 8, /// Send public text message - /// char[] (not null terminated) reliable + /// char[] (not null terminated) MESSAGE = 9, - /// Position update - /// voxel_pos notify + /// Position update (unreliable) + /// (sequence)uint64_t voxel_pos MOVE = 16, }; constexpr auto MAX_PENDING_CHUNK_COUNT = 256; -struct connection { - std::string address; +struct address { + std::string host; int port; - std::optional toAddress() const { - ENetAddress addr; - addr.port = port; - if(enet_address_set_host(&addr, address.c_str()) != 0) + /// Return address_storage(ip+port) and isName + std::optional> toAddress() const { + std::pair res; + int isName = 0; + if (picoquic_get_server_address(host.c_str(), port, &res.first, &isName) != 0) return {}; - return std::make_optional(addr); + res.second = isName; + return res; } - friend inline std::ostream& operator<<(std::ostream& os, const connection& ct) { - return os << ct.address << ':' << ct.port; + friend inline std::ostream& operator<<(std::ostream& os, const address& ct) { + return os << ct.host << ':' << ct.port; } }; - -inline std::ostream &operator<<(std::ostream &os, const ENetAddress &addr) { - constexpr auto LEN = 16; - char *buf = (char*)enet_malloc(LEN); - if(enet_address_get_host_ip(&addr, buf, LEN) == 0) { - os << buf; - } else { - os << addr.host; - } - enet_free(buf); - os << ':' << addr.port; - return os; -} - -void inline Setup() { - if(enet_initialize() != 0) { - FATAL("Enet initialization failed"); - } - std::srand(enet_host_random_seed()); -} -void inline Destroy() { - enet_deinitialize(); -} +struct exposure: address { + uint32_t max_connections; + std::string key; + std::string cert; +}; } \ No newline at end of file diff --git a/src/core/net/io.hpp b/src/core/net/io.hpp new file mode 100644 index 0000000..dc82b65 --- /dev/null +++ b/src/core/net/io.hpp @@ -0,0 +1,107 @@ +#pragma once + +#include "data.hpp" +#include "../data/mem.hpp" + +namespace net { + +//TODO: preallocate static data pool +class PacketWriter final { +public: + PacketWriter(size_t size): buffer((uint8_t*)malloc(size), size) { } + PacketWriter(uint8_t type, size_t data_size): PacketWriter(sizeof(type) + data_size) { + write(type); + } + PacketWriter(net::client_packet_type type, size_t data_size): PacketWriter((uint8_t)type, data_size) { } + PacketWriter(net::server_packet_type type, size_t data_size): PacketWriter((uint8_t)type, data_size) { } + ~PacketWriter() { + if (buffer.ptr != nullptr) + free(buffer.ptr); + } + + void write(const void* data, size_t len) { + buffer.write((uint8_t*)data, len); + } + template + void write(const D& d) { + write(&d, sizeof(d)); + } + + bool isFull() const { + return buffer.isDone(); + } + + [[nodiscard]] + data::out_buffer finish() { + assert(isFull()); + data::out_buffer cpy(buffer.ptr, buffer.siz); + buffer.ptr = nullptr; + buffer.siz = 0; + return cpy; + } + + [[nodiscard]] + static data::out_buffer Of(net::client_packet_type type, const void* data, size_t size) { + auto packet = PacketWriter(type, size); + packet.write(data, size); + return packet.finish(); + } + [[nodiscard]] + static data::out_buffer Of(net::server_packet_type type, const void* data, size_t size) { + auto packet = PacketWriter(type, size); + packet.write(data, size); + return packet.finish(); + } + template + [[nodiscard]] + static data::out_buffer Of(net::client_packet_type type, const D& data) { + return Of(type, &data, sizeof(data)); + } + template + [[nodiscard]] + static data::out_buffer Of(net::server_packet_type type, const D& data) { + return Of(type, &data, sizeof(data)); + } + +private: + data::in_view buffer; +}; + +class PacketReader final { +public: + PacketReader(const data::out_view& buf): buffer(buf) { } + + template + const D* read() { + return buffer.cur + sizeof(D) <= buffer.size() ? + (const D*)buffer.readFrom(sizeof(D)) : nullptr; + } + template + bool read(D& out) { + const auto ptr = read(); + if (ptr == nullptr) + return false; + + out = *ptr; + return true; + } + + bool skip(size_t size) { + buffer.readFrom(size); + return !isFull(); + } + + data::out_view readAll() { + const size_t remain = buffer.size() - buffer.cur; + return data::out_view(buffer.readFrom(remain), remain); + } + + bool isFull() const { + return buffer.isDone(); + } + +private: + data::out_view buffer; +}; + +} \ No newline at end of file diff --git a/src/core/world/raycast.hpp b/src/core/world/raycast.hpp index 1d353b3..cf5f025 100644 --- a/src/core/world/raycast.hpp +++ b/src/core/world/raycast.hpp @@ -1,6 +1,7 @@ #pragma once #include "Universe.hpp" +#include "Chunk.hpp" namespace world { diff --git a/src/main.cpp b/src/main.cpp index 5b3c2f1..32e1004 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -39,8 +39,6 @@ int main(int argc, char *argv[]){ client.value().run(handle); }; - net::Setup(); - if (server.has_value()) { if (client.has_value()) { auto serverThread = std::thread(serverTask); @@ -61,8 +59,6 @@ int main(int argc, char *argv[]){ } } - net::Destroy(); - options.save(); return 0; diff --git a/src/server.cpp b/src/server.cpp index f2debb2..184b3c2 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -26,14 +26,10 @@ int main(int argc, char *argv[]){ #if TRACY_ENABLE tracy::SetThreadName("Main"); #endif - - net::Setup(); auto server = Server(options.get()); server.run(); - net::Destroy(); - options.save(); return 0; diff --git a/src/server/Server.cpp b/src/server/Server.cpp index 7526335..f5bcdf3 100644 --- a/src/server/Server.cpp +++ b/src/server/Server.cpp @@ -35,6 +35,7 @@ void Server::run() { universe->update(deltaTime.count()); FrameMarkEnd("Server"); while (std::chrono::steady_clock::now() < startTick + std::chrono::milliseconds(1000 / TPS)) { + universe->pull(); std::this_thread::sleep_for(std::chrono::milliseconds(1)); } lastTick = startTick; diff --git a/src/server/config.hpp b/src/server/config.hpp index c9ea5bc..72aa2fc 100644 --- a/src/server/config.hpp +++ b/src/server/config.hpp @@ -15,9 +15,11 @@ public: allowLocal = false; #endif - world.connection.address = config["connection"]["address"].value_or(world.connection.address); + world.connection.host = config["connection"]["hosts"].value_or(world.connection.host); world.connection.port = config["connection"]["port"].value_or(world.connection.port); - world.maxPlayers = config["max_players"].value_or(world.maxPlayers); + world.connection.key = config["connection"]["key"].value_or(world.connection.key); + world.connection.cert = config["connection"]["cert"].value_or(world.connection.cert); + world.connection.max_connections = config["max_players"].value_or(world.connection.max_connections); world.loadDistance = config["world"]["load_distance"].value_or(world.loadDistance); world.keepDistance = config["world"]["keep_distance"].value_or(world.keepDistance); @@ -31,10 +33,12 @@ public: {"allow_local", allowLocal}, #endif {"connection", toml::table({ - {"address", world.connection.address}, - {"port", world.connection.port} + {"hosts", world.connection.host}, + {"port", world.connection.port}, + {"key", world.connection.key}, + {"cert", world.connection.cert} })}, - {"max_players", world.maxPlayers}, + {"max_players", world.connection.max_connections}, {"world", toml::table({ {"load_distance", world.loadDistance}, {"keep_distance", world.keepDistance}, diff --git a/src/server/net/Server.cpp b/src/server/net/Server.cpp new file mode 100644 index 0000000..12d1251 --- /dev/null +++ b/src/server/net/Server.cpp @@ -0,0 +1,190 @@ +#include "Server.hpp" + +using namespace net::server; + +namespace net::server { + int connection_callback(picoquic_cnx_t* cnx, + uint64_t stream_id, uint8_t* bytes, size_t length, + picoquic_call_back_event_t fin_or_event, void* callback_ctx, void* v_stream_ctx) { + + if (fin_or_event == picoquic_callback_almost_ready) + return 0; //NOTE: only use full connections + + auto server = (Server*)picoquic_get_default_callback_context(picoquic_get_quic_ctx(cnx)); + auto peer = (Peer*)callback_ctx; + if (peer == nullptr || peer == (void*)server) { + assert(fin_or_event == picoquic_callback_ready); + peer = server->connect(cnx); + picoquic_set_callback(cnx, connection_callback, peer); + } + assert(peer == nullptr || peer->contains(cnx) || fin_or_event == picoquic_callback_close); + assert(v_stream_ctx == nullptr || ((net::stream_ctx*)v_stream_ctx)->stream_id == stream_id); + + return server->connectionCallback(peer, stream_id, bytes, length, fin_or_event, v_stream_ctx); + } +} + +Server::Server(const exposure& ct, + std::function(Peer*)> onConnect, + std::function onDisconnect, + std::function onPacket): + Context(ct.max_connections, ct.cert.c_str(), ct.key.c_str(), connection_callback, this), + onConnect(onConnect), onDisconnect(onDisconnect), onPacket(onPacket), max_connections(ct.max_connections) +{ + if (ct.max_connections > 0) { + openSockets(ct.port, 0); + LOG_I("Listening on port " << ct.port); + } +} +Server::~Server() { + for(auto& peer: peers) { + constexpr auto reason = (uint16_t)disconnect_reason::CLOSE; + if (onDisconnect(&peer, true, reason)) + peer.release(reason); + } + peers.clear(); +} + +void Server::emitBroadcast(const uint8_t* ptr, size_t size) { + for(auto& peer: peers) { + peer.emit(ptr, size); + } +} + +int Server::connectionCallback(Peer* peer, uint64_t stream_id, uint8_t* bytes, size_t length, + picoquic_call_back_event_t fin_or_event, void* v_stream_ctx) { + + switch (fin_or_event) { + case picoquic_callback_stream_data: + case picoquic_callback_stream_fin: { + assert(stream_ctx::IsClientId(stream_id)); + auto stream_ctx = (in_stream_ctx *)v_stream_ctx; + + /* Data arrival on stream #x, maybe with fin mark */ + if (stream_ctx == NULL) { // New stream from peer + stream_ctx = peer->receive(stream_id); + } + + if (length > 0) { + stream_ctx->buffer.write(bytes, length); + } + + if (fin_or_event == picoquic_callback_stream_fin) { + if (onPacket(peer, data::out_view(stream_ctx->buffer.data.data(), stream_ctx->buffer.data.size()), PacketFlags::NONE)) { + peer->close(stream_ctx); + } else { + return -1; + } + } + break; + } + + case picoquic_callback_datagram: + if(!onPacket(peer, data::out_view(bytes, length), PacketFlags::DATAGRAM)) { + return -1; + } + break; + + case picoquic_callback_stop_sending: /* Should not happen, treated as reset */ + /* Mark stream as abandoned, close the file, etc. */ + peer->reset(stream_id); + /* Fall through */ + case picoquic_callback_stream_reset: /* Server reset stream #x */ { + assert(stream_ctx::IsUnidirId(stream_id)); + + // auto remote_error = picoquic_get_remote_stream_error(cnx, stream_id); + //FIXME: if remote_error callback onError(remote_error) + if (stream_ctx::IsClientId(stream_id)) { + auto stream_ctx = (in_stream_ctx*)v_stream_ctx; + if (stream_ctx == NULL) { + peer->close(stream_ctx); + } + } else { + auto stream_ctx = (out_stream_ctx*)v_stream_ctx; + if (stream_ctx == NULL) { + peer->close(stream_ctx); + } + } + break; + } + + case picoquic_callback_close: + case picoquic_callback_stateless_reset: + case picoquic_callback_application_close: /* Received application close */ { + if (peer == nullptr) + break; + + const auto is_app = fin_or_event == picoquic_callback_application_close; + if (onDisconnect(peer, is_app, peer->getErrorCode(is_app))) { + peer->setCallback(NULL, NULL); + peers.remove(*peer); + } + break; + } + case picoquic_callback_version_negotiation: + /* The server should never receive a version negotiation response */ + break; + case picoquic_callback_stream_gap: + /* This callback is never used. */ + break; + case picoquic_callback_prepare_to_send: { + /* Active sending API */ + assert(stream_ctx::IsServerId(stream_id)); + + auto stream_ctx = (out_stream_ctx*)v_stream_ctx; + if (stream_ctx == NULL) { + return -1; + } + + if (!stream_ctx->buffer.isDone()) { + size_t available = stream_ctx->buffer.remaining(); + int is_fin = 1; + + /* The length parameter marks the space available in the packet */ + if (available > length) { + available = length; + is_fin = 0; + } + uint8_t* buffer = picoquic_provide_stream_data_buffer(bytes, available, is_fin, !is_fin); + if (buffer == NULL) { + return -1; + } + + // MAYBE: try zero copy + stream_ctx->buffer.read(buffer, available); + } else { + LOG_W("Out stream reset"); + peer->reset(stream_id); + } + if (stream_ctx->buffer.isDone()) { + //MAYBE: reuse + peer->close(stream_ctx); + } + break; + } + case picoquic_callback_almost_ready: + // NOTE: handled is static callback + // MAYBE: use pre-connect for server status + break; + case picoquic_callback_ready: + /* TODO: Check that the transport parameters are what the sample expects */ + if (auto reason = onConnect(peer)) { + peer->release(reason.value()); + peers.remove(*peer); + return -1; + } + break; + default: + //MAYBE: picoquic_callback_request_alpn_list + //MAYBE: picoquic_callback_set_alpn + //MAYBE: picoquic_callback_pacing_changed + /* unexpected -- just ignore. */ + break; + } + + return 0; +} + +Peer *Server::connect(picoquic_cnx_t * cnx) { + return &peers.emplace_front(cnx); +} diff --git a/src/server/net/Server.hpp b/src/server/net/Server.hpp new file mode 100644 index 0000000..2192fb9 --- /dev/null +++ b/src/server/net/Server.hpp @@ -0,0 +1,72 @@ +#pragma once + +#include "../../core/net/Context.hpp" + +#include + +namespace net::server { + +enum queue: uint8_t { + GLOBAL = 0, + ENTITY, + CHUNK, + count +}; + +class Peer final: public Connection { +public: + Peer(picoquic_cnx_t *cnx): Connection(cnx, false, queue::count) { } + ~Peer() { assert(ctx == nullptr); } + + template + constexpr C *getCtx() { + assert(ctx != nullptr); + return (C *)ctx; + } + void *ctx = nullptr; +}; +class Server final: Context { +public: + Server(const exposure& ct, + std::function(Peer*)> onConnect, + std::function onDisconnect, + std::function onPacket); + ~Server(); + + /// Send unreliable data to all peers + void emitBroadcast(const uint8_t *ptr, size_t size); + /// Send reliable data to all peers + void sendBroadcast(const data::out_buffer& buffer, uint8_t queue = 0, size_t queue_size = 0) { + iterPeers([&](Peer* peer){ peer->send(buffer, queue, queue_size); }); + } + + /// Register peer or return nullptr + Peer *connect(picoquic_cnx_t *); + + /// Read-write on sockets and notify callbacks + void pull() { Context::pull(1000, 50, 20); } + + int connectionCallback(Peer* client, uint64_t stream_id, uint8_t *bytes, size_t length, + picoquic_call_back_event_t fin_or_event, void *v_stream_ctx); + + bool isRunning() const { return true; } + constexpr bool isFull(uint32_t val) const { return val >= max_connections; } + + template + void iterPeers(C call) { + for(auto& peer: peers) { + call(&peer); + } + } + +private: + std::forward_list peers; + + std::function(Peer *)> onConnect; + std::function onDisconnect; + std::function onPacket; + + uint32_t max_connections; +}; + +} \ No newline at end of file diff --git a/src/server/world/Universe.cpp b/src/server/world/Universe.cpp index b89337e..eaee429 100644 --- a/src/server/world/Universe.cpp +++ b/src/server/world/Universe.cpp @@ -9,14 +9,18 @@ #include "../../core/world/raycast.hpp" #include "../../core/world/iterators.hpp" #include "../../core/world/actions.hpp" -#include "../../core/net/PacketView.hpp" +#include "../../core/net/io.hpp" using namespace world::server; const auto AREAS_FILE = "/areas.idx"; -Universe::Universe(const Universe::options &options): host(options.connection, options.maxPlayers), - dict_content({options.folderPath + "/zstd.dict", "content/zstd.dict"}), dicts(dict_content), dict_write_ctx(dicts.make_writer()) { +Universe::Universe(const Universe::options &options): host(options.connection, + [&](net::server::Peer* peer) { return onConnect(peer); }, + [&](net::server::Peer* peer, bool is_app, uint16_t reason) { return onDisconnect(peer, is_app, reason); }, + [&](net::server::Peer* peer, const data::out_view &buf, net::PacketFlags flags) { return onPacket(peer, buf, flags); } + ), dict_content({options.folderPath + "/zstd.dict", "content/zstd.dict"}), dicts(dict_content), dict_write_ctx(dicts.make_writer()) +{ setOptions(options); folderPath = options.folderPath; running = true; @@ -120,8 +124,7 @@ Universe::~Universe() { } struct net_client { - net_client(net::salt_t salt, data::generational::id id): salt(salt), instanceId(id) { } - net::salt_t salt; + net_client(data::generational::id id): instanceId(id) { } data::generational::id instanceId; std::vector, long>> pendingChunks; @@ -211,11 +214,34 @@ void Universe::saveAreas() const { index.close(); } +void Universe::pull() { + ZoneScopedN("Network"); + host.pull(); + host.iterPeers([&](net::server::Peer *peer) { + auto data = peer->getCtx(); + if (data == nullptr || data->pendingChunks.empty()) + return; + + //TODO: use congestion + area_ pending; + size_t i = peer->queueSize(net::server::queue::CHUNK); + while(i <= 4 && data->popPendingChunk(pending)) { + if (const auto it = areas.find(pending.first); it != areas.end()) { + if (const auto chunk = it->second->getChunks().findInRange(pending.second)) { + peer->send(serializeChunk(pending, std::dynamic_pointer_cast(chunk.value())), net::server::queue::CHUNK); + i++; + } + } + } + + if (data->pendingChunks.empty()) + peer->send(net::PacketWriter(net::server_packet_type::CHUNK, 0).finish()); //FIXME: must be received after last chunk + }); +} + void Universe::update(float deltaTime) { ZoneScopedN("Universe"); - pullNetwork(); - std::vector moves; { moves.reserve(movedPlayers.size()); @@ -390,7 +416,9 @@ void Universe::update(float deltaTime) { constexpr auto CAT_SIZE = sizeof(entity_id::index) + sizeof(size_t); constexpr auto ITEM_SIZE = sizeof(entity_id::index) + sizeof(glm::ifvec3) + sizeof(glm::vec3); - auto packet = net::Server::makePacket(net::server_packet_type::ENTITIES, NULL, CAT_SIZE * entities.size() + ITEM_SIZE * item_count, 0); + //TODO: only moved (pos, vel modified) + //TODO: reduce rate with distance + auto packet = net::PacketWriter(net::server_packet_type::ENTITIES, CAT_SIZE * entities.size() + ITEM_SIZE * item_count); entities.iter([&](entity_id id, const Entity &entity) { packet.write(id.index); packet.write(entity.instances.size()); @@ -400,9 +428,7 @@ void Universe::update(float deltaTime) { packet.write(inst.velocity); }); }); - - assert(packet.isFull()); - host.broadcast(packet.get(), net::channel_type::RELIABLE); + host.sendBroadcast(packet.finish(), net::server::queue::ENTITY, 1); } { // Store loaded chunks @@ -413,199 +439,163 @@ void Universe::update(float deltaTime) { it->second->setChunks().emplace(loaded.first.second, loaded.second); const auto areaOffset = glm::divide(it->second->getOffset().as_voxel()); loadChunk(loaded.first, areaOffset, it->second->getChunks()); - host.iterPeers([&](net::peer_t *peer) { - if (peer->data == nullptr) - return; - - auto data = net::Server::GetPeerData(peer); - if(auto entity = findEntity(PLAYER_ENTITY_ID, data->instanceId)) { - const auto dist = glm::length2(glm::divide(entity->pos.as_voxel()) - areaOffset - loaded.first.second); - if(dist <= glm::pow2(loadDistance)) - data->pushChunk(loaded.first, dist); - } - }); } } } - host.iterPeers([&](net::peer_t *peer) { - if (peer->data == nullptr) - return; - - auto data = net::Server::GetPeerData(peer); - if (data->pendingChunks.empty()) - return; - - if (peer->packetThrottle > 3 * ENET_PEER_PACKET_THROTTLE_SCALE / 4) - data->chunkEmitRate = std::min(UINT32_MAX / 2, data->chunkEmitRate + 1 + (data->chunkEmitRate >> 6)); - else if (peer->packetThrottleEpoch != data->chunkRateEpoch) { - data->chunkRateEpoch = peer->packetThrottleEpoch; - data->chunkEmitRate /= 2; - } - - area_ pending; - size_t i = 0; - while(peer->outgoingDataTotal < data->chunkEmitRate && data->popPendingChunk(pending)) { - if (const auto it = areas.find(pending.first); it != areas.end()) { - if (const auto chunk = it->second->getChunks().findInRange(pending.second)) { - host.send(peer, serializeChunk(pending, std::dynamic_pointer_cast(chunk.value())), net::channel_type::RELIABLE); - i++; - } - } - } - peer->incomingBandwidth = 0; - peer->outgoingDataTotal = 0; - - if (data->pendingChunks.empty()) - host.send(peer, net::server_packet_type::CHUNK, nullptr, 0, net::channel_type::RELIABLE); - }); } -void Universe::pullNetwork() { - ZoneScopedN("Network"); - using namespace net; - host.pull( - [&](peer_t *peer, salt_t salt) { - ZoneScopedN("Connect"); - LOG_I("Client connect from " << peer->address); - net_client* client = new net_client(salt, entities.at(PLAYER_ENTITY_ID).instances.emplace(Entity::Instance{spawnPoint, glm::vec3(0)})); - peer->data = client; +std::optional Universe::onConnect(net::server::Peer* peer) { + ZoneScopedN("Connect"); + LOG_I("Client connect from " << peer->getAddress()); + net_client* client = new net_client(entities.at(PLAYER_ENTITY_ID).instances.emplace(Entity::Instance{spawnPoint, glm::vec3(0)})); + peer->ctx = client; + if (host.isFull(client->instanceId.index)) { + LOG_W("Server is full"); + return (uint16_t)net::disconnect_reason::FULL; + } - const salt_t rnd = std::rand(); - host.sendTo(peer, server_packet_type::CHALLENGE, rnd, channel_type::RELIABLE); - client->salt = salt ^ rnd; + peer->send(net::PacketWriter::Of(net::server_packet_type::CAPABILITIES, loadDistance)); - host.send(peer, server_packet_type::CAPABILITIES, loadDistance, channel_type::RELIABLE); - host.send(peer, server_packet_type::COMPRESSION, dict_content.data(), dict_content.size(), channel_type::RELIABLE); - { - auto player = findEntity(PLAYER_ENTITY_ID, client->instanceId); - struct tp { size_t bindEntity; voxel_pos position; }; - host.sendTo(peer, server_packet_type::TELEPORT, tp{client->instanceId.index, player->pos.as_voxel()}, channel_type::RELIABLE); - movedPlayers.insert(client->instanceId); - } - { - constexpr auto ITEM_SIZE = sizeof(entity_id::index) + sizeof(glm::vec3) * 2; - auto packet = net::Server::makePacket(net::server_packet_type::ENTITY_TYPES, NULL, ITEM_SIZE * entities.size(), ENET_PACKET_FLAG_RELIABLE); - entities.iter([&](entity_id id, const Entity &entity) { - packet.write(id.index); - packet.write(entity.size); - packet.write(entity.scale); - }); + //TODO: lock while not received + //MAYBE: add net::server_packet_type::COMPRESSION to dict_content: zero copy + peer->send(net::PacketWriter::Of(net::server_packet_type::COMPRESSION, dict_content.data(), dict_content.size()), net::server::queue::CHUNK); - assert(packet.isFull()); - host.broadcast(packet.get(), net::channel_type::RELIABLE); - } - broadcastMessage("> Player" + std::to_string(client->instanceId.index) + " has joined us"); - broadcastAreas(); - }, - [&](peer_t *peer, disconnect_reason reason) { - ZoneScopedN("Disconnect"); - LOG_I("Client disconnect from " << peer->address << " with " << (enet_uint32)reason); - if (const auto data = Server::GetPeerData(peer)) { - broadcastMessage("> Player" + std::to_string(data->instanceId.index) + " has left our universe"); - entities.at(PLAYER_ENTITY_ID).instances.free(data->instanceId); - delete data; - } - }, - [&](peer_t *peer, packet_t* packet, channel_type) { - ZoneScopedN("Data"); - if(packet->dataLength < sizeof(client_packet_type) + sizeof(salt_t)) { - LOG_T("Empty packet from " << peer->address); - return; - } - if (memcmp(peer->data, packet->data + sizeof(client_packet_type), sizeof(salt_t)) != 0) { - LOG_D("Wrong salt from " << peer->address); - host.disconnect(peer, disconnect_reason::WRONG_SALT); - return; - } - - const auto type = static_cast(*packet->data); - switch (type) { - case client_packet_type::MOVE: { - if(voxel_pos pos; !PacketReader(packet).read(pos) || - !movePlayer(Server::GetPeerData(peer)->instanceId, pos)) { - LOG_T("Bad move"); - } - break; - } - case client_packet_type::FILL_SHAPE: { - if(const auto fill = PacketReader(packet).read()) { - //TODO: check ray - if (fill->val.is_solid() && !isAreaFree(fill->pos, world::action::ToGeometry(fill->shape), fill->radius)) { - LOG_T("Entity in solid fill area"); - break; - } - set(fill->pos, fill->radius, fill->shape, fill->val); - //TODO: handle inventory - } else { - LOG_T("Bad fill"); - } - break; - } - case client_packet_type::MESSAGE: { - const auto ref = PacketReader(packet).remaning(); - broadcastMessage("Player" + std::to_string(Server::GetPeerData(peer)->instanceId.index) - + ": " + std::string(static_cast(ref.data()), ref.size())); - break; - } - case client_packet_type::MISSING_CHUNKS: { - auto data = Server::GetPeerData(peer); - if (auto player = findEntity(PLAYER_ENTITY_ID, data->instanceId )) { - const auto pos = player->pos.as_voxel(); - - auto reader = PacketReader(packet); - area_id id = *reader.read(); - if(auto area = areas.find(id); area != areas.end()) { - auto &chunks = area->second->getChunks(); - const chunk_pos areaOffset = glm::divide(pos - area->second->getOffset().as_voxel()); - - for (size_t i = 0; !reader.isFull() && i < MAX_PENDING_CHUNK_COUNT; i++) { - const chunk_pos cpos = *reader.read(); - const auto dist = glm::length2(areaOffset - cpos); - if (dist <= glm::pow2(loadDistance) && chunks.inRange(cpos) && chunks.findInRange(cpos).has_value()) { - data->pushChunk(std::make_pair(id, cpos), dist); - } else { - LOG_T("Request out of range chunk"); - } - } - } else { - LOG_T("Bad chunk request"); - } - } - break; - } - default: - LOG_T("Bad packet from " << peer->address); - break; - } + { + auto player = findEntity(PLAYER_ENTITY_ID, client->instanceId); + auto packet = net::PacketWriter(net::server_packet_type::TELEPORT, sizeof(size_t) + sizeof(voxel_pos)); + packet.write(client->instanceId.index); + packet.write(player->pos.as_voxel()); + peer->send(packet.finish()); + movedPlayers.insert(client->instanceId); + } + { + constexpr auto ITEM_SIZE = sizeof(entity_id::index) + sizeof(glm::vec3) * 2; + auto packet = net::PacketWriter(net::server_packet_type::ENTITY_TYPES, ITEM_SIZE * entities.size()); + entities.iter([&](entity_id id, const Entity &entity) { + packet.write(id.index); + packet.write(entity.size); + packet.write(entity.scale); }); + //MAYBE: sendBroadcast + peer->send(packet.finish(), net::server::queue::ENTITY); + } + broadcastMessage("> Player" + std::to_string(client->instanceId.index) + " has joined us"); + broadcastAreas(); + return std::nullopt; +} +bool Universe::onDisconnect(net::server::Peer *peer, bool is_app, uint16_t reason) { + ZoneScopedN("Disconnect"); + LOG_I((is_app ? "Client disconnect from " : "Client connection lost from ") << peer->getAddress() << " (" << reason << ')'); + if (auto data = peer->getCtx()) { + broadcastMessage("> Player" + std::to_string(data->instanceId.index) + " has left our universe"); + entities.at(PLAYER_ENTITY_ID).instances.free(data->instanceId); + delete data; + peer->ctx = nullptr; + } + return true; +} + +bool Universe::onPacket(net::server::Peer *peer, const data::out_view &buf, net::PacketFlags) { + ZoneScopedN("Packet"); + + using namespace net; + + auto packet = PacketReader(buf); + client_packet_type type; + if(!packet.read(type)) { + LOG_D("Empty packet from " << peer->getAddress()); + return false; + } + + switch (type) { + case client_packet_type::MOVE: { + auto data = peer->getCtx(); + if (voxel_pos pos; !packet.read(pos) || + !movePlayer(data->instanceId, pos)) { + LOG_T("Bad move"); + } + break; + } + case client_packet_type::FILL_SHAPE: { + if(const auto fill = packet.read()) { + //TODO: check ray + if (fill->val.is_solid() && !isAreaFree(fill->pos, world::action::ToGeometry(fill->shape), fill->radius)) { + LOG_T("Entity in solid fill area"); + break; + } + set(fill->pos, fill->radius, fill->shape, fill->val); + //TODO: handle inventory + } else { + LOG_T("Bad fill"); + } + break; + } + case client_packet_type::MESSAGE: { + const auto ref = packet.readAll(); + broadcastMessage("Player" + std::to_string(peer->getCtx()->instanceId.index) + + ": " + std::string((const char*)ref.data(), ref.size())); + break; + } + case client_packet_type::MISSING_CHUNKS: { + auto data = peer->getCtx(); + if (auto player = findEntity(PLAYER_ENTITY_ID, data->instanceId )) { + const auto pos = player->pos.as_voxel(); + + area_id id; + if (!packet.read(id)) + break; + if (auto area = areas.find(id); area != areas.end()) { + auto &chunks = area->second->getChunks(); + const chunk_pos areaOffset = glm::divide(pos - area->second->getOffset().as_voxel()); + + for (size_t i = 0; !packet.isFull() && i < MAX_PENDING_CHUNK_COUNT; i++) { + chunk_pos cpos; + if (!packet.read(cpos)) + break; + const auto dist = glm::length2(areaOffset - cpos); + if (dist <= glm::pow2(loadDistance) && chunks.inRange(cpos) && chunks.findInRange(cpos).has_value()) { + data->pushChunk(std::make_pair(id, cpos), dist); + } else { + LOG_T("Request out of range chunk"); + } + } + } else { + LOG_T("Bad chunk request"); + } + } + break; + } + default: + LOG_T("Bad packet from " << peer->getAddress()); + break; + } + return true; } void Universe::broadcastAreas() { constexpr size_t ITEM_SIZE = sizeof(area_id) + sizeof(world::Area::params); - auto packet = net::Server::makePacket(net::server_packet_type::AREAS, NULL, ITEM_SIZE * areas.size(), ENET_PACKET_FLAG_RELIABLE); + auto packet = net::PacketWriter(net::server_packet_type::AREAS, ITEM_SIZE * areas.size()); for(const auto& area: areas) { const auto params = area.second->getParams(); packet.write(area.first); packet.write(world::Area::params{params.center, params.radius, area.second->getCurvature()}); } - - assert(packet.isFull()); - host.broadcast(packet.get(), net::channel_type::RELIABLE); + host.sendBroadcast(packet.finish()); } -net::packet_t* Universe::serializeChunk(area_ id, const std::shared_ptr &data) { +data::out_buffer Universe::serializeChunk(area_ id, const std::shared_ptr &data) { ZoneScopedN("Chunk"); std::ostringstream out; data->write(out); std::vector buffer; + //FIXME: avoid buffer copy dict_write_ctx.compress(out.str(), buffer); - auto packet = net::Server::makePacket(net::server_packet_type::CHUNK, NULL, sizeof(id) + buffer.size(), ENET_PACKET_FLAG_RELIABLE); + auto packet = net::PacketWriter(net::server_packet_type::CHUNK, sizeof(id) + buffer.size()); packet.write(id); packet.write(buffer.data(), buffer.size()); - assert(packet.isFull()); - return packet.get(); + return packet.finish(); } void Universe::broadcastMessage(const std::string& text) { - host.broadcast(net::server_packet_type::MESSAGE, text.data(), text.size(), net::channel_type::RELIABLE); + host.sendBroadcast(net::PacketWriter::Of(net::server_packet_type::MESSAGE, text.data(), text.size())); } void Universe::updateChunk(area_map::iterator &, world::ChunkContainer::iterator &, chunk_pos, float /*deltaTime*/) {} @@ -678,16 +668,15 @@ world::ItemList Universe::set(const area_& pos, int radius, action::S size += sizeof(chunk_voxel_idx); size += sizeof(Chunk::Edit) * part.second.size(); } - auto packet = net::Server::makePacket(net::server_packet_type::EDITS, NULL, size, 0); + auto packet = net::PacketWriter(net::server_packet_type::EDITS, size); packet.write(pos.first); for(const auto& part: edits) { packet.write(part.first); packet.write(part.second.size()); packet.write(part.second.data(), part.second.size() * sizeof(Chunk::Edit)); } - - assert(packet.isFull()); - host.broadcast(packet.get(), net::channel_type::NOTIFY); + //MAYBE: only in range + host.sendBroadcast(packet.finish(), net::server::queue::CHUNK); } return list; } diff --git a/src/server/world/Universe.hpp b/src/server/world/Universe.hpp index 4adc358..08ad513 100644 --- a/src/server/world/Universe.hpp +++ b/src/server/world/Universe.hpp @@ -8,7 +8,8 @@ #include "../../core/data/safe_queue.hpp" #include "../../core/data/safe_priority_queue.hpp" #include "../../core/data/file.hpp" -#include "../../core/net/Server.hpp" +#include "../../core/data/mem.hpp" +#include "../net/Server.hpp" #include "Area.hpp" namespace buffer { @@ -28,8 +29,7 @@ namespace world::server { /// Storage path std::string folderPath = "world"; - net::connection connection = net::connection{"localhost", 4242}; - int maxPlayers = 1; + net::exposure connection = net::exposure{"", 4242, 1, "content/key.pem", "content/cert.pem"}; }; Universe(const options &); @@ -37,6 +37,9 @@ namespace world::server { /// Update physics void update(float deltaTime); + /// Send-Receive packets + //NOTE: triggers onPacket, onConnect & onDisconnect + void pull(); /// Apply new options void setOptions(const options &); @@ -67,9 +70,11 @@ namespace world::server { void saveAll(bool remove); /// Handle networking requests - void pullNetwork(); + std::optional onConnect(net::server::Peer*); + bool onDisconnect(net::server::Peer*, bool, uint16_t); + bool onPacket(net::server::Peer*, const data::out_view&, net::PacketFlags); void broadcastAreas(); - net::packet_t* serializeChunk(area_, const std::shared_ptr&); + data::out_buffer serializeChunk(area_, const std::shared_ptr&); virtual void broadcastMessage(const std::string &); using area_map = robin_hood::unordered_map>; @@ -105,7 +110,7 @@ namespace world::server { ushort keepDistance; std::string folderPath; - net::Server host; + net::server::Server host; data::file_content dict_content; zstd::dict_set dicts;