pq_crypto 0.3.2 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (328) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +56 -0
  3. data/CHANGELOG.md +62 -0
  4. data/GET_STARTED.md +366 -40
  5. data/README.md +76 -233
  6. data/SECURITY.md +107 -82
  7. data/ext/pqcrypto/extconf.rb +169 -87
  8. data/ext/pqcrypto/mldsa_api.h +1 -48
  9. data/ext/pqcrypto/mlkem_api.h +1 -18
  10. data/ext/pqcrypto/pq_externalmu.c +89 -204
  11. data/ext/pqcrypto/pqcrypto_native_api.h +129 -0
  12. data/ext/pqcrypto/pqcrypto_ruby_secure.c +484 -84
  13. data/ext/pqcrypto/pqcrypto_secure.c +203 -78
  14. data/ext/pqcrypto/pqcrypto_secure.h +53 -14
  15. data/ext/pqcrypto/pqcrypto_version.h +7 -0
  16. data/ext/pqcrypto/randombytes.h +9 -0
  17. data/ext/pqcrypto/vendor/.vendored +10 -5
  18. data/ext/pqcrypto/vendor/mldsa-native/BUILDING.md +105 -0
  19. data/ext/pqcrypto/vendor/mldsa-native/LICENSE +286 -0
  20. data/ext/pqcrypto/vendor/mldsa-native/META.yml +24 -0
  21. data/ext/pqcrypto/vendor/mldsa-native/README.md +221 -0
  22. data/ext/pqcrypto/vendor/mldsa-native/SECURITY.md +8 -0
  23. data/ext/pqcrypto/vendor/mldsa-native/mldsa/mldsa_native.c +721 -0
  24. data/ext/pqcrypto/vendor/mldsa-native/mldsa/mldsa_native.h +975 -0
  25. data/ext/pqcrypto/vendor/mldsa-native/mldsa/mldsa_native_asm.S +724 -0
  26. data/ext/pqcrypto/vendor/mldsa-native/mldsa/mldsa_native_config.h +723 -0
  27. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/cbmc.h +166 -0
  28. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/common.h +321 -0
  29. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/ct.c +21 -0
  30. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/ct.h +385 -0
  31. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/debug.c +73 -0
  32. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/debug.h +130 -0
  33. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/fips202/fips202.c +277 -0
  34. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/fips202/fips202.h +244 -0
  35. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/fips202/fips202x4.c +182 -0
  36. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/fips202/fips202x4.h +117 -0
  37. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/fips202/keccakf1600.c +438 -0
  38. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/fips202/keccakf1600.h +105 -0
  39. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/fips202/native/aarch64/auto.h +71 -0
  40. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/fips202/native/aarch64/src/fips202_native_aarch64.h +62 -0
  41. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/fips202/native/aarch64/src/keccak_f1600_x1_scalar_asm.S +376 -0
  42. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/fips202/native/aarch64/src/keccak_f1600_x1_v84a_asm.S +204 -0
  43. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/fips202/native/aarch64/src/keccak_f1600_x2_v84a_asm.S +259 -0
  44. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/fips202/native/aarch64/src/keccak_f1600_x4_v8a_scalar_hybrid_asm.S +1077 -0
  45. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/fips202/native/aarch64/src/keccak_f1600_x4_v8a_v84a_scalar_hybrid_asm.S +987 -0
  46. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/fips202/native/aarch64/src/keccakf1600_round_constants.c +41 -0
  47. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/fips202/native/aarch64/x1_scalar.h +26 -0
  48. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/fips202/native/aarch64/x1_v84a.h +35 -0
  49. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/fips202/native/aarch64/x2_v84a.h +37 -0
  50. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/fips202/native/aarch64/x4_v8a_scalar.h +27 -0
  51. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/fips202/native/aarch64/x4_v8a_v84a_scalar.h +36 -0
  52. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/fips202/native/api.h +69 -0
  53. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/fips202/native/armv81m/README.md +10 -0
  54. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/fips202/native/armv81m/mve.h +32 -0
  55. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/fips202/native/armv81m/src/fips202_native_armv81m.h +20 -0
  56. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/fips202/native/armv81m/src/keccak_f1600_x4_mve.S +638 -0
  57. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/fips202/native/armv81m/src/keccak_f1600_x4_mve.c +136 -0
  58. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/fips202/native/armv81m/src/keccakf1600_round_constants.c +52 -0
  59. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/fips202/native/auto.h +29 -0
  60. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/fips202/native/x86_64/src/KeccakP_1600_times4_SIMD256.c +488 -0
  61. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/fips202/native/x86_64/src/KeccakP_1600_times4_SIMD256.h +16 -0
  62. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/fips202/native/x86_64/xkcp.h +31 -0
  63. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/aarch64/meta.h +247 -0
  64. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/aarch64/src/aarch64_zetas.c +231 -0
  65. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/aarch64/src/arith_native_aarch64.h +150 -0
  66. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/aarch64/src/intt.S +753 -0
  67. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/aarch64/src/mld_polyvecl_pointwise_acc_montgomery_l4.S +129 -0
  68. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/aarch64/src/mld_polyvecl_pointwise_acc_montgomery_l5.S +145 -0
  69. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/aarch64/src/mld_polyvecl_pointwise_acc_montgomery_l7.S +177 -0
  70. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/aarch64/src/ntt.S +653 -0
  71. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/aarch64/src/pointwise_montgomery.S +79 -0
  72. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/aarch64/src/poly_caddq_asm.S +53 -0
  73. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/aarch64/src/poly_chknorm_asm.S +55 -0
  74. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/aarch64/src/poly_decompose_32_asm.S +85 -0
  75. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/aarch64/src/poly_decompose_88_asm.S +85 -0
  76. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/aarch64/src/poly_use_hint_32_asm.S +102 -0
  77. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/aarch64/src/poly_use_hint_88_asm.S +110 -0
  78. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/aarch64/src/polyz_unpack_17_asm.S +72 -0
  79. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/aarch64/src/polyz_unpack_19_asm.S +69 -0
  80. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/aarch64/src/polyz_unpack_table.c +40 -0
  81. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/aarch64/src/rej_uniform_asm.S +189 -0
  82. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/aarch64/src/rej_uniform_eta2_asm.S +135 -0
  83. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/aarch64/src/rej_uniform_eta4_asm.S +128 -0
  84. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/aarch64/src/rej_uniform_eta_table.c +543 -0
  85. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/aarch64/src/rej_uniform_table.c +62 -0
  86. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/api.h +649 -0
  87. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/meta.h +23 -0
  88. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/x86_64/meta.h +315 -0
  89. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/x86_64/src/arith_native_x86_64.h +124 -0
  90. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/x86_64/src/consts.c +157 -0
  91. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/x86_64/src/consts.h +27 -0
  92. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/x86_64/src/intt.S +2311 -0
  93. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/x86_64/src/ntt.S +2383 -0
  94. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/x86_64/src/nttunpack.S +239 -0
  95. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/x86_64/src/pointwise.S +131 -0
  96. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/x86_64/src/pointwise_acc_l4.S +139 -0
  97. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/x86_64/src/pointwise_acc_l5.S +155 -0
  98. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/x86_64/src/pointwise_acc_l7.S +187 -0
  99. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/x86_64/src/poly_caddq_avx2.c +61 -0
  100. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/x86_64/src/poly_chknorm_avx2.c +52 -0
  101. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/x86_64/src/poly_decompose_32_avx2.c +155 -0
  102. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/x86_64/src/poly_decompose_88_avx2.c +155 -0
  103. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/x86_64/src/poly_use_hint_32_avx2.c +102 -0
  104. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/x86_64/src/poly_use_hint_88_avx2.c +104 -0
  105. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/x86_64/src/polyz_unpack_17_avx2.c +91 -0
  106. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/x86_64/src/polyz_unpack_19_avx2.c +93 -0
  107. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/x86_64/src/rej_uniform_avx2.c +126 -0
  108. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/x86_64/src/rej_uniform_eta2_avx2.c +155 -0
  109. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/x86_64/src/rej_uniform_eta4_avx2.c +139 -0
  110. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/x86_64/src/rej_uniform_table.c +160 -0
  111. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/packing.c +293 -0
  112. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/packing.h +224 -0
  113. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/params.h +77 -0
  114. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/poly.c +991 -0
  115. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/poly.h +393 -0
  116. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/poly_kl.c +946 -0
  117. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/poly_kl.h +360 -0
  118. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/polyvec.c +877 -0
  119. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/polyvec.h +725 -0
  120. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/randombytes.h +26 -0
  121. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/reduce.h +139 -0
  122. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/rounding.h +249 -0
  123. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/sign.c +1511 -0
  124. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/sign.h +806 -0
  125. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/symmetric.h +68 -0
  126. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/sys.h +268 -0
  127. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/zetas.inc +55 -0
  128. data/ext/pqcrypto/vendor/mlkem-native/BUILDING.md +104 -0
  129. data/ext/pqcrypto/vendor/mlkem-native/LICENSE +294 -0
  130. data/ext/pqcrypto/vendor/mlkem-native/META.yml +30 -0
  131. data/ext/pqcrypto/vendor/mlkem-native/README.md +223 -0
  132. data/ext/pqcrypto/vendor/mlkem-native/RELEASE.md +86 -0
  133. data/ext/pqcrypto/vendor/mlkem-native/SECURITY.md +8 -0
  134. data/ext/pqcrypto/vendor/mlkem-native/mlkem/README.md +23 -0
  135. data/ext/pqcrypto/vendor/mlkem-native/mlkem/mlkem_native.c +660 -0
  136. data/ext/pqcrypto/vendor/mlkem-native/mlkem/mlkem_native.h +538 -0
  137. data/ext/pqcrypto/vendor/mlkem-native/mlkem/mlkem_native_asm.S +681 -0
  138. data/ext/pqcrypto/vendor/mlkem-native/mlkem/mlkem_native_config.h +709 -0
  139. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/cbmc.h +174 -0
  140. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/common.h +274 -0
  141. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/compress.c +717 -0
  142. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/compress.h +688 -0
  143. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/debug.c +64 -0
  144. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/debug.h +128 -0
  145. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/fips202/fips202.c +251 -0
  146. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/fips202/fips202.h +158 -0
  147. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/fips202/fips202x4.c +208 -0
  148. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/fips202/fips202x4.h +80 -0
  149. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/fips202/keccakf1600.c +463 -0
  150. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/fips202/keccakf1600.h +98 -0
  151. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/fips202/native/aarch64/auto.h +70 -0
  152. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/fips202/native/aarch64/src/fips202_native_aarch64.h +69 -0
  153. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/fips202/native/aarch64/src/keccak_f1600_x1_scalar_asm.S +375 -0
  154. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/fips202/native/aarch64/src/keccak_f1600_x1_v84a_asm.S +203 -0
  155. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/fips202/native/aarch64/src/keccak_f1600_x2_v84a_asm.S +258 -0
  156. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/fips202/native/aarch64/src/keccak_f1600_x4_v8a_scalar_hybrid_asm.S +1076 -0
  157. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/fips202/native/aarch64/src/keccak_f1600_x4_v8a_v84a_scalar_hybrid_asm.S +986 -0
  158. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/fips202/native/aarch64/src/keccakf1600_round_constants.c +46 -0
  159. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/fips202/native/aarch64/x1_scalar.h +25 -0
  160. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/fips202/native/aarch64/x1_v84a.h +34 -0
  161. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/fips202/native/aarch64/x2_v84a.h +35 -0
  162. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/fips202/native/aarch64/x4_v8a_scalar.h +26 -0
  163. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/fips202/native/aarch64/x4_v8a_v84a_scalar.h +35 -0
  164. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/fips202/native/api.h +117 -0
  165. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/fips202/native/armv81m/README.md +10 -0
  166. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/fips202/native/armv81m/mve.h +79 -0
  167. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/fips202/native/armv81m/src/fips202_native_armv81m.h +35 -0
  168. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/fips202/native/armv81m/src/keccak_f1600_x4_mve.S +667 -0
  169. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/fips202/native/armv81m/src/keccak_f1600_x4_mve.c +40 -0
  170. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/fips202/native/armv81m/src/keccakf1600_round_constants.c +51 -0
  171. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/fips202/native/armv81m/src/state_extract_bytes_x4_mve.S +290 -0
  172. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/fips202/native/armv81m/src/state_xor_bytes_x4_mve.S +314 -0
  173. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/fips202/native/auto.h +28 -0
  174. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/fips202/native/x86_64/keccak_f1600_x4_avx2.h +33 -0
  175. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/fips202/native/x86_64/src/fips202_native_x86_64.h +41 -0
  176. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/fips202/native/x86_64/src/keccak_f1600_x4_avx2.S +451 -0
  177. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/fips202/native/x86_64/src/keccakf1600_constants.c +51 -0
  178. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/indcpa.c +622 -0
  179. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/indcpa.h +156 -0
  180. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/kem.c +446 -0
  181. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/kem.h +326 -0
  182. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/native/aarch64/README.md +16 -0
  183. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/native/aarch64/meta.h +122 -0
  184. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/native/aarch64/src/aarch64_zetas.c +174 -0
  185. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/native/aarch64/src/arith_native_aarch64.h +177 -0
  186. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/native/aarch64/src/intt.S +628 -0
  187. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/native/aarch64/src/ntt.S +562 -0
  188. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/native/aarch64/src/poly_mulcache_compute_asm.S +127 -0
  189. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/native/aarch64/src/poly_reduce_asm.S +150 -0
  190. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/native/aarch64/src/poly_tobytes_asm.S +117 -0
  191. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/native/aarch64/src/poly_tomont_asm.S +98 -0
  192. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/native/aarch64/src/polyvec_basemul_acc_montgomery_cached_asm_k2.S +261 -0
  193. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/native/aarch64/src/polyvec_basemul_acc_montgomery_cached_asm_k3.S +314 -0
  194. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/native/aarch64/src/polyvec_basemul_acc_montgomery_cached_asm_k4.S +368 -0
  195. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/native/aarch64/src/rej_uniform_asm.S +226 -0
  196. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/native/aarch64/src/rej_uniform_table.c +542 -0
  197. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/native/api.h +637 -0
  198. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/native/meta.h +25 -0
  199. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/native/riscv64/README.md +11 -0
  200. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/native/riscv64/meta.h +128 -0
  201. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/native/riscv64/src/arith_native_riscv64.h +45 -0
  202. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/native/riscv64/src/rv64v_debug.c +81 -0
  203. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/native/riscv64/src/rv64v_debug.h +145 -0
  204. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/native/riscv64/src/rv64v_izetas.inc +27 -0
  205. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/native/riscv64/src/rv64v_poly.c +805 -0
  206. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/native/riscv64/src/rv64v_zetas.inc +27 -0
  207. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/native/riscv64/src/rv64v_zetas_basemul.inc +39 -0
  208. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/native/x86_64/README.md +4 -0
  209. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/native/x86_64/meta.h +304 -0
  210. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/native/x86_64/src/arith_native_x86_64.h +309 -0
  211. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/native/x86_64/src/compress_consts.c +94 -0
  212. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/native/x86_64/src/compress_consts.h +45 -0
  213. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/native/x86_64/src/consts.c +102 -0
  214. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/native/x86_64/src/consts.h +25 -0
  215. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/native/x86_64/src/intt.S +719 -0
  216. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/native/x86_64/src/mulcache_compute.S +90 -0
  217. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/native/x86_64/src/ntt.S +639 -0
  218. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/native/x86_64/src/nttfrombytes.S +193 -0
  219. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/native/x86_64/src/ntttobytes.S +181 -0
  220. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/native/x86_64/src/nttunpack.S +174 -0
  221. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/native/x86_64/src/poly_compress_d10.S +382 -0
  222. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/native/x86_64/src/poly_compress_d11.S +448 -0
  223. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/native/x86_64/src/poly_compress_d4.S +163 -0
  224. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/native/x86_64/src/poly_compress_d5.S +220 -0
  225. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/native/x86_64/src/poly_decompress_d10.S +228 -0
  226. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/native/x86_64/src/poly_decompress_d11.S +277 -0
  227. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/native/x86_64/src/poly_decompress_d4.S +180 -0
  228. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/native/x86_64/src/poly_decompress_d5.S +192 -0
  229. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/native/x86_64/src/polyvec_basemul_acc_montgomery_cached_asm_k2.S +502 -0
  230. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/native/x86_64/src/polyvec_basemul_acc_montgomery_cached_asm_k3.S +750 -0
  231. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/native/x86_64/src/polyvec_basemul_acc_montgomery_cached_asm_k4.S +998 -0
  232. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/native/x86_64/src/reduce.S +218 -0
  233. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/native/x86_64/src/rej_uniform_asm.S +103 -0
  234. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/native/x86_64/src/rej_uniform_table.c +544 -0
  235. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/native/x86_64/src/tomont.S +155 -0
  236. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/params.h +76 -0
  237. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/poly.c +572 -0
  238. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/poly.h +317 -0
  239. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/poly_k.c +502 -0
  240. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/poly_k.h +668 -0
  241. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/randombytes.h +60 -0
  242. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/sampling.c +362 -0
  243. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/sampling.h +118 -0
  244. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/symmetric.h +70 -0
  245. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/sys.h +260 -0
  246. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/verify.c +20 -0
  247. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/verify.h +464 -0
  248. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/zetas.inc +30 -0
  249. data/lib/pq_crypto/algorithm_registry.rb +200 -0
  250. data/lib/pq_crypto/hybrid_kem.rb +1 -12
  251. data/lib/pq_crypto/kem.rb +104 -13
  252. data/lib/pq_crypto/pkcs8.rb +387 -0
  253. data/lib/pq_crypto/serialization.rb +1 -14
  254. data/lib/pq_crypto/signature.rb +123 -17
  255. data/lib/pq_crypto/spki.rb +131 -0
  256. data/lib/pq_crypto/version.rb +1 -1
  257. data/lib/pq_crypto.rb +79 -20
  258. data/script/vendor_libs.rb +88 -155
  259. metadata +241 -73
  260. data/ext/pqcrypto/vendor/pqclean/common/aes.c +0 -639
  261. data/ext/pqcrypto/vendor/pqclean/common/aes.h +0 -64
  262. data/ext/pqcrypto/vendor/pqclean/common/compat.h +0 -73
  263. data/ext/pqcrypto/vendor/pqclean/common/crypto_declassify.h +0 -7
  264. data/ext/pqcrypto/vendor/pqclean/common/fips202.c +0 -928
  265. data/ext/pqcrypto/vendor/pqclean/common/fips202.h +0 -166
  266. data/ext/pqcrypto/vendor/pqclean/common/keccak2x/feat.S +0 -168
  267. data/ext/pqcrypto/vendor/pqclean/common/keccak2x/fips202x2.c +0 -684
  268. data/ext/pqcrypto/vendor/pqclean/common/keccak2x/fips202x2.h +0 -60
  269. data/ext/pqcrypto/vendor/pqclean/common/keccak4x/KeccakP-1600-times4-SIMD256.c +0 -1028
  270. data/ext/pqcrypto/vendor/pqclean/common/keccak4x/KeccakP-1600-times4-SnP.h +0 -50
  271. data/ext/pqcrypto/vendor/pqclean/common/keccak4x/KeccakP-1600-unrolling.macros +0 -198
  272. data/ext/pqcrypto/vendor/pqclean/common/keccak4x/Makefile +0 -8
  273. data/ext/pqcrypto/vendor/pqclean/common/keccak4x/Makefile.Microsoft_nmake +0 -8
  274. data/ext/pqcrypto/vendor/pqclean/common/keccak4x/SIMD256-config.h +0 -3
  275. data/ext/pqcrypto/vendor/pqclean/common/keccak4x/align.h +0 -34
  276. data/ext/pqcrypto/vendor/pqclean/common/keccak4x/brg_endian.h +0 -142
  277. data/ext/pqcrypto/vendor/pqclean/common/nistseedexpander.c +0 -101
  278. data/ext/pqcrypto/vendor/pqclean/common/nistseedexpander.h +0 -39
  279. data/ext/pqcrypto/vendor/pqclean/common/randombytes.c +0 -355
  280. data/ext/pqcrypto/vendor/pqclean/common/randombytes.h +0 -27
  281. data/ext/pqcrypto/vendor/pqclean/common/sha2.c +0 -769
  282. data/ext/pqcrypto/vendor/pqclean/common/sha2.h +0 -173
  283. data/ext/pqcrypto/vendor/pqclean/common/sp800-185.c +0 -156
  284. data/ext/pqcrypto/vendor/pqclean/common/sp800-185.h +0 -27
  285. data/ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-768/clean/LICENSE +0 -5
  286. data/ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-768/clean/Makefile +0 -19
  287. data/ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-768/clean/Makefile.Microsoft_nmake +0 -23
  288. data/ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-768/clean/api.h +0 -18
  289. data/ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-768/clean/cbd.c +0 -83
  290. data/ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-768/clean/cbd.h +0 -11
  291. data/ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-768/clean/indcpa.c +0 -327
  292. data/ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-768/clean/indcpa.h +0 -22
  293. data/ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-768/clean/kem.c +0 -164
  294. data/ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-768/clean/kem.h +0 -23
  295. data/ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-768/clean/ntt.c +0 -146
  296. data/ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-768/clean/ntt.h +0 -14
  297. data/ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-768/clean/params.h +0 -36
  298. data/ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-768/clean/poly.c +0 -299
  299. data/ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-768/clean/poly.h +0 -37
  300. data/ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-768/clean/polyvec.c +0 -188
  301. data/ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-768/clean/polyvec.h +0 -26
  302. data/ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-768/clean/reduce.c +0 -41
  303. data/ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-768/clean/reduce.h +0 -13
  304. data/ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-768/clean/symmetric-shake.c +0 -71
  305. data/ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-768/clean/symmetric.h +0 -30
  306. data/ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-768/clean/verify.c +0 -67
  307. data/ext/pqcrypto/vendor/pqclean/crypto_kem/ml-kem-768/clean/verify.h +0 -13
  308. data/ext/pqcrypto/vendor/pqclean/crypto_sign/ml-dsa-65/clean/LICENSE +0 -5
  309. data/ext/pqcrypto/vendor/pqclean/crypto_sign/ml-dsa-65/clean/Makefile +0 -19
  310. data/ext/pqcrypto/vendor/pqclean/crypto_sign/ml-dsa-65/clean/Makefile.Microsoft_nmake +0 -23
  311. data/ext/pqcrypto/vendor/pqclean/crypto_sign/ml-dsa-65/clean/api.h +0 -50
  312. data/ext/pqcrypto/vendor/pqclean/crypto_sign/ml-dsa-65/clean/ntt.c +0 -98
  313. data/ext/pqcrypto/vendor/pqclean/crypto_sign/ml-dsa-65/clean/ntt.h +0 -10
  314. data/ext/pqcrypto/vendor/pqclean/crypto_sign/ml-dsa-65/clean/packing.c +0 -261
  315. data/ext/pqcrypto/vendor/pqclean/crypto_sign/ml-dsa-65/clean/packing.h +0 -31
  316. data/ext/pqcrypto/vendor/pqclean/crypto_sign/ml-dsa-65/clean/params.h +0 -44
  317. data/ext/pqcrypto/vendor/pqclean/crypto_sign/ml-dsa-65/clean/poly.c +0 -799
  318. data/ext/pqcrypto/vendor/pqclean/crypto_sign/ml-dsa-65/clean/poly.h +0 -52
  319. data/ext/pqcrypto/vendor/pqclean/crypto_sign/ml-dsa-65/clean/polyvec.c +0 -415
  320. data/ext/pqcrypto/vendor/pqclean/crypto_sign/ml-dsa-65/clean/polyvec.h +0 -65
  321. data/ext/pqcrypto/vendor/pqclean/crypto_sign/ml-dsa-65/clean/reduce.c +0 -69
  322. data/ext/pqcrypto/vendor/pqclean/crypto_sign/ml-dsa-65/clean/reduce.h +0 -17
  323. data/ext/pqcrypto/vendor/pqclean/crypto_sign/ml-dsa-65/clean/rounding.c +0 -92
  324. data/ext/pqcrypto/vendor/pqclean/crypto_sign/ml-dsa-65/clean/rounding.h +0 -14
  325. data/ext/pqcrypto/vendor/pqclean/crypto_sign/ml-dsa-65/clean/sign.c +0 -407
  326. data/ext/pqcrypto/vendor/pqclean/crypto_sign/ml-dsa-65/clean/sign.h +0 -47
  327. data/ext/pqcrypto/vendor/pqclean/crypto_sign/ml-dsa-65/clean/symmetric-shake.c +0 -26
  328. data/ext/pqcrypto/vendor/pqclean/crypto_sign/ml-dsa-65/clean/symmetric.h +0 -34
