pq_crypto-seal 0.1.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 (143) hide show
  1. checksums.yaml +7 -0
  2. data/.github/workflows/ci.yml +66 -0
  3. data/CHANGELOG.md +44 -0
  4. data/FORMAT.md +139 -0
  5. data/GET_STARTED.md +30 -0
  6. data/Gemfile +7 -0
  7. data/LICENSE.txt +21 -0
  8. data/README.md +215 -0
  9. data/RELEASING.md +34 -0
  10. data/Rakefile +19 -0
  11. data/SECURITY.md +85 -0
  12. data/VENDORING.md +14 -0
  13. data/ext/pq_crypto_seal/aegis_unused_stubs.c +20 -0
  14. data/ext/pq_crypto_seal/extconf.rb +296 -0
  15. data/ext/pq_crypto_seal/pq_crypto_seal.c +390 -0
  16. data/ext/pq_crypto_seal/vendor/libaegis/LICENSE +21 -0
  17. data/ext/pq_crypto_seal/vendor/libaegis/TREE_SHA256 +1 -0
  18. data/ext/pq_crypto_seal/vendor/libaegis/src/aegis128l/aegis128l.c +246 -0
  19. data/ext/pq_crypto_seal/vendor/libaegis/src/aegis128l/aegis128l_aesni.c +73 -0
  20. data/ext/pq_crypto_seal/vendor/libaegis/src/aegis128l/aegis128l_aesni.h +9 -0
  21. data/ext/pq_crypto_seal/vendor/libaegis/src/aegis128l/aegis128l_altivec.c +70 -0
  22. data/ext/pq_crypto_seal/vendor/libaegis/src/aegis128l/aegis128l_altivec.h +9 -0
  23. data/ext/pq_crypto_seal/vendor/libaegis/src/aegis128l/aegis128l_common.h +691 -0
  24. data/ext/pq_crypto_seal/vendor/libaegis/src/aegis128l/aegis128l_neon_aes.c +77 -0
  25. data/ext/pq_crypto_seal/vendor/libaegis/src/aegis128l/aegis128l_neon_aes.h +9 -0
  26. data/ext/pq_crypto_seal/vendor/libaegis/src/aegis128l/aegis128l_neon_sha3.c +86 -0
  27. data/ext/pq_crypto_seal/vendor/libaegis/src/aegis128l/aegis128l_neon_sha3.h +9 -0
  28. data/ext/pq_crypto_seal/vendor/libaegis/src/aegis128l/aegis128l_soft.c +63 -0
  29. data/ext/pq_crypto_seal/vendor/libaegis/src/aegis128l/aegis128l_soft.h +9 -0
  30. data/ext/pq_crypto_seal/vendor/libaegis/src/aegis128l/implementations.h +40 -0
  31. data/ext/pq_crypto_seal/vendor/libaegis/src/aegis128x2/aegis128x2.c +247 -0
  32. data/ext/pq_crypto_seal/vendor/libaegis/src/aegis128x2/aegis128x2_aesni.c +108 -0
  33. data/ext/pq_crypto_seal/vendor/libaegis/src/aegis128x2/aegis128x2_aesni.h +9 -0
  34. data/ext/pq_crypto_seal/vendor/libaegis/src/aegis128x2/aegis128x2_altivec.c +103 -0
  35. data/ext/pq_crypto_seal/vendor/libaegis/src/aegis128x2/aegis128x2_altivec.h +9 -0
  36. data/ext/pq_crypto_seal/vendor/libaegis/src/aegis128x2/aegis128x2_avx2.c +79 -0
  37. data/ext/pq_crypto_seal/vendor/libaegis/src/aegis128x2/aegis128x2_avx2.h +11 -0
  38. data/ext/pq_crypto_seal/vendor/libaegis/src/aegis128x2/aegis128x2_common.h +795 -0
  39. data/ext/pq_crypto_seal/vendor/libaegis/src/aegis128x2/aegis128x2_neon_aes.c +111 -0
  40. data/ext/pq_crypto_seal/vendor/libaegis/src/aegis128x2/aegis128x2_neon_aes.h +9 -0
  41. data/ext/pq_crypto_seal/vendor/libaegis/src/aegis128x2/aegis128x2_soft.c +96 -0
  42. data/ext/pq_crypto_seal/vendor/libaegis/src/aegis128x2/aegis128x2_soft.h +9 -0
  43. data/ext/pq_crypto_seal/vendor/libaegis/src/aegis128x2/implementations.h +40 -0
  44. data/ext/pq_crypto_seal/vendor/libaegis/src/aegis128x4/aegis128x4.c +252 -0
  45. data/ext/pq_crypto_seal/vendor/libaegis/src/aegis128x4/aegis128x4_aesni.c +117 -0
  46. data/ext/pq_crypto_seal/vendor/libaegis/src/aegis128x4/aegis128x4_aesni.h +9 -0
  47. data/ext/pq_crypto_seal/vendor/libaegis/src/aegis128x4/aegis128x4_altivec.c +111 -0
  48. data/ext/pq_crypto_seal/vendor/libaegis/src/aegis128x4/aegis128x4_altivec.h +9 -0
  49. data/ext/pq_crypto_seal/vendor/libaegis/src/aegis128x4/aegis128x4_avx2.c +112 -0
  50. data/ext/pq_crypto_seal/vendor/libaegis/src/aegis128x4/aegis128x4_avx2.h +11 -0
  51. data/ext/pq_crypto_seal/vendor/libaegis/src/aegis128x4/aegis128x4_avx512.c +85 -0
  52. data/ext/pq_crypto_seal/vendor/libaegis/src/aegis128x4/aegis128x4_avx512.h +11 -0
  53. data/ext/pq_crypto_seal/vendor/libaegis/src/aegis128x4/aegis128x4_common.h +808 -0
  54. data/ext/pq_crypto_seal/vendor/libaegis/src/aegis128x4/aegis128x4_neon_aes.c +119 -0
  55. data/ext/pq_crypto_seal/vendor/libaegis/src/aegis128x4/aegis128x4_neon_aes.h +9 -0
  56. data/ext/pq_crypto_seal/vendor/libaegis/src/aegis128x4/aegis128x4_soft.c +104 -0
  57. data/ext/pq_crypto_seal/vendor/libaegis/src/aegis128x4/aegis128x4_soft.h +9 -0
  58. data/ext/pq_crypto_seal/vendor/libaegis/src/aegis128x4/implementations.h +41 -0
  59. data/ext/pq_crypto_seal/vendor/libaegis/src/aegis256/aegis256.c +240 -0
  60. data/ext/pq_crypto_seal/vendor/libaegis/src/aegis256/aegis256_aesni.c +68 -0
  61. data/ext/pq_crypto_seal/vendor/libaegis/src/aegis256/aegis256_aesni.h +9 -0
  62. data/ext/pq_crypto_seal/vendor/libaegis/src/aegis256/aegis256_altivec.c +68 -0
  63. data/ext/pq_crypto_seal/vendor/libaegis/src/aegis256/aegis256_altivec.h +9 -0
  64. data/ext/pq_crypto_seal/vendor/libaegis/src/aegis256/aegis256_common.h +673 -0
  65. data/ext/pq_crypto_seal/vendor/libaegis/src/aegis256/aegis256_neon_aes.c +75 -0
  66. data/ext/pq_crypto_seal/vendor/libaegis/src/aegis256/aegis256_neon_aes.h +9 -0
  67. data/ext/pq_crypto_seal/vendor/libaegis/src/aegis256/aegis256_soft.c +58 -0
  68. data/ext/pq_crypto_seal/vendor/libaegis/src/aegis256/aegis256_soft.h +9 -0
  69. data/ext/pq_crypto_seal/vendor/libaegis/src/aegis256/implementations.h +39 -0
  70. data/ext/pq_crypto_seal/vendor/libaegis/src/aegis256x2/aegis256x2.c +247 -0
  71. data/ext/pq_crypto_seal/vendor/libaegis/src/aegis256x2/aegis256x2_aesni.c +103 -0
  72. data/ext/pq_crypto_seal/vendor/libaegis/src/aegis256x2/aegis256x2_aesni.h +9 -0
  73. data/ext/pq_crypto_seal/vendor/libaegis/src/aegis256x2/aegis256x2_altivec.c +101 -0
  74. data/ext/pq_crypto_seal/vendor/libaegis/src/aegis256x2/aegis256x2_altivec.h +9 -0
  75. data/ext/pq_crypto_seal/vendor/libaegis/src/aegis256x2/aegis256x2_avx2.c +74 -0
  76. data/ext/pq_crypto_seal/vendor/libaegis/src/aegis256x2/aegis256x2_avx2.h +11 -0
  77. data/ext/pq_crypto_seal/vendor/libaegis/src/aegis256x2/aegis256x2_common.h +790 -0
  78. data/ext/pq_crypto_seal/vendor/libaegis/src/aegis256x2/aegis256x2_neon_aes.c +109 -0
  79. data/ext/pq_crypto_seal/vendor/libaegis/src/aegis256x2/aegis256x2_neon_aes.h +9 -0
  80. data/ext/pq_crypto_seal/vendor/libaegis/src/aegis256x2/aegis256x2_soft.c +91 -0
  81. data/ext/pq_crypto_seal/vendor/libaegis/src/aegis256x2/aegis256x2_soft.h +9 -0
  82. data/ext/pq_crypto_seal/vendor/libaegis/src/aegis256x2/implementations.h +41 -0
  83. data/ext/pq_crypto_seal/vendor/libaegis/src/aegis256x4/aegis256x4.c +252 -0
  84. data/ext/pq_crypto_seal/vendor/libaegis/src/aegis256x4/aegis256x4_aesni.c +112 -0
  85. data/ext/pq_crypto_seal/vendor/libaegis/src/aegis256x4/aegis256x4_aesni.h +9 -0
  86. data/ext/pq_crypto_seal/vendor/libaegis/src/aegis256x4/aegis256x4_altivec.c +109 -0
  87. data/ext/pq_crypto_seal/vendor/libaegis/src/aegis256x4/aegis256x4_altivec.h +9 -0
  88. data/ext/pq_crypto_seal/vendor/libaegis/src/aegis256x4/aegis256x4_avx2.c +107 -0
  89. data/ext/pq_crypto_seal/vendor/libaegis/src/aegis256x4/aegis256x4_avx2.h +11 -0
  90. data/ext/pq_crypto_seal/vendor/libaegis/src/aegis256x4/aegis256x4_avx512.c +79 -0
  91. data/ext/pq_crypto_seal/vendor/libaegis/src/aegis256x4/aegis256x4_avx512.h +11 -0
  92. data/ext/pq_crypto_seal/vendor/libaegis/src/aegis256x4/aegis256x4_common.h +805 -0
  93. data/ext/pq_crypto_seal/vendor/libaegis/src/aegis256x4/aegis256x4_neon_aes.c +117 -0
  94. data/ext/pq_crypto_seal/vendor/libaegis/src/aegis256x4/aegis256x4_neon_aes.h +9 -0
  95. data/ext/pq_crypto_seal/vendor/libaegis/src/aegis256x4/aegis256x4_soft.c +99 -0
  96. data/ext/pq_crypto_seal/vendor/libaegis/src/aegis256x4/aegis256x4_soft.h +9 -0
  97. data/ext/pq_crypto_seal/vendor/libaegis/src/aegis256x4/implementations.h +42 -0
  98. data/ext/pq_crypto_seal/vendor/libaegis/src/common/common.c +81 -0
  99. data/ext/pq_crypto_seal/vendor/libaegis/src/common/common.h +144 -0
  100. data/ext/pq_crypto_seal/vendor/libaegis/src/common/cpu.c +378 -0
  101. data/ext/pq_crypto_seal/vendor/libaegis/src/common/cpu.h +34 -0
  102. data/ext/pq_crypto_seal/vendor/libaegis/src/common/keccak.c +187 -0
  103. data/ext/pq_crypto_seal/vendor/libaegis/src/common/keccak.h +13 -0
  104. data/ext/pq_crypto_seal/vendor/libaegis/src/common/softaes.c +338 -0
  105. data/ext/pq_crypto_seal/vendor/libaegis/src/common/softaes.h +69 -0
  106. data/ext/pq_crypto_seal/vendor/libaegis/src/include/aegis.h +77 -0
  107. data/ext/pq_crypto_seal/vendor/libaegis/src/include/aegis128l.h +311 -0
  108. data/ext/pq_crypto_seal/vendor/libaegis/src/include/aegis128x2.h +313 -0
  109. data/ext/pq_crypto_seal/vendor/libaegis/src/include/aegis128x4.h +313 -0
  110. data/ext/pq_crypto_seal/vendor/libaegis/src/include/aegis256.h +311 -0
  111. data/ext/pq_crypto_seal/vendor/libaegis/src/include/aegis256x2.h +313 -0
  112. data/ext/pq_crypto_seal/vendor/libaegis/src/include/aegis256x4.h +313 -0
  113. data/ext/pq_crypto_seal/vendor/libaegis/src/include/aegis_raf.h +580 -0
  114. data/ext/pq_crypto_seal/vendor/libaegis/src/raf/raf.c +87 -0
  115. data/ext/pq_crypto_seal/vendor/libaegis/src/raf/raf_aegis128l.c +20 -0
  116. data/ext/pq_crypto_seal/vendor/libaegis/src/raf/raf_aegis128x2.c +20 -0
  117. data/ext/pq_crypto_seal/vendor/libaegis/src/raf/raf_aegis128x4.c +20 -0
  118. data/ext/pq_crypto_seal/vendor/libaegis/src/raf/raf_aegis256.c +20 -0
  119. data/ext/pq_crypto_seal/vendor/libaegis/src/raf/raf_aegis256x2.c +20 -0
  120. data/ext/pq_crypto_seal/vendor/libaegis/src/raf/raf_aegis256x4.c +20 -0
  121. data/ext/pq_crypto_seal/vendor/libaegis/src/raf/raf_internal.h +152 -0
  122. data/ext/pq_crypto_seal/vendor/libaegis/src/raf/raf_merkle.c +268 -0
  123. data/ext/pq_crypto_seal/vendor/libaegis/src/raf/raf_merkle.h +165 -0
  124. data/ext/pq_crypto_seal/vendor/libaegis/src/raf/raf_variant.h +1119 -0
  125. data/fuzz/decrypt_fuzz.rb +119 -0
  126. data/lib/pq_crypto/seal/binary.rb +62 -0
  127. data/lib/pq_crypto/seal/core.rb +398 -0
  128. data/lib/pq_crypto/seal/errors.rb +15 -0
  129. data/lib/pq_crypto/seal/format.rb +275 -0
  130. data/lib/pq_crypto/seal/io.rb +336 -0
  131. data/lib/pq_crypto/seal/native.rb +13 -0
  132. data/lib/pq_crypto/seal/padding.rb +47 -0
  133. data/lib/pq_crypto/seal/version.rb +6 -0
  134. data/lib/pq_crypto/seal.rb +32 -0
  135. data/pq_crypto-seal.gemspec +37 -0
  136. data/script/vendor_libs.rb +79 -0
  137. data/test/aegis_vectors_test.rb +54 -0
  138. data/test/golden_vectors_test.rb +183 -0
  139. data/test/io_test.rb +125 -0
  140. data/test/native_test.rb +36 -0
  141. data/test/seal_test.rb +238 -0
  142. data/test/test_helper.rb +6 -0
  143. metadata +244 -0