data/README.md CHANGED
@@ -1,284 +1,127 @@
1
1
  # pq_crypto
2
2
 
3
3
  `pq_crypto` is a primitive-first Ruby gem for post-quantum cryptography.
4
+ It provides small Ruby APIs for ML-KEM, ML-DSA, and one hybrid X-Wing KEM,
5
+ with standard key serialization where available.
4
6
 
5
- It exposes three public building blocks:
6
-
7
- - `PQCrypto::KEM` — pure `ML-KEM-768` (FIPS 203)
8
- - `PQCrypto::Signature` — `ML-DSA-65` (FIPS 204)
9
- - `PQCrypto::HybridKEM` — `ML-KEM-768 + X25519` combined via the
10
- [X-Wing](https://datatracker.ietf.org/doc/draft-connolly-cfrg-xwing-kem/)
11
- SHA3-256 combiner
12
-
13
- The gem is backed by vendored `PQClean` sources for `ML-KEM-768` /
14
- `ML-DSA-65` and by OpenSSL for `X25519` and `SHA3-256`. Every piece of
15
- conventional-crypto functionality goes through standard library calls
16
- (`EVP_*`, `RAND_bytes`, `CRYPTO_memcmp`, `BIO_f_base64`) — nothing
17
- roll-your-own where a library primitive exists.
18
-
19
- ## Status
20
-
21
- - primitive-first API only
22
- - no protocol/session helpers in the public surface
23
- - streaming ML-DSA signing/verification is available for large IO inputs
24
- - serialization uses pq_crypto-specific `pqc_container_*` wrappers
25
- - not audited
26
- - not yet positioned as production-ready
7
+ The gem intentionally stays close to cryptographic primitives. It does not
8
+ provide protocol, session, transport, certificate-chain, or application-level
9
+ handshake helpers.
27
10
 
28
11
  ## Installation
29
12
 
30
- Add the gem to your project and compile the extension:
13
+ Add the gem to your `Gemfile`:
31
14
 
32
15
  ```ruby
33
16
  # Gemfile
34
17
  gem "pq_crypto"
35
18
  ```
36
19
 
20
+ Then install it:
21
+
37
22
  ```bash
38
23
  bundle install
39
- bundle exec rake compile
40
24
  ```
41
25
 
42
- ### Native dependencies
43
-
44
- - Ruby 3.4.x
45
- - a C toolchain with C11 support (for `_Static_assert` / `_Thread_local`)
46
- - OpenSSL **3.0 or later** with SHA3-256 and SHAKE256 available (default provider)
47
-
48
- ### Build-time Keccak backend
49
-
50
- The default build uses PQClean's scalar `common/fips202.c` backend:
26
+ When working from a source checkout, compile the native extension before
27
+ running tests or examples:
51
28
 
52
29
  ```bash
53
- PQCRYPTO_KECCAK_BACKEND=clean bundle exec rake compile
54
- ```
55
-
56
- `PQCRYPTO_KECCAK_BACKEND=xkcp` is reserved for a separately vendored,
57
- reviewed, `fips202.h`-compatible XKCP adapter. If requested without that
58
- adapter, the build aborts instead of silently falling back to `clean`.
59
- This avoids mixing OpenSSL EVP SHAKE state with PQClean SHAKE state and
60
- keeps output-byte compatibility explicit.
61
-
62
- ## Async / Fiber scheduler support
63
-
64
- `pq_crypto` does not require any gem-specific Async configuration. On
65
- Ruby 3.4, `sign` and `verify` use Ruby's scheduler-aware
66
- `rb_nogvl(..., RB_NOGVL_OFFLOAD_SAFE)` path automatically.
67
-
68
- That means:
69
-
70
- - without a Fiber scheduler, these methods fall back to the ordinary
71
- no-GVL behavior;
72
- - with a scheduler that implements `blocking_operation_wait` (for
73
- example `Async` with a worker pool), the blocking native work can
74
- be moved off the event loop.
75
-
76
- This integration is intentionally limited to `sign` and `verify`; the
77
- faster primitive operations keep the lower-overhead path.
78
-
79
- Example with `Async`:
80
-
81
- ```ruby
82
- require "async"
83
- require "pq_crypto"
84
-
85
- keypair = PQCrypto::Signature.generate(:ml_dsa_65)
86
- message = "hello" * 100_000
87
-
88
- reactor = Async::Reactor.new(worker_pool: true)
89
- root = reactor.async do |task|
90
- task.async do
91
- signature = keypair.secret_key.sign(message)
92
- keypair.public_key.verify(message, signature)
93
- end
94
-
95
- task.async do
96
- sleep 0.01
97
- puts "event loop stayed responsive"
98
- end
99
- end
100
-
101
- reactor.run
102
- root.wait
103
- reactor.close
104
- ```
105
-
106
- ## Primitive API
107
-
108
- ### ML-KEM-768
109
-
110
- ```ruby
111
- keypair = PQCrypto::KEM.generate(:ml_kem_768)
112
- result = keypair.public_key.encapsulate
113
- shared_secret = keypair.secret_key.decapsulate(result.ciphertext)
114
- ```
115
-
116
- ### ML-DSA-65
117
-
118
- One-shot signing keeps the existing API:
119
-
120
- ```ruby
121
- keypair = PQCrypto::Signature.generate(:ml_dsa_65)
122
- signature = keypair.secret_key.sign("hello")
123
-
124
- keypair.public_key.verify("hello", signature) # => true / false
125
- keypair.public_key.verify!("hello", signature) # raises on mismatch
126
- ```
127
-
128
- For large inputs, use streaming IO so the message does not need to be
129
- materialized as one Ruby string:
130
-
131
- ```ruby
132
- signature = File.open("document.bin", "rb") do |io|
133
- keypair.secret_key.sign_io(io, chunk_size: 1 << 20)
134
- end
135
-
136
- ok = File.open("document.bin", "rb") do |io|
137
- keypair.public_key.verify_io(io, signature, chunk_size: 1 << 20)
138
- end
30
+ bundle exec rake compile
31
+ bundle exec rake test
139
32
  ```
140
33
 
141
- `sign_io` / `verify_io` use pure ML-DSA with an internal FIPS 204
142
- ExternalMu flow. They are not HashML-DSA/prehash shortcuts and do not
143
- expose public `sign_mu` / `verify_mu` APIs. With the default empty
144
- context, streaming signatures verify with `verify(message, signature)`
145
- and one-shot signatures verify with `verify_io(io, signature)`.
34
+ ## What this gem provides
146
35
 
147
- Optional context is supported and must match on verify:
36
+ | Area | Capabilities |
37
+ | --- | --- |
38
+ | ML-KEM | Key generation, encapsulation, decapsulation, raw key import/export, SPKI public keys, PKCS#8 private keys. |
39
+ | ML-DSA | Key generation, signing, verification, streaming signing/verification for large inputs, raw key import/export, SPKI public keys, PKCS#8 private keys. |
40
+ | Hybrid KEM | ML-KEM-768 + X25519 using the X-Wing combiner. |
41
+ | Serialization | Standard SPKI / PKCS#8 for NIST PQC keys, plus frozen `pqc_container_*` compatibility formats for the original algorithms. |
42
+ | Safety helpers | Best-effort secret wiping and constant-time equality for key comparisons. |
43
+ | Introspection | Supported algorithm lists, algorithm metadata, backend/version helpers. |
148
44
 
149
- ```ruby
150
- ctx = "invoice-v1".b
151
- signature = File.open("document.bin", "rb") { |io| keypair.secret_key.sign_io(io, context: ctx) }
152
- ok = File.open("document.bin", "rb") { |io| keypair.public_key.verify_io(io, signature, context: ctx) }
153
- ```
45
+ ## Supported algorithms
154
46
 
155
- `chunk_size` must be positive. `context` is limited to 255 bytes by
156
- FIPS 204. `verify_io!` raises `PQCrypto::VerificationError` on mismatch.
47
+ | Family | Algorithms | Notes |
48
+ | --- | --- | --- |
49
+ | KEM | `:ml_kem_512`, `:ml_kem_768`, `:ml_kem_1024` | FIPS 203 ML-KEM. Standard SPKI public keys and PKCS#8 private keys. |
50
+ | Signature | `:ml_dsa_44`, `:ml_dsa_65`, `:ml_dsa_87` | FIPS 204 ML-DSA. Standard SPKI public keys and PKCS#8 private keys. |
51
+ | Hybrid KEM | `:ml_kem_768_x25519_xwing` | ML-KEM-768 + X25519 hybrid KEM using the X-Wing construction. |
157
52
 
158
- Note: `verify` returns a plain boolean for normal outcomes. `verify!`
159
- raises `PQCrypto::VerificationError` when the signature does not
160
- match.
53
+ Standard encodings use RFC 9935 OIDs for ML-KEM and RFC 9881 OIDs for
54
+ ML-DSA. `AlgorithmIdentifier.parameters` are omitted, not encoded as `NULL`.
161
55
 
162
- ### Hybrid ML-KEM-768 + X25519 (X-Wing)
56
+ The `pqc_container_*` format is project-local and kept only for backward
57
+ compatibility. It is not ASN.1, SPKI, or PKCS#8. It remains limited to the
58
+ original algorithms:
163
59
 
164
- ```ruby
165
- keypair = PQCrypto::HybridKEM.generate(:ml_kem_768_x25519_xwing)
166
- result = keypair.public_key.encapsulate
167
- shared_secret = keypair.secret_key.decapsulate(result.ciphertext)
168
- ```
169
-
170
- The implementation follows draft-10 key expansion: the X-Wing secret
171
- decapsulation key is a 32-byte seed expanded with SHAKE256 into ML-KEM
172
- and X25519 private material. The combiner is exactly:
60
+ - `:ml_kem_768`
61
+ - `:ml_dsa_65`
62
+ - `:ml_kem_768_x25519_xwing`
173
63
 
174
- ```
175
- ss = SHA3-256( ss_M || ss_X || ct_X || pk_X || "\.//^\" )
176
- ```
64
+ ## Requirements
177
65
 
178
- as specified by `draft-connolly-cfrg-xwing-kem-10`. See `SECURITY.md`
179
- for audit status and interoperability caveats.
66
+ - Ruby 3.4 or later
67
+ - a C toolchain with C11 support
68
+ - OpenSSL 3.0 or later with SHA3-256 and SHAKE256 available
69
+ - vendored minimal PQ Code Package native snapshot in `ext/pqcrypto/vendor`
180
70
 
181
- ## Serialization
71
+ ## Native backend
182
72
 
183
- Key import/export is available through pq_crypto-specific containers:
73
+ Version `0.5.0` moves ML-KEM and ML-DSA to PQ Code Package
74
+ `mlkem-native` / `mldsa-native` sources. PQClean is no longer built and there
75
+ is no runtime or build-time PQClean fallback. The repository and source gem
76
+ ship a minimal vendor snapshot containing only the `mlkem/` and `mldsa/` source
77
+ trees plus license/docs; upstream examples, tests, proofs, `.git` directories,
78
+ and symlink-heavy trees are intentionally omitted. If the native vendor snapshot
79
+ is missing, the extension build fails early.
184
80
 
185
- - `to_pqc_container_der`
186
- - `to_pqc_container_pem`
187
- - `*_from_pqc_container_der`
188
- - `*_from_pqc_container_pem`
81
+ From a source checkout, refresh the native vendor snapshot before compiling:
189
82
 
190
- Example:
191
-
192
- ```ruby
193
- keypair = PQCrypto::KEM.generate(:ml_kem_768)
194
- der = keypair.public_key.to_pqc_container_der
195
- imported = PQCrypto::KEM.public_key_from_pqc_container_der(der)
83
+ ```bash
84
+ bundle exec rake vendor
85
+ bundle exec rake compile
196
86
  ```
197
87
 
198
- These containers are **not real ASN.1 SPKI or PKCS#8**. They are
199
- intended for stable import/export inside `pq_crypto` itself and are
200
- not advertised as interoperable with external PKI tooling.
88
+ The default build uses the portable native source path. Upstream native assembly
89
+ can be tested explicitly with:
201
90
 
202
- ## Secure wiping
203
-
204
- `PQCrypto.secure_wipe(str)` zeros the bytes of a mutable Ruby string
205
- in place. Key objects hold a private copy of their bytes, so `wipe!`
206
- on a `SecretKey` zeroes **only** that internal copy — any prior Ruby
207
- string the caller holds is untouched. If you need to wipe the
208
- caller-side buffer, do so explicitly:
209
-
210
- ```ruby
211
- raw = File.binread(path)
212
- key = PQCrypto::KEM.secret_key_from_bytes(:ml_kem_768, raw)
213
- PQCrypto.secure_wipe(raw) # scrub the original input
214
- # ... use key ...
215
- key.wipe! # scrub the key's internal copy
91
+ ```bash
92
+ PQCRYPTO_NATIVE_ASM=1 bundle exec rake compile
216
93
  ```
217
94
 
218
- ## Constant-time comparison
95
+ ## Security status
219
96
 
220
- `==` on `PublicKey` / `SecretKey` instances uses OpenSSL
221
- `CRYPTO_memcmp` through a `PQCrypto.ct_equals` helper so comparisons
222
- do not leak timing information about a prefix match.
97
+ `pq_crypto` is experimental and not audited. Treat it as a low-level primitive
98
+ library, not a complete security protocol. See [`SECURITY.md`](SECURITY.md) for
99
+ audit status, serialization caveats, hybrid-KEM notes, and interoperability
100
+ warnings.
223
101
 
224
- Secret key `inspect` output is intentionally redacted and secret key
225
- objects do not expose a public `fingerprint` method. `wipe!` remains
226
- best-effort only: it clears the current Ruby string buffer owned by the
227
- key object, not every possible copy made by Ruby, OpenSSL, serialization,
228
- logging, or the garbage collector.
229
-
230
- ## Introspection
102
+ ## Useful entry points
231
103
 
232
104
  ```ruby
233
105
  PQCrypto.version
234
106
  PQCrypto.backend
235
107
  PQCrypto.supported_kems
236
- PQCrypto.supported_hybrid_kems
237
108
  PQCrypto.supported_signatures
238
- PQCrypto::KEM.details(:ml_kem_768)
239
- PQCrypto::HybridKEM.details(:ml_kem_768_x25519_xwing)
240
- PQCrypto::Signature.details(:ml_dsa_65)
241
- ```
242
-
243
- ## Testing helpers
244
-
245
- Deterministic test hooks are exposed under `PQCrypto::Testing` for
246
- regression coverage:
247
-
248
- - `ml_kem_keypair_from_seed` — requires a 64-byte `d||z` seed (FIPS 203)
249
- - `ml_kem_encapsulate_from_seed` — requires a 32-byte seed
250
- - `ml_dsa_keypair_from_seed` — requires a 32-byte seed
251
- - `ml_dsa_sign_from_seed` — requires a 32-byte seed
252
-
253
- These helpers are intended for tests only. They work by installing a
254
- thread-local seed-replay mode inside the gem's `randombytes()` for
255
- the duration of the call, then call the stock PQClean entrypoints.
256
- No internal PQClean algorithm logic is reimplemented in this gem.
257
-
258
- ## Development
259
-
260
- Run the test suite with:
109
+ PQCrypto.supported_hybrid_kems
261
110
 
262
- ```bash
263
- bundle exec rake test
111
+ PQCrypto::KEM.generate(:ml_kem_768)
112
+ PQCrypto::Signature.generate(:ml_dsa_65)
113
+ PQCrypto::HybridKEM.generate(:ml_kem_768_x25519_xwing)
264
114
  ```
265
115
 
266
- Refresh vendored PQClean sources manually only when you intentionally
267
- update the vendor snapshot. The refresh script has a safe pinned
268
- default and records the exact vendored snapshot in
269
- `ext/pqcrypto/vendor/.vendored`:
270
-
271
- ```bash
272
- bundle exec ruby script/vendor_libs.rb
273
- ```
116
+ ## More examples
274
117
 
275
- To intentionally change the upstream snapshot, override all four
276
- pinning inputs together:
118
+ Detailed usage examples live in [`GET_STARTED.md`](GET_STARTED.md):
277
119
 
278
- ```bash
279
- PQCLEAN_VERSION=<full-git-commit> \
280
- PQCLEAN_URL=https://github.com/PQClean/PQClean/archive/<full-git-commit>.tar.gz \
281
- PQCLEAN_SHA256=<archive-sha256> \
282
- PQCLEAN_STRIP=PQClean-<full-git-commit> \
283
- bundle exec ruby script/vendor_libs.rb
284
- ```
120
+ - generating keys
121
+ - ML-KEM encapsulation / decapsulation
122
+ - ML-DSA signing / verification
123
+ - streaming ML-DSA for large files
124
+ - SPKI and PKCS#8 serialization
125
+ - `pqc_container_*` compatibility serialization
126
+ - native backend / vendoring notes
127
+ - secure wiping and practical safety notes
data/SECURITY.md CHANGED
@@ -4,125 +4,150 @@
4
4
 
5
5
  `pq_crypto` exposes a primitive-first public surface:
6
6
 
7
- - `PQCrypto::KEM` (`ML-KEM-768`)
8
- - `PQCrypto::Signature` (`ML-DSA-65`)
9
- - `PQCrypto::HybridKEM` (`ML-KEM-768 + X25519` via the X-Wing combiner)
7
+ - `PQCrypto::KEM` ML-KEM-512, ML-KEM-768, ML-KEM-1024
8
+ - `PQCrypto::Signature` ML-DSA-44, ML-DSA-65, ML-DSA-87
9
+ - `PQCrypto::HybridKEM` ML-KEM-768 + X25519 via the X-Wing combiner
10
10
  - `PQCrypto.secure_wipe`
11
- - `PQCrypto.ct_equals` (constant-time byte-string comparison)
11
+ - `PQCrypto.ct_equals`
12
12
 
13
- The gem does **not** publish protocol/session helpers as part of the
14
- supported public API.
13
+ The gem does not publish protocol/session helpers as part of the supported
14
+ public API.
15
15
 
16
16
  ## Audit status
17
17
 
18
18
  This project has not been audited. Treat it as experimental software.
19
19
 
20
+ The test surface includes deterministic regression tests, NIST ACVP KAT test
21
+ infrastructure, and OpenSSL 3.5+ interoperability tests for standard SPKI /
22
+ PKCS#8 encodings where the linked OpenSSL exposes the corresponding ML-KEM /
23
+ ML-DSA EVP support. These tests improve compatibility coverage but are not a
24
+ substitute for a security audit.
25
+
20
26
  ## Algorithm notes
21
27
 
22
- ### ML-KEM-768 / ML-DSA-65
28
+ ### ML-KEM / ML-DSA
23
29
 
24
- The post-quantum primitives are backed by vendored `PQClean` sources
25
- and called through PQClean's public `crypto_kem_*` and `crypto_sign_*`
26
- entrypoints only. Internal PQClean symbols are not called from this
27
- gem.
30
+ As of `0.5.0`, the post-quantum primitives are backed by vendored PQ Code
31
+ Package `mlkem-native` and `mldsa-native` sources. PQClean is not built and
32
+ there is intentionally no PQClean fallback.
28
33
 
29
- ### HybridKEM
34
+ The gem calls the native package entrypoints for ML-KEM key generation,
35
+ encapsulation, decapsulation, ML-DSA key generation, signing, verification, and
36
+ test-only deterministic hooks. It does not reimplement ML-KEM, ML-DSA, SHAKE,
37
+ or Keccak.
30
38
 
31
- `PQCrypto::HybridKEM` implements the **X-Wing** construction from
32
- [`draft-connolly-cfrg-xwing-kem-10`](https://datatracker.ietf.org/doc/draft-connolly-cfrg-xwing-kem/).
39
+ ### HybridKEM
33
40
 
34
- The X-Wing secret decapsulation key is a 32-byte seed. It is expanded
35
- with SHAKE256 into the ML-KEM-768 and X25519 private material used
36
- internally for decapsulation. The public key and ciphertext are the
37
- fixed-length concatenations specified by the draft.
41
+ `PQCrypto::HybridKEM` implements the X-Wing construction from
42
+ `draft-connolly-cfrg-xwing-kem-10`.
38
43
 
39
- ss = SHA3-256( ss_M || ss_X || ct_X || pk_X || XWingLabel )
44
+ The X-Wing secret decapsulation key is a 32-byte seed. It is expanded with
45
+ SHAKE256 into the ML-KEM-768 and X25519 private material used internally for
46
+ decapsulation. The public key and ciphertext are the fixed-length
47
+ concatenations specified by the draft.
40
48
 
41
- where `XWingLabel = "\.//^\"` (6 ASCII bytes).
49
+ ```text
50
+ ss = SHA3-256( ss_M || ss_X || ct_X || pk_X || XWingLabel )
51
+ ```
42
52
 
43
- X-Wing as specified has a proof of classical IND-CCA security under
44
- the strong Diffie-Hellman assumption for X25519 (in the ROM), and
45
- post-quantum IND-CCA security in the standard model assuming ML-KEM-768
46
- is IND-CCA secure and SHA3-256 behaves as a PRF.
53
+ where `XWingLabel = "\.//^\"`.
47
54
 
48
- This gem is intended to match the X-Wing draft as of version 10. External
49
- interoperability should still be verified against the reference
55
+ External interoperability should be verified against the reference
50
56
  implementation before relying on it.
51
57
 
52
- ### Deterministic test hooks
58
+ ## Serialization formats
53
59
 
54
- `PQCrypto::Testing` deterministic helpers drive the stock PQClean
55
- `crypto_sign_keypair` / `crypto_sign_signature` (for ML-DSA) and
56
- `crypto_kem_keypair_derand` / `crypto_kem_enc_derand` (for ML-KEM)
57
- against a caller-supplied seed. For ML-DSA, which has no derand API
58
- upstream, the gem installs a thread-local seed-replay buffer inside
59
- its `randombytes()` implementation; outside of a test call the same
60
- `randombytes()` entry delegates directly to OpenSSL `RAND_bytes`. No
61
- internal PQClean algorithm logic is reimplemented in this gem.
60
+ ### pq_crypto-local `pqc_container_*`
62
61
 
63
- ## Serialization
62
+ `pqc_container_*` DER/PEM wrappers are pq_crypto-specific containers. They are:
64
63
 
65
- `pqc_container_*` DER/PEM wrappers are pq_crypto-specific containers.
64
+ - not ASN.1
65
+ - not SPKI
66
+ - not PKCS#8
67
+ - not advertised as interoperable with OpenSSL, Go, Java, or PKI tooling
66
68
 
67
- They are:
69
+ This format is frozen for backward compatibility and remains limited to the
70
+ original three algorithms:
68
71
 
69
- - not real SPKI
70
- - not real PKCS#8
71
- - not advertised as interoperable with OpenSSL, Go, Java, or PKI tooling
72
+ - `:ml_kem_768`
73
+ - `:ml_dsa_65`
74
+ - `:ml_kem_768_x25519_xwing`
75
+
76
+ ### Standard SPKI / PKCS#8
77
+
78
+ ML-KEM and ML-DSA use standard SPKI public-key and PKCS#8 private-key encodings
79
+ for the NIST parameter sets. AlgorithmIdentifier parameters are absent, not
80
+ encoded as `NULL`.
81
+
82
+ | Algorithm | Standard OID | Reference |
83
+ | --- | --- | --- |
84
+ | ML-KEM-512 | `2.16.840.1.101.3.4.4.1` | RFC 9935 |
85
+ | ML-KEM-768 | `2.16.840.1.101.3.4.4.2` | RFC 9935 |
86
+ | ML-KEM-1024 | `2.16.840.1.101.3.4.4.3` | RFC 9935 |
87
+ | ML-DSA-44 | `2.16.840.1.101.3.4.3.17` | RFC 9881 |
88
+ | ML-DSA-65 | `2.16.840.1.101.3.4.3.18` | RFC 9881 |
89
+ | ML-DSA-87 | `2.16.840.1.101.3.4.3.19` | RFC 9881 |
90
+
91
+ `PQCrypto::KEM.details` / `PQCrypto::Signature.details` keep `:oid` as the
92
+ legacy `pqc_container_*` OID for backward compatibility. Use
93
+ `PQCrypto::AlgorithmRegistry.standard_oid` for the standard OID.
72
94
 
73
- The `pqc_container_*` envelope itself is project-specific. ML-KEM and
74
- ML-DSA currently use project-local UUID-derived OIDs under `2.25.*`.
75
- Hybrid X-Wing uses the draft X-Wing OID `1.3.6.1.4.1.62253.25722`.
95
+ ## ML-DSA seed-format imports
76
96
 
77
- The hybrid OID used by 0.2.0
78
- (`2.25.260242945110721168101139140490528778800`) is retired. The
79
- intermediate 0.3.0 project-local hybrid OID
80
- (`2.25.318532651283923671095712569430174917109`) is also retired in
81
- favor of the draft X-Wing OID. Older hybrid containers are rejected at
82
- decode time.
97
+ ML-DSA seed and both-form PKCS#8 imports are disabled by default. To import
98
+ these encodings, callers must explicitly set:
99
+
100
+ ```ruby
101
+ PQCrypto::PKCS8.allow_ml_dsa_seed_format = true
102
+ ```
103
+
104
+ This opt-in remains explicit because seed and both-form imports are more
105
+ sensitive than expanded-key imports: the decoder expands the seed into an
106
+ expanded private key and, for `both` encodings, rejects the key if the
107
+ expandedKey half does not match the seed-derived key.
108
+
109
+ The expansion path uses the vendored `mldsa-native` deterministic keypair
110
+ entrypoints rather than a `randombytes()` seed-replay fallback.
111
+
112
+ ## Deterministic test hooks
113
+
114
+ `PQCrypto::Testing` deterministic helpers drive the vendored PQ Code Package
115
+ native deterministic entrypoints against caller-supplied seeds. ML-DSA
116
+ deterministic signing passes the FIPS 204 pure-mode domain-separation prefix
117
+ into `mldsa-native` `signature_internal`; for an empty context this prefix is
118
+ `00 00`.
119
+
120
+ Outside of test-only deterministic calls, production randomness delegates
121
+ directly to OpenSSL `RAND_bytes`.
83
122
 
84
123
  ## Memory wiping
85
124
 
86
- `PQCrypto.secure_wipe` clears mutable Ruby strings in place. Ruby key
87
- objects (`PublicKey`, `SecretKey`) take a copy of the bytes passed into
88
- their constructor and expose `#wipe!` to zero only that internal copy
89
- any prior Ruby string the caller still holds is untouched. Ruby
90
- garbage collection and prior derived copies may still leave sensitive
91
- material elsewhere in process memory.
125
+ `PQCrypto.secure_wipe` clears mutable Ruby strings in place. Ruby key objects
126
+ take a copy of the bytes passed into their constructor and expose `#wipe!` to
127
+ zero only that internal copy. Ruby garbage collection and prior derived copies
128
+ may still leave sensitive material elsewhere in process memory.
129
+
130
+ Secret key objects redact `inspect` output and intentionally do not expose a
131
+ public `fingerprint` method. This avoids accidental logging of raw secret bytes
132
+ or stable secret-derived identifiers.
92
133
 
93
134
  ## OpenSSL baseline
94
135
 
95
- `pq_crypto` requires OpenSSL **3.0 or later**.
136
+ `pq_crypto` requires OpenSSL 3.0 or later.
96
137
 
97
138
  OpenSSL is used for:
98
139
 
99
- - `X25519` key generation and key agreement (`EVP_PKEY_*`)
100
- - `SHA3-256` (X-Wing combiner, via `EVP_sha3_256`)
101
- - `RAND_bytes` (production entropy source for `randombytes()`)
102
- - `CRYPTO_memcmp` (constant-time comparison used by `PQCrypto.ct_equals`)
103
- - Base64 encode/decode for PEM via OpenSSL `BIO_f_base64`, with strict
104
- header/footer framing and trailing-garbage checks.
105
-
106
- ## Secret key display and wiping
107
-
108
- Secret key objects redact `inspect` output and intentionally do not expose
109
- a public `fingerprint` method. This avoids accidental logging of raw secret
110
- bytes or stable secret-derived identifiers.
140
+ - X25519 key generation and key agreement
141
+ - SHA3-256 for the X-Wing combiner
142
+ - RAND_bytes as the production entropy source for `randombytes()`
143
+ - CRYPTO_memcmp for constant-time comparison
144
+ - Base64 encode/decode for PEM
111
145
 
112
- `wipe!` is best-effort only. It wipes the current Ruby string buffer held
113
- by the key object; it cannot guarantee erasure of copies made by Ruby,
114
- OpenSSL, native wrapper buffers, serialization, logging, crash dumps, or
115
- the garbage collector.
146
+ OpenSSL 3.5+ is additionally used in interop tests when ML-KEM / ML-DSA EVP
147
+ support is available.
116
148
 
117
149
  ## Threading
118
150
 
119
151
  Concurrent read-only operations on primitive key objects are supported.
120
- Native calls copy Ruby string inputs before releasing the GVL, so
121
- normal concurrent use does not rely on Ruby string storage remaining
122
- pinned in place.
123
-
124
- The deterministic test hooks use a thread-local seed-replay mode
125
- around `randombytes()`, so a test running on one thread does not
126
- affect production callers on other threads. The deterministic helpers
127
- remain test-only utilities and should not be relied on as a general
128
- multi-threading contract.
152
+ Mutating operations such as `wipe!` must not race with other uses of the same
153
+ object.