@@ -0,0 +1,390 @@
1
+ #include "ruby.h"
2
+ #include "ruby/thread.h"
3
+ #include <limits.h>
4
+ #include <stdint.h>
5
+ #include <string.h>
6
+ #include <openssl/crypto.h>
7
+ #include <openssl/evp.h>
8
+ #include <openssl/hmac.h>
9
+ #include <openssl/rand.h>
10
+ #include "aegis.h"
11
+ #include "aegis256.h"
12
+
13
+ #define KEY_BYTES 32
14
+ #define NONCE_BYTES 32
15
+ #define TAG_BYTES 32
16
+ #define NOGVL_THRESHOLD 32768
17
+
18
+ typedef struct {
19
+ aegis256_state state;
20
+ int mode;
21
+ int finalized;
22
+ } seal_state;
23
+
24
+ static VALUE mPQCrypto, mSeal, mNative, cEncryptor, cDecryptor, eAuth;
25
+
26
+ static void secure_free(void *ptr) {
27
+ seal_state *st = (seal_state *)ptr;
28
+ if (st != NULL) {
29
+ OPENSSL_cleanse(st, sizeof(*st));
30
+ xfree(st);
31
+ }
32
+ }
33
+
34
+ static size_t state_memsize(const void *ptr) {
35
+ return ptr ? sizeof(seal_state) : 0;
36
+ }
37
+
38
+ static const rb_data_type_t state_type = {"PQCrypto::Seal::NativeState",
39
+ {0, secure_free, state_memsize, 0, {0}},
40
+ 0,
41
+ 0,
42
+ RUBY_TYPED_FREE_IMMEDIATELY};
43
+
44
+ static VALUE state_alloc(VALUE klass) {
45
+ seal_state *st;
46
+ VALUE obj = TypedData_Make_Struct(klass, seal_state, &state_type, st);
47
+ memset(st, 0, sizeof(*st));
48
+ return obj;
49
+ }
50
+
51
+ static void require_length(VALUE str, long expected, const char *name) {
52
+ StringValue(str);
53
+ if (RSTRING_LEN(str) != expected)
54
+ rb_raise(rb_eArgError, "%s must be %ld bytes", name, expected);
55
+ }
56
+
57
+ static VALUE native_random_bytes(VALUE self, VALUE length_value) {
58
+ (void)self;
59
+ long length = NUM2LONG(length_value);
60
+ if (length < 0)
61
+ rb_raise(rb_eArgError, "length must be non-negative");
62
+ VALUE out = rb_str_new(NULL, length);
63
+ long offset = 0;
64
+ while (offset < length) {
65
+ int chunk = (int)((length - offset) > INT_MAX ? INT_MAX : (length - offset));
66
+ if (RAND_bytes((unsigned char *)RSTRING_PTR(out) + offset, chunk) != 1) {
67
+ rb_raise(rb_eRuntimeError, "OpenSSL RAND_bytes failed");
68
+ }
69
+ offset += chunk;
70
+ }
71
+ return out;
72
+ }
73
+
74
+ static VALUE native_sha256(VALUE self, VALUE input) {
75
+ (void)self;
76
+ StringValue(input);
77
+ unsigned char digest[EVP_MAX_MD_SIZE];
78
+ unsigned int length = 0;
79
+ if (EVP_Digest(RSTRING_PTR(input), (size_t)RSTRING_LEN(input), digest, &length, EVP_sha256(),
80
+ NULL) != 1 ||
81
+ length != 32) {
82
+ rb_raise(rb_eRuntimeError, "OpenSSL SHA-256 failed");
83
+ }
84
+ return rb_str_new((const char *)digest, 32);
85
+ }
86
+
87
+ static VALUE native_hkdf_sha256(VALUE self, VALUE ikm, VALUE info, VALUE length_value) {
88
+ (void)self;
89
+ StringValue(ikm);
90
+ StringValue(info);
91
+
92
+ long requested = NUM2LONG(length_value);
93
+ if (requested <= 0 || requested > 8160) {
94
+ rb_raise(rb_eArgError, "invalid HKDF output length");
95
+ }
96
+
97
+ const long ikm_len_long = RSTRING_LEN(ikm);
98
+ const long info_len_long = RSTRING_LEN(info);
99
+ if (ikm_len_long < 0 || ikm_len_long > INT_MAX) {
100
+ rb_raise(rb_eArgError, "HKDF input key material is too large");
101
+ }
102
+ if (info_len_long < 0) {
103
+ rb_raise(rb_eArgError, "HKDF info length is invalid");
104
+ }
105
+
106
+ const size_t ikm_len = (size_t)ikm_len_long;
107
+ const size_t info_len = (size_t)info_len_long;
108
+ if (info_len > SIZE_MAX - 33U) {
109
+ rb_raise(rb_eArgError, "HKDF info is too large");
110
+ }
111
+
112
+ VALUE out = rb_str_new(NULL, requested);
113
+ const size_t expand_capacity = 32U + info_len + 1U;
114
+ unsigned char *expand_input = ALLOC_N(unsigned char, expand_capacity);
115
+ unsigned char salt[32] = {0};
116
+ unsigned char prk[32] = {0};
117
+ unsigned char t[32] = {0};
118
+ unsigned int digest_len = 0;
119
+ size_t t_len = 0;
120
+ size_t produced = 0;
121
+ unsigned int block = 1;
122
+
123
+ if (HMAC(EVP_sha256(), salt, (int)sizeof(salt), (const unsigned char *)RSTRING_PTR(ikm),
124
+ ikm_len, prk, &digest_len) == NULL ||
125
+ digest_len != sizeof(prk)) {
126
+ OPENSSL_cleanse(expand_input, expand_capacity);
127
+ xfree(expand_input);
128
+ OPENSSL_cleanse(prk, sizeof(prk));
129
+ OPENSSL_cleanse(t, sizeof(t));
130
+ rb_raise(rb_eRuntimeError, "OpenSSL HMAC-SHA256 extract failed");
131
+ }
132
+
133
+ while (produced < (size_t)requested) {
134
+ size_t input_len = 0;
135
+ if (t_len != 0) {
136
+ memcpy(expand_input, t, t_len);
137
+ input_len += t_len;
138
+ }
139
+ if (info_len != 0) {
140
+ memcpy(expand_input + input_len, RSTRING_PTR(info), info_len);
141
+ input_len += info_len;
142
+ }
143
+ expand_input[input_len++] = (unsigned char)block;
144
+
145
+ digest_len = 0;
146
+ if (HMAC(EVP_sha256(), prk, (int)sizeof(prk), expand_input, input_len, t, &digest_len) ==
147
+ NULL ||
148
+ digest_len != sizeof(t)) {
149
+ OPENSSL_cleanse(expand_input, expand_capacity);
150
+ xfree(expand_input);
151
+ OPENSSL_cleanse(prk, sizeof(prk));
152
+ OPENSSL_cleanse(t, sizeof(t));
153
+ rb_raise(rb_eRuntimeError, "OpenSSL HMAC-SHA256 expand failed");
154
+ }
155
+
156
+ t_len = sizeof(t);
157
+ size_t remaining = (size_t)requested - produced;
158
+ size_t copy_len = remaining < t_len ? remaining : t_len;
159
+ memcpy((unsigned char *)RSTRING_PTR(out) + produced, t, copy_len);
160
+ produced += copy_len;
161
+ block++;
162
+ }
163
+
164
+ OPENSSL_cleanse(expand_input, expand_capacity);
165
+ xfree(expand_input);
166
+ OPENSSL_cleanse(prk, sizeof(prk));
167
+ OPENSSL_cleanse(t, sizeof(t));
168
+ RB_GC_GUARD(ikm);
169
+ RB_GC_GUARD(info);
170
+ return out;
171
+ }
172
+
173
+ static VALUE native_hkdf_backend(VALUE self) {
174
+ (void)self;
175
+ return rb_str_new_cstr("rfc5869-hmac-sha256-v1");
176
+ }
177
+
178
+ typedef struct {
179
+ const uint8_t *key, *nonce, *ad, *input, *tag;
180
+ uint8_t *output, *out_tag;
181
+ size_t adlen, input_len;
182
+ int result;
183
+ } one_shot_args;
184
+
185
+ static void *encrypt_without_gvl(void *ptr) {
186
+ one_shot_args *a = (one_shot_args *)ptr;
187
+ a->result = aegis256_encrypt_detached(a->output, a->out_tag, TAG_BYTES, a->input, a->input_len,
188
+ a->ad, a->adlen, a->nonce, a->key);
189
+ return NULL;
190
+ }
191
+
192
+ static void *decrypt_without_gvl(void *ptr) {
193
+ one_shot_args *a = (one_shot_args *)ptr;
194
+ a->result = aegis256_decrypt_detached(a->output, a->input, a->input_len, a->tag, TAG_BYTES,
195
+ a->ad, a->adlen, a->nonce, a->key);
196
+ return NULL;
197
+ }
198
+
199
+ static VALUE native_encrypt(VALUE self, VALUE key, VALUE nonce, VALUE ad, VALUE plaintext) {
200
+ (void)self;
201
+ require_length(key, KEY_BYTES, "key");
202
+ require_length(nonce, NONCE_BYTES, "nonce");
203
+ StringValue(ad);
204
+ StringValue(plaintext);
205
+ VALUE ciphertext = rb_str_new(NULL, RSTRING_LEN(plaintext));
206
+ VALUE tag = rb_str_new(NULL, TAG_BYTES);
207
+ one_shot_args args = {(uint8_t *)RSTRING_PTR(key),
208
+ (uint8_t *)RSTRING_PTR(nonce),
209
+ (uint8_t *)RSTRING_PTR(ad),
210
+ (uint8_t *)RSTRING_PTR(plaintext),
211
+ NULL,
212
+ (uint8_t *)RSTRING_PTR(ciphertext),
213
+ (uint8_t *)RSTRING_PTR(tag),
214
+ (size_t)RSTRING_LEN(ad),
215
+ (size_t)RSTRING_LEN(plaintext),
216
+ -1};
217
+ if (args.input_len >= NOGVL_THRESHOLD)
218
+ rb_thread_call_without_gvl(encrypt_without_gvl, &args, RUBY_UBF_IO, NULL);
219
+ else
220
+ encrypt_without_gvl(&args);
221
+ RB_GC_GUARD(key);
222
+ RB_GC_GUARD(nonce);
223
+ RB_GC_GUARD(ad);
224
+ RB_GC_GUARD(plaintext);
225
+ if (args.result != 0)
226
+ rb_raise(rb_eRuntimeError, "AEGIS-256 encryption failed");
227
+ return rb_ary_new_from_args(2, ciphertext, tag);
228
+ }
229
+
230
+ static VALUE native_decrypt(VALUE self, VALUE key, VALUE nonce, VALUE ad, VALUE ciphertext,
231
+ VALUE tag) {
232
+ (void)self;
233
+ require_length(key, KEY_BYTES, "key");
234
+ require_length(nonce, NONCE_BYTES, "nonce");
235
+ require_length(tag, TAG_BYTES, "tag");
236
+ StringValue(ad);
237
+ StringValue(ciphertext);
238
+ VALUE plaintext = rb_str_new(NULL, RSTRING_LEN(ciphertext));
239
+ one_shot_args args = {(uint8_t *)RSTRING_PTR(key),
240
+ (uint8_t *)RSTRING_PTR(nonce),
241
+ (uint8_t *)RSTRING_PTR(ad),
242
+ (uint8_t *)RSTRING_PTR(ciphertext),
243
+ (uint8_t *)RSTRING_PTR(tag),
244
+ (uint8_t *)RSTRING_PTR(plaintext),
245
+ NULL,
246
+ (size_t)RSTRING_LEN(ad),
247
+ (size_t)RSTRING_LEN(ciphertext),
248
+ -1};
249
+ if (args.input_len >= NOGVL_THRESHOLD)
250
+ rb_thread_call_without_gvl(decrypt_without_gvl, &args, RUBY_UBF_IO, NULL);
251
+ else
252
+ decrypt_without_gvl(&args);
253
+ RB_GC_GUARD(key);
254
+ RB_GC_GUARD(nonce);
255
+ RB_GC_GUARD(ad);
256
+ RB_GC_GUARD(ciphertext);
257
+ RB_GC_GUARD(tag);
258
+ if (args.result != 0) {
259
+ OPENSSL_cleanse(RSTRING_PTR(plaintext), (size_t)RSTRING_LEN(plaintext));
260
+ rb_raise(eAuth, "AEGIS-256 authentication failed");
261
+ }
262
+ return plaintext;
263
+ }
264
+
265
+ static VALUE native_secure_equal(VALUE self, VALUE a, VALUE b) {
266
+ (void)self;
267
+ StringValue(a);
268
+ StringValue(b);
269
+ if (RSTRING_LEN(a) != RSTRING_LEN(b))
270
+ return Qfalse;
271
+ return CRYPTO_memcmp(RSTRING_PTR(a), RSTRING_PTR(b), (size_t)RSTRING_LEN(a)) == 0 ? Qtrue
272
+ : Qfalse;
273
+ }
274
+
275
+ static VALUE state_initialize(VALUE self, VALUE key, VALUE nonce, VALUE ad) {
276
+ seal_state *st;
277
+ TypedData_Get_Struct(self, seal_state, &state_type, st);
278
+ require_length(key, KEY_BYTES, "key");
279
+ require_length(nonce, NONCE_BYTES, "nonce");
280
+ StringValue(ad);
281
+ aegis256_state_init(&st->state, (uint8_t *)RSTRING_PTR(ad), (size_t)RSTRING_LEN(ad),
282
+ (uint8_t *)RSTRING_PTR(nonce), (uint8_t *)RSTRING_PTR(key));
283
+ st->finalized = 0;
284
+ st->mode = rb_obj_is_kind_of(self, cEncryptor) ? 1 : 2;
285
+ return self;
286
+ }
287
+
288
+ typedef struct {
289
+ seal_state *st;
290
+ const uint8_t *in;
291
+ uint8_t *out;
292
+ size_t len;
293
+ int result;
294
+ } update_args;
295
+ static void *update_encrypt_nogvl(void *ptr) {
296
+ update_args *a = ptr;
297
+ a->result = aegis256_state_encrypt_update(&a->st->state, a->out, a->in, a->len);
298
+ return NULL;
299
+ }
300
+ static void *update_decrypt_nogvl(void *ptr) {
301
+ update_args *a = ptr;
302
+ a->result = aegis256_state_decrypt_update(&a->st->state, a->out, a->in, a->len);
303
+ return NULL;
304
+ }
305
+
306
+ static VALUE state_update(VALUE self, VALUE input) {
307
+ seal_state *st;
308
+ TypedData_Get_Struct(self, seal_state, &state_type, st);
309
+ if (st->mode != 1 && st->mode != 2)
310
+ rb_raise(rb_eRuntimeError, "AEGIS state is not initialized");
311
+ if (st->finalized)
312
+ rb_raise(rb_eRuntimeError, "AEGIS state is finalized");
313
+ StringValue(input);
314
+ VALUE out = rb_str_new(NULL, RSTRING_LEN(input));
315
+ update_args args = {st, (uint8_t *)RSTRING_PTR(input), (uint8_t *)RSTRING_PTR(out),
316
+ (size_t)RSTRING_LEN(input), -1};
317
+ void *(*fn)(void *) = st->mode == 1 ? update_encrypt_nogvl : update_decrypt_nogvl;
318
+ if (args.len >= NOGVL_THRESHOLD)
319
+ rb_thread_call_without_gvl(fn, &args, RUBY_UBF_IO, NULL);
320
+ else
321
+ fn(&args);
322
+ RB_GC_GUARD(input);
323
+ if (args.result != 0)
324
+ rb_raise(rb_eRuntimeError, "AEGIS-256 update failed");
325
+ return out;
326
+ }
327
+
328
+ static VALUE encrypt_final(VALUE self) {
329
+ seal_state *st;
330
+ TypedData_Get_Struct(self, seal_state, &state_type, st);
331
+ if (st->mode != 1)
332
+ rb_raise(rb_eRuntimeError, "AEGIS encrypt state is not initialized");
333
+ if (st->finalized)
334
+ rb_raise(rb_eRuntimeError, "AEGIS state is finalized");
335
+ VALUE tag = rb_str_new(NULL, TAG_BYTES);
336
+ int result = aegis256_state_encrypt_final(&st->state, (uint8_t *)RSTRING_PTR(tag), TAG_BYTES);
337
+ st->finalized = 1;
338
+ OPENSSL_cleanse(&st->state, sizeof(st->state));
339
+ if (result != 0)
340
+ rb_raise(rb_eRuntimeError, "AEGIS-256 finalization failed");
341
+ return tag;
342
+ }
343
+
344
+ static VALUE decrypt_final(VALUE self, VALUE tag) {
345
+ seal_state *st;
346
+ TypedData_Get_Struct(self, seal_state, &state_type, st);
347
+ if (st->mode != 2)
348
+ rb_raise(rb_eRuntimeError, "AEGIS decrypt state is not initialized");
349
+ if (st->finalized)
350
+ rb_raise(rb_eRuntimeError, "AEGIS state is finalized");
351
+ require_length(tag, TAG_BYTES, "tag");
352
+ int result = aegis256_state_decrypt_final(&st->state, (uint8_t *)RSTRING_PTR(tag), TAG_BYTES);
353
+ st->finalized = 1;
354
+ OPENSSL_cleanse(&st->state, sizeof(st->state));
355
+ if (result != 0)
356
+ rb_raise(eAuth, "AEGIS-256 authentication failed");
357
+ return Qtrue;
358
+ }
359
+
360
+ void Init_pq_crypto_seal(void) {
361
+ if (OpenSSL_version_num() < 0x30000000L) {
362
+ rb_raise(rb_eRuntimeError, "pq_crypto-seal requires OpenSSL 3.0 or newer at runtime");
363
+ }
364
+ if (aegis_init() != 0)
365
+ rb_raise(rb_eRuntimeError, "libaegis initialization failed");
366
+ mPQCrypto = rb_define_module("PQCrypto");
367
+ mSeal = rb_define_module_under(mPQCrypto, "Seal");
368
+ mNative = rb_define_module_under(mSeal, "Native");
369
+ eAuth = rb_path2class("PQCrypto::Seal::AuthenticationError");
370
+
371
+ rb_define_singleton_method(mNative, "random_bytes", native_random_bytes, 1);
372
+ rb_define_singleton_method(mNative, "sha256", native_sha256, 1);
373
+ rb_define_singleton_method(mNative, "hkdf_sha256", native_hkdf_sha256, 3);
374
+ rb_define_singleton_method(mNative, "hkdf_backend", native_hkdf_backend, 0);
375
+ rb_define_singleton_method(mNative, "aegis256_encrypt", native_encrypt, 4);
376
+ rb_define_singleton_method(mNative, "aegis256_decrypt", native_decrypt, 5);
377
+ rb_define_singleton_method(mNative, "secure_equal", native_secure_equal, 2);
378
+
379
+ cEncryptor = rb_define_class_under(mNative, "Encryptor", rb_cObject);
380
+ rb_define_alloc_func(cEncryptor, state_alloc);
381
+ rb_define_method(cEncryptor, "initialize", state_initialize, 3);
382
+ rb_define_method(cEncryptor, "update", state_update, 1);
383
+ rb_define_method(cEncryptor, "final", encrypt_final, 0);
384
+
385
+ cDecryptor = rb_define_class_under(mNative, "Decryptor", rb_cObject);
386
+ rb_define_alloc_func(cDecryptor, state_alloc);
387
+ rb_define_method(cDecryptor, "initialize", state_initialize, 3);
388
+ rb_define_method(cDecryptor, "update", state_update, 1);
389
+ rb_define_method(cDecryptor, "final", decrypt_final, 1);
390
+ }
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025-2026 Frank Denis
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1 @@
1
+ c2f4594acaf51785960fb1dda1cb80d663d54e671c6e465197f7924b338b8ef7
@@ -0,0 +1,246 @@
1
+ #include <stddef.h>
2
+ #include <stdint.h>
3
+
4
+ #include "../common/common.h"
5
+ #include "../common/cpu.h"
6
+ #include "aegis128l.h"
7
+ #include "aegis128l_aesni.h"
8
+ #include "aegis128l_altivec.h"
9
+ #include "aegis128l_neon_aes.h"
10
+ #include "aegis128l_neon_sha3.h"
11
+
12
+ #ifndef HAS_HW_AES
13
+ # include "aegis128l_soft.h"
14
+ static const aegis128l_implementation *implementation = &aegis128l_soft_implementation;
15
+ #else
16
+ # if defined(__aarch64__) || defined(_M_ARM64)
17
+ static const aegis128l_implementation *implementation = &aegis128l_neon_aes_implementation;
18
+ # elif defined(__x86_64__) || defined(__i386__)
19
+ static const aegis128l_implementation *implementation = &aegis128l_aesni_implementation;
20
+ # elif defined(__ALTIVEC__) && defined(__CRYPTO__)
21
+ static const aegis128l_implementation *implementation = &aegis128l_altivec_implementation;
22
+ # else
23
+ # error "Unsupported architecture"
24
+ # endif
25
+ #endif
26
+
27
+ size_t
28
+ aegis128l_keybytes(void)
29
+ {
30
+ return aegis128l_KEYBYTES;
31
+ }
32
+
33
+ size_t
34
+ aegis128l_npubbytes(void)
35
+ {
36
+ return aegis128l_NPUBBYTES;
37
+ }
38
+
39
+ size_t
40
+ aegis128l_abytes_min(void)
41
+ {
42
+ return aegis128l_ABYTES_MIN;
43
+ }
44
+
45
+ size_t
46
+ aegis128l_abytes_max(void)
47
+ {
48
+ return aegis128l_ABYTES_MAX;
49
+ }
50
+
51
+ size_t
52
+ aegis128l_tailbytes_max(void)
53
+ {
54
+ return aegis128l_TAILBYTES_MAX;
55
+ }
56
+
57
+ int
58
+ aegis128l_encrypt_detached(uint8_t *c, uint8_t *mac, size_t maclen, const uint8_t *m, size_t mlen,
59
+ const uint8_t *ad, size_t adlen, const uint8_t *npub, const uint8_t *k)
60
+ {
61
+ if (maclen != 16 && maclen != 32) {
62
+ errno = EINVAL;
63
+ return -1;
64
+ }
65
+ return implementation->encrypt_detached(c, mac, maclen, m, mlen, ad, adlen, npub, k);
66
+ }
67
+
68
+ int
69
+ aegis128l_decrypt_detached(uint8_t *m, const uint8_t *c, size_t clen, const uint8_t *mac,
70
+ size_t maclen, const uint8_t *ad, size_t adlen, const uint8_t *npub,
71
+ const uint8_t *k)
72
+ {
73
+ if (maclen != 16 && maclen != 32) {
74
+ errno = EINVAL;
75
+ return -1;
76
+ }
77
+ return implementation->decrypt_detached(m, c, clen, mac, maclen, ad, adlen, npub, k);
78
+ }
79
+
80
+ int
81
+ aegis128l_encrypt(uint8_t *c, size_t maclen, const uint8_t *m, size_t mlen, const uint8_t *ad,
82
+ size_t adlen, const uint8_t *npub, const uint8_t *k)
83
+ {
84
+ return aegis128l_encrypt_detached(c, c + mlen, maclen, m, mlen, ad, adlen, npub, k);
85
+ }
86
+
87
+ int
88
+ aegis128l_decrypt(uint8_t *m, const uint8_t *c, size_t clen, size_t maclen, const uint8_t *ad,
89
+ size_t adlen, const uint8_t *npub, const uint8_t *k)
90
+ {
91
+ int ret = -1;
92
+
93
+ if (clen >= maclen) {
94
+ ret = aegis128l_decrypt_detached(m, c, clen - maclen, c + clen - maclen, maclen, ad, adlen,
95
+ npub, k);
96
+ }
97
+ return ret;
98
+ }
99
+
100
+ void
101
+ aegis128l_state_init(aegis128l_state *st_, const uint8_t *ad, size_t adlen, const uint8_t *npub,
102
+ const uint8_t *k)
103
+ {
104
+ memset(st_, 0, sizeof *st_);
105
+ implementation->state_init(st_, ad, adlen, npub, k);
106
+ }
107
+
108
+ int
109
+ aegis128l_state_encrypt_update(aegis128l_state *st_, uint8_t *c, const uint8_t *m, size_t mlen)
110
+ {
111
+ return implementation->state_encrypt_update(st_, c, m, mlen);
112
+ }
113
+
114
+ int
115
+ aegis128l_state_encrypt_final(aegis128l_state *st_, uint8_t *mac, size_t maclen)
116
+ {
117
+ if (maclen != 16 && maclen != 32) {
118
+ errno = EINVAL;
119
+ return -1;
120
+ }
121
+ return implementation->state_encrypt_final(st_, mac, maclen);
122
+ }
123
+
124
+ int
125
+ aegis128l_state_decrypt_update(aegis128l_state *st_, uint8_t *m, const uint8_t *c, size_t clen)
126
+ {
127
+ return implementation->state_decrypt_update(st_, m, c, clen);
128
+ }
129
+
130
+ int
131
+ aegis128l_state_decrypt_final(aegis128l_state *st_, const uint8_t *mac, size_t maclen)
132
+ {
133
+ if (maclen != 16 && maclen != 32) {
134
+ errno = EINVAL;
135
+ return -1;
136
+ }
137
+ return implementation->state_decrypt_final(st_, mac, maclen);
138
+ }
139
+
140
+ void
141
+ aegis128l_stream(uint8_t *out, size_t len, const uint8_t *npub, const uint8_t *k)
142
+ {
143
+ implementation->stream(out, len, npub, k);
144
+ }
145
+
146
+ void
147
+ aegis128l_encrypt_unauthenticated(uint8_t *c, const uint8_t *m, size_t mlen, const uint8_t *npub,
148
+ const uint8_t *k)
149
+ {
150
+ implementation->encrypt_unauthenticated(c, m, mlen, npub, k);
151
+ }
152
+
153
+ void
154
+ aegis128l_decrypt_unauthenticated(uint8_t *m, const uint8_t *c, size_t clen, const uint8_t *npub,
155
+ const uint8_t *k)
156
+ {
157
+ implementation->decrypt_unauthenticated(m, c, clen, npub, k);
158
+ }
159
+
160
+ void
161
+ aegis128l_mac_init(aegis128l_mac_state *st_, const uint8_t *k, const uint8_t *npub)
162
+ {
163
+ memset(st_, 0, sizeof *st_);
164
+ implementation->state_mac_init(st_, npub, k);
165
+ }
166
+
167
+ int
168
+ aegis128l_mac_update(aegis128l_mac_state *st_, const uint8_t *m, size_t mlen)
169
+ {
170
+ return implementation->state_mac_update(st_, m, mlen);
171
+ }
172
+
173
+ int
174
+ aegis128l_mac_final(aegis128l_mac_state *st_, uint8_t *mac, size_t maclen)
175
+ {
176
+ if (maclen != 16 && maclen != 32) {
177
+ errno = EINVAL;
178
+ return -1;
179
+ }
180
+ return implementation->state_mac_final(st_, mac, maclen);
181
+ }
182
+
183
+ int
184
+ aegis128l_mac_verify(aegis128l_mac_state *st_, const uint8_t *mac, size_t maclen)
185
+ {
186
+ uint8_t expected_mac[32];
187
+
188
+ switch (maclen) {
189
+ case 16:
190
+ implementation->state_mac_final(st_, expected_mac, maclen);
191
+ return aegis_verify_16(expected_mac, mac);
192
+ case 32:
193
+ implementation->state_mac_final(st_, expected_mac, maclen);
194
+ return aegis_verify_32(expected_mac, mac);
195
+ default:
196
+ errno = EINVAL;
197
+ return -1;
198
+ }
199
+ }
200
+
201
+ void
202
+ aegis128l_mac_reset(aegis128l_mac_state *st_)
203
+ {
204
+ implementation->state_mac_reset(st_);
205
+ }
206
+
207
+ void
208
+ aegis128l_mac_state_clone(aegis128l_mac_state *dst, const aegis128l_mac_state *src)
209
+ {
210
+ implementation->state_mac_clone(dst, src);
211
+ }
212
+
213
+ int
214
+ aegis128l_pick_best_implementation(void)
215
+ {
216
+ #ifndef HAS_HW_AES
217
+ implementation = &aegis128l_soft_implementation;
218
+ #endif
219
+
220
+ #if defined(__aarch64__) || defined(_M_ARM64)
221
+ if (aegis_runtime_has_neon_sha3()) {
222
+ implementation = &aegis128l_neon_sha3_implementation;
223
+ return 0;
224
+ }
225
+ if (aegis_runtime_has_neon_aes()) {
226
+ implementation = &aegis128l_neon_aes_implementation;
227
+ return 0;
228
+ }
229
+ #endif
230
+
231
+ #if defined(__x86_64__) || defined(_M_AMD64) || defined(__i386__) || defined(_M_IX86)
232
+ if (aegis_runtime_has_aesni() && aegis_runtime_has_avx()) {
233
+ implementation = &aegis128l_aesni_implementation;
234
+ return 0;
235
+ }
236
+ #endif
237
+
238
+ #if defined(__ALTIVEC__) && defined(__CRYPTO__)
239
+ if (aegis_runtime_has_altivec()) {
240
+ implementation = &aegis128l_altivec_implementation;
241
+ return 0;
242
+ }
243
+ #endif
244
+
245
+ return 0; /* LCOV_EXCL_LINE */
246
+ }