pq_crypto 0.6.6 → 0.6.8
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +108 -0
- data/README.md +11 -0
- data/SECURITY.md +46 -0
- data/ext/pqcrypto/extconf.rb +4 -0
- data/ext/pqcrypto/pqcrypto_native_api.h +6 -0
- data/ext/pqcrypto/pqcrypto_ruby_secure.c +122 -11
- data/ext/pqcrypto/pqcrypto_secure.c +553 -296
- data/ext/pqcrypto/pqcrypto_secure.h +14 -1
- data/ext/pqcrypto/pqcrypto_version.h +1 -1
- data/lib/pq_crypto/internal.rb +20 -0
- data/lib/pq_crypto/kem.rb +52 -15
- data/lib/pq_crypto/key.rb +14 -8
- data/lib/pq_crypto/pkcs8/der.rb +26 -11
- data/lib/pq_crypto/pkcs8/private_key_choice.rb +21 -25
- data/lib/pq_crypto/pkcs8.rb +13 -5
- data/lib/pq_crypto/signature.rb +61 -48
- data/lib/pq_crypto/spki.rb +39 -24
- data/lib/pq_crypto/version.rb +1 -1
- data/lib/pq_crypto.rb +9 -0
- metadata +1 -1
|
@@ -265,12 +265,64 @@ cleanup:
|
|
|
265
265
|
return PQ_ERROR_BUFFER; \
|
|
266
266
|
} \
|
|
267
267
|
return native##_dec(ss, ct, sk) == 0 ? PQ_SUCCESS : PQ_ERROR_DECAPSULATE; \
|
|
268
|
+
} \
|
|
269
|
+
int pq_##prefix##_check_public_key(const uint8_t *pk) { \
|
|
270
|
+
int rc; \
|
|
271
|
+
if (!pk) { \
|
|
272
|
+
return PQ_ERROR_BUFFER; \
|
|
273
|
+
} \
|
|
274
|
+
rc = native##_check_pk(pk); \
|
|
275
|
+
if (rc == 0) { \
|
|
276
|
+
return PQ_SUCCESS; \
|
|
277
|
+
} \
|
|
278
|
+
if (rc == -2) { \
|
|
279
|
+
return PQ_ERROR_NOMEM; \
|
|
280
|
+
} \
|
|
281
|
+
return PQ_ERROR_INVALID_PUBLIC_KEY; \
|
|
282
|
+
} \
|
|
283
|
+
int pq_##prefix##_check_secret_key(const uint8_t *sk) { \
|
|
284
|
+
int rc; \
|
|
285
|
+
if (!sk) { \
|
|
286
|
+
return PQ_ERROR_BUFFER; \
|
|
287
|
+
} \
|
|
288
|
+
rc = native##_check_sk(sk); \
|
|
289
|
+
if (rc == 0) { \
|
|
290
|
+
return PQ_SUCCESS; \
|
|
291
|
+
} \
|
|
292
|
+
if (rc == -2) { \
|
|
293
|
+
return PQ_ERROR_NOMEM; \
|
|
294
|
+
} \
|
|
295
|
+
return PQ_ERROR_INVALID_SECRET_KEY; \
|
|
268
296
|
}
|
|
269
297
|
|
|
270
298
|
PQ_MLKEM_VARIANTS(PQ_DEFINE_MLKEM_SHIMS)
|
|
271
299
|
|
|
272
300
|
#undef PQ_DEFINE_MLKEM_SHIMS
|
|
273
301
|
|
|
302
|
+
#define PQ_DEFINE_MLDSA_CHECK_SK(prefix, native, pk_bytes) \
|
|
303
|
+
int pq_##prefix##_check_secret_key(const uint8_t *sk) { \
|
|
304
|
+
uint8_t pk[(pk_bytes)]; \
|
|
305
|
+
int rc; \
|
|
306
|
+
if (!sk) { \
|
|
307
|
+
return PQ_ERROR_BUFFER; \
|
|
308
|
+
} \
|
|
309
|
+
rc = native##_pk_from_sk(pk, sk); \
|
|
310
|
+
pq_secure_wipe(pk, sizeof(pk)); \
|
|
311
|
+
if (rc == 0) { \
|
|
312
|
+
return PQ_SUCCESS; \
|
|
313
|
+
} \
|
|
314
|
+
if (rc == -2) { \
|
|
315
|
+
return PQ_ERROR_NOMEM; \
|
|
316
|
+
} \
|
|
317
|
+
return PQ_ERROR_INVALID_SECRET_KEY; \
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
PQ_DEFINE_MLDSA_CHECK_SK(mldsa, pqcr_mldsa65, MLDSA65_PUBLICKEYBYTES)
|
|
321
|
+
PQ_DEFINE_MLDSA_CHECK_SK(mldsa44, pqcr_mldsa44, MLDSA44_PUBLICKEYBYTES)
|
|
322
|
+
PQ_DEFINE_MLDSA_CHECK_SK(mldsa87, pqcr_mldsa87, MLDSA87_PUBLICKEYBYTES)
|
|
323
|
+
|
|
324
|
+
#undef PQ_DEFINE_MLDSA_CHECK_SK
|
|
325
|
+
|
|
274
326
|
static int pq_testing_mlkem_keypair_from_seed_with(uint8_t *public_key, uint8_t *secret_key,
|
|
275
327
|
const uint8_t *seed, size_t seed_len,
|
|
276
328
|
int (*keypair_derand)(uint8_t *, uint8_t *,
|
|
@@ -731,13 +783,46 @@ static const pq_serialization_algorithm_t *pq_find_serialization_algorithm_by_oi
|
|
|
731
783
|
return NULL;
|
|
732
784
|
}
|
|
733
785
|
|
|
786
|
+
static int pq_serialized_key_size(size_t oid_len, size_t key_len, size_t *total_len) {
|
|
787
|
+
const size_t parts[] = {8, oid_len, 4, key_len};
|
|
788
|
+
size_t total = 0;
|
|
789
|
+
|
|
790
|
+
for (size_t i = 0; i < sizeof(parts) / sizeof(parts[0]); ++i) {
|
|
791
|
+
if (pq_size_add(total, parts[i], &total) != PQ_SUCCESS)
|
|
792
|
+
return PQ_ERROR_BUFFER;
|
|
793
|
+
}
|
|
794
|
+
*total_len = total;
|
|
795
|
+
return PQ_SUCCESS;
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
static void pq_write_u16be(uint8_t *output, uint16_t value) {
|
|
799
|
+
output[0] = (uint8_t)(value >> 8);
|
|
800
|
+
output[1] = (uint8_t)value;
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
static void pq_write_u32be(uint8_t *output, uint32_t value) {
|
|
804
|
+
output[0] = (uint8_t)(value >> 24);
|
|
805
|
+
output[1] = (uint8_t)(value >> 16);
|
|
806
|
+
output[2] = (uint8_t)(value >> 8);
|
|
807
|
+
output[3] = (uint8_t)value;
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
static uint16_t pq_read_u16be(const uint8_t *input) {
|
|
811
|
+
return (uint16_t)(((uint16_t)input[0] << 8) | input[1]);
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
static uint32_t pq_read_u32be(const uint8_t *input) {
|
|
815
|
+
return ((uint32_t)input[0] << 24) | ((uint32_t)input[1] << 16) | ((uint32_t)input[2] << 8) |
|
|
816
|
+
input[3];
|
|
817
|
+
}
|
|
818
|
+
|
|
734
819
|
static int pq_encode_serialized_key(uint8_t **output, size_t *output_len, uint8_t type,
|
|
735
820
|
const uint8_t *key_bytes, size_t key_len,
|
|
736
821
|
const char *algorithm) {
|
|
737
822
|
const pq_serialization_algorithm_t *entry;
|
|
738
823
|
size_t expected_len;
|
|
739
824
|
size_t oid_len;
|
|
740
|
-
size_t total_len
|
|
825
|
+
size_t total_len;
|
|
741
826
|
uint8_t *buf;
|
|
742
827
|
int ret;
|
|
743
828
|
|
|
@@ -762,19 +847,7 @@ static int pq_encode_serialized_key(uint8_t **output, size_t *output_len, uint8_
|
|
|
762
847
|
if (key_len > UINT32_MAX)
|
|
763
848
|
return PQ_ERROR_BUFFER;
|
|
764
849
|
|
|
765
|
-
ret =
|
|
766
|
-
if (ret != PQ_SUCCESS)
|
|
767
|
-
return ret;
|
|
768
|
-
ret = pq_size_add(total_len, 1 + 1 + 2, &total_len);
|
|
769
|
-
if (ret != PQ_SUCCESS)
|
|
770
|
-
return ret;
|
|
771
|
-
ret = pq_size_add(total_len, oid_len, &total_len);
|
|
772
|
-
if (ret != PQ_SUCCESS)
|
|
773
|
-
return ret;
|
|
774
|
-
ret = pq_size_add(total_len, 4, &total_len);
|
|
775
|
-
if (ret != PQ_SUCCESS)
|
|
776
|
-
return ret;
|
|
777
|
-
ret = pq_size_add(total_len, key_len, &total_len);
|
|
850
|
+
ret = pq_serialized_key_size(oid_len, key_len, &total_len);
|
|
778
851
|
if (ret != PQ_SUCCESS)
|
|
779
852
|
return ret;
|
|
780
853
|
|
|
@@ -788,13 +861,9 @@ static int pq_encode_serialized_key(uint8_t **output, size_t *output_len, uint8_
|
|
|
788
861
|
buf[3] = PQC_SERIALIZATION_MAGIC_3;
|
|
789
862
|
buf[4] = PQC_SERIALIZATION_VERSION;
|
|
790
863
|
buf[5] = type;
|
|
791
|
-
buf
|
|
792
|
-
buf[7] = (uint8_t)(oid_len & 0xFF);
|
|
864
|
+
pq_write_u16be(buf + 6, (uint16_t)oid_len);
|
|
793
865
|
memcpy(buf + 8, entry->oid, oid_len);
|
|
794
|
-
buf
|
|
795
|
-
buf[8 + oid_len + 1] = (uint8_t)((key_len >> 16) & 0xFF);
|
|
796
|
-
buf[8 + oid_len + 2] = (uint8_t)((key_len >> 8) & 0xFF);
|
|
797
|
-
buf[8 + oid_len + 3] = (uint8_t)(key_len & 0xFF);
|
|
866
|
+
pq_write_u32be(buf + 8 + oid_len, (uint32_t)key_len);
|
|
798
867
|
memcpy(buf + 8 + oid_len + 4, key_bytes, key_len);
|
|
799
868
|
|
|
800
869
|
*output = buf;
|
|
@@ -802,62 +871,92 @@ static int pq_encode_serialized_key(uint8_t **output, size_t *output_len, uint8_
|
|
|
802
871
|
return PQ_SUCCESS;
|
|
803
872
|
}
|
|
804
873
|
|
|
805
|
-
static int
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
uint32_t key_len;
|
|
809
|
-
const pq_serialization_algorithm_t *entry;
|
|
810
|
-
size_t offset;
|
|
811
|
-
size_t expected_len = 0;
|
|
812
|
-
uint8_t *key_copy = NULL;
|
|
813
|
-
char *algorithm_copy = NULL;
|
|
814
|
-
|
|
815
|
-
if (!input || !algorithm_out || !key_out || !key_len_out)
|
|
874
|
+
static int pq_serialized_key_header(uint8_t expected_type, const uint8_t *input, size_t input_len,
|
|
875
|
+
uint16_t *oid_len_out) {
|
|
876
|
+
if (!input || !oid_len_out)
|
|
816
877
|
return PQ_ERROR_BUFFER;
|
|
817
|
-
|
|
818
|
-
*algorithm_out = NULL;
|
|
819
|
-
*key_out = NULL;
|
|
820
|
-
*key_len_out = 0;
|
|
821
|
-
|
|
822
878
|
if (input_len < 12)
|
|
823
879
|
return PQ_ERROR_BUFFER;
|
|
824
880
|
if (input[0] != PQC_SERIALIZATION_MAGIC_0 || input[1] != PQC_SERIALIZATION_MAGIC_1 ||
|
|
825
|
-
input[2] != PQC_SERIALIZATION_MAGIC_2 || input[3] != PQC_SERIALIZATION_MAGIC_3)
|
|
881
|
+
input[2] != PQC_SERIALIZATION_MAGIC_2 || input[3] != PQC_SERIALIZATION_MAGIC_3)
|
|
826
882
|
return PQ_ERROR_BUFFER;
|
|
827
|
-
}
|
|
828
883
|
if (input[4] != PQC_SERIALIZATION_VERSION || input[5] != expected_type)
|
|
829
884
|
return PQ_ERROR_BUFFER;
|
|
830
885
|
|
|
831
|
-
|
|
832
|
-
if (
|
|
886
|
+
*oid_len_out = pq_read_u16be(input + 6);
|
|
887
|
+
if (*oid_len_out == 0 || input_len - 8 < (size_t)*oid_len_out + 4)
|
|
833
888
|
return PQ_ERROR_BUFFER;
|
|
834
|
-
|
|
835
|
-
|
|
889
|
+
return PQ_SUCCESS;
|
|
890
|
+
}
|
|
891
|
+
|
|
892
|
+
static int pq_serialized_key_payload(const uint8_t *input, size_t input_len, uint8_t expected_type,
|
|
893
|
+
const pq_serialization_algorithm_t **entry_out,
|
|
894
|
+
const uint8_t **key_out, size_t *key_len_out) {
|
|
895
|
+
uint16_t oid_len;
|
|
896
|
+
uint32_t key_len;
|
|
897
|
+
const pq_serialization_algorithm_t *entry;
|
|
898
|
+
size_t offset = 8;
|
|
899
|
+
size_t expected_len;
|
|
900
|
+
int ret;
|
|
901
|
+
|
|
902
|
+
if (!entry_out || !key_out || !key_len_out)
|
|
836
903
|
return PQ_ERROR_BUFFER;
|
|
904
|
+
*entry_out = NULL;
|
|
905
|
+
*key_out = NULL;
|
|
906
|
+
*key_len_out = 0;
|
|
907
|
+
|
|
908
|
+
ret = pq_serialized_key_header(expected_type, input, input_len, &oid_len);
|
|
909
|
+
if (ret != PQ_SUCCESS)
|
|
910
|
+
return ret;
|
|
837
911
|
entry = pq_find_serialization_algorithm_by_oid((const char *)(input + offset), oid_len);
|
|
838
912
|
if (!entry)
|
|
839
913
|
return PQ_ERROR_BUFFER;
|
|
840
914
|
offset += oid_len;
|
|
841
|
-
key_len = (
|
|
842
|
-
((uint32_t)input[offset + 2] << 8) | (uint32_t)input[offset + 3];
|
|
915
|
+
key_len = pq_read_u32be(input + offset);
|
|
843
916
|
offset += 4;
|
|
844
|
-
if (input_len
|
|
917
|
+
if (input_len - offset != (size_t)key_len)
|
|
845
918
|
return PQ_ERROR_BUFFER;
|
|
846
919
|
expected_len = (expected_type == PQC_SERIALIZATION_TYPE_PUBLIC) ? entry->public_key_len
|
|
847
920
|
: entry->secret_key_len;
|
|
848
921
|
if ((size_t)key_len != expected_len)
|
|
849
922
|
return PQ_ERROR_BUFFER;
|
|
850
923
|
|
|
851
|
-
|
|
924
|
+
*entry_out = entry;
|
|
925
|
+
*key_out = input + offset;
|
|
926
|
+
*key_len_out = (size_t)key_len;
|
|
927
|
+
return PQ_SUCCESS;
|
|
928
|
+
}
|
|
929
|
+
|
|
930
|
+
static int pq_decode_serialized_key(const uint8_t *input, size_t input_len, uint8_t expected_type,
|
|
931
|
+
char **algorithm_out, uint8_t **key_out, size_t *key_len_out) {
|
|
932
|
+
const pq_serialization_algorithm_t *entry = NULL;
|
|
933
|
+
const uint8_t *key_data = NULL;
|
|
934
|
+
size_t key_len = 0;
|
|
935
|
+
uint8_t *key_copy = NULL;
|
|
936
|
+
char *algorithm_copy = NULL;
|
|
937
|
+
int ret;
|
|
938
|
+
|
|
939
|
+
if (!input || !algorithm_out || !key_out || !key_len_out)
|
|
940
|
+
return PQ_ERROR_BUFFER;
|
|
941
|
+
|
|
942
|
+
*algorithm_out = NULL;
|
|
943
|
+
*key_out = NULL;
|
|
944
|
+
*key_len_out = 0;
|
|
945
|
+
|
|
946
|
+
ret = pq_serialized_key_payload(input, input_len, expected_type, &entry, &key_data, &key_len);
|
|
947
|
+
if (ret != PQ_SUCCESS)
|
|
948
|
+
return ret;
|
|
949
|
+
|
|
950
|
+
key_copy = malloc(key_len);
|
|
852
951
|
if (!key_copy)
|
|
853
952
|
return PQ_ERROR_NOMEM;
|
|
854
|
-
memcpy(key_copy,
|
|
953
|
+
memcpy(key_copy, key_data, key_len);
|
|
855
954
|
|
|
856
955
|
{
|
|
857
956
|
size_t algorithm_len = strlen(entry->algorithm);
|
|
858
957
|
algorithm_copy = malloc(algorithm_len + 1);
|
|
859
958
|
if (!algorithm_copy) {
|
|
860
|
-
pq_secure_wipe(key_copy,
|
|
959
|
+
pq_secure_wipe(key_copy, key_len);
|
|
861
960
|
free(key_copy);
|
|
862
961
|
return PQ_ERROR_NOMEM;
|
|
863
962
|
}
|
|
@@ -866,7 +965,7 @@ static int pq_decode_serialized_key(const uint8_t *input, size_t input_len, uint
|
|
|
866
965
|
|
|
867
966
|
*algorithm_out = algorithm_copy;
|
|
868
967
|
*key_out = key_copy;
|
|
869
|
-
*key_len_out =
|
|
968
|
+
*key_len_out = key_len;
|
|
870
969
|
return PQ_SUCCESS;
|
|
871
970
|
}
|
|
872
971
|
|
|
@@ -898,6 +997,143 @@ static const char *pq_find_pem_footer(const char *start, size_t len, const char
|
|
|
898
997
|
return NULL;
|
|
899
998
|
}
|
|
900
999
|
|
|
1000
|
+
static int pq_pem_body_bounds(const char *label, const char *input, size_t input_len,
|
|
1001
|
+
const char **body_start_out, size_t *body_len_out) {
|
|
1002
|
+
char header[64], footer[64];
|
|
1003
|
+
int header_len, footer_len;
|
|
1004
|
+
const char *body_start, *footer_pos, *tail;
|
|
1005
|
+
|
|
1006
|
+
if (!label || !input || !body_start_out || !body_len_out)
|
|
1007
|
+
return PQ_ERROR_BUFFER;
|
|
1008
|
+
header_len = snprintf(header, sizeof(header), "-----BEGIN %s-----", label);
|
|
1009
|
+
footer_len = snprintf(footer, sizeof(footer), "-----END %s-----", label);
|
|
1010
|
+
if (header_len <= 0 || footer_len <= 0 ||
|
|
1011
|
+
input_len < (size_t)header_len + (size_t)footer_len + 2 ||
|
|
1012
|
+
strncmp(input, header, (size_t)header_len) != 0)
|
|
1013
|
+
return PQ_ERROR_BUFFER;
|
|
1014
|
+
|
|
1015
|
+
body_start = input + header_len;
|
|
1016
|
+
while ((size_t)(body_start - input) < input_len && pq_is_pem_whitespace(*body_start))
|
|
1017
|
+
body_start++;
|
|
1018
|
+
footer_pos = pq_find_pem_footer(body_start, input_len - (size_t)(body_start - input), footer,
|
|
1019
|
+
(size_t)footer_len);
|
|
1020
|
+
if (!footer_pos)
|
|
1021
|
+
return PQ_ERROR_BUFFER;
|
|
1022
|
+
|
|
1023
|
+
tail = footer_pos + footer_len;
|
|
1024
|
+
while ((size_t)(tail - input) < input_len) {
|
|
1025
|
+
if (!pq_is_pem_whitespace(*tail))
|
|
1026
|
+
return PQ_ERROR_BUFFER;
|
|
1027
|
+
tail++;
|
|
1028
|
+
}
|
|
1029
|
+
|
|
1030
|
+
*body_len_out = (size_t)(footer_pos - body_start);
|
|
1031
|
+
if (*body_len_out > (size_t)INT_MAX)
|
|
1032
|
+
return PQ_ERROR_BUFFER;
|
|
1033
|
+
*body_start_out = body_start;
|
|
1034
|
+
return PQ_SUCCESS;
|
|
1035
|
+
}
|
|
1036
|
+
|
|
1037
|
+
static int pq_pem_compact_body(const char *body_start, size_t body_len, unsigned char **compact_out,
|
|
1038
|
+
size_t *compact_len_out) {
|
|
1039
|
+
unsigned char *compact;
|
|
1040
|
+
size_t compact_len = 0;
|
|
1041
|
+
int padding = 0;
|
|
1042
|
+
int saw_padding = 0;
|
|
1043
|
+
|
|
1044
|
+
compact = malloc(body_len ? body_len : 1);
|
|
1045
|
+
if (!compact)
|
|
1046
|
+
return PQ_ERROR_NOMEM;
|
|
1047
|
+
|
|
1048
|
+
for (size_t i = 0; i < body_len; ++i) {
|
|
1049
|
+
unsigned char c = (unsigned char)body_start[i];
|
|
1050
|
+
int value;
|
|
1051
|
+
|
|
1052
|
+
if (pq_is_pem_whitespace((char)c))
|
|
1053
|
+
continue;
|
|
1054
|
+
value = pq_base64_char_value(c);
|
|
1055
|
+
if (value < 0)
|
|
1056
|
+
goto invalid;
|
|
1057
|
+
if (c == '=') {
|
|
1058
|
+
saw_padding = 1;
|
|
1059
|
+
if (++padding > 2)
|
|
1060
|
+
goto invalid;
|
|
1061
|
+
} else if (saw_padding)
|
|
1062
|
+
goto invalid;
|
|
1063
|
+
compact[compact_len++] = c;
|
|
1064
|
+
}
|
|
1065
|
+
|
|
1066
|
+
if (compact_len == 0 || (compact_len % 4) != 0 || compact_len > (size_t)INT_MAX) {
|
|
1067
|
+
goto invalid;
|
|
1068
|
+
}
|
|
1069
|
+
*compact_out = compact;
|
|
1070
|
+
*compact_len_out = compact_len;
|
|
1071
|
+
return PQ_SUCCESS;
|
|
1072
|
+
|
|
1073
|
+
invalid:
|
|
1074
|
+
pq_secure_wipe(compact, body_len);
|
|
1075
|
+
free(compact);
|
|
1076
|
+
return PQ_ERROR_BUFFER;
|
|
1077
|
+
}
|
|
1078
|
+
|
|
1079
|
+
static int pq_base64_encode_der(const uint8_t *der, size_t der_len, unsigned char **encoded_out,
|
|
1080
|
+
size_t *encoded_len_out) {
|
|
1081
|
+
size_t encoded_len;
|
|
1082
|
+
unsigned char *encoded;
|
|
1083
|
+
|
|
1084
|
+
if (der_len > (size_t)INT_MAX)
|
|
1085
|
+
return PQ_ERROR_BUFFER;
|
|
1086
|
+
encoded_len = 4 * ((der_len + 2) / 3);
|
|
1087
|
+
if (encoded_len > (size_t)INT_MAX)
|
|
1088
|
+
return PQ_ERROR_BUFFER;
|
|
1089
|
+
encoded = malloc(encoded_len + 1);
|
|
1090
|
+
if (!encoded)
|
|
1091
|
+
return PQ_ERROR_NOMEM;
|
|
1092
|
+
if (EVP_EncodeBlock(encoded, der, (int)der_len) != (int)encoded_len) {
|
|
1093
|
+
pq_secure_wipe(encoded, encoded_len + 1);
|
|
1094
|
+
free(encoded);
|
|
1095
|
+
return PQ_ERROR_OPENSSL;
|
|
1096
|
+
}
|
|
1097
|
+
*encoded_out = encoded;
|
|
1098
|
+
*encoded_len_out = encoded_len;
|
|
1099
|
+
return PQ_SUCCESS;
|
|
1100
|
+
}
|
|
1101
|
+
|
|
1102
|
+
static int pq_pem_encoded_size(size_t header_len, size_t encoded_len, size_t line_count,
|
|
1103
|
+
size_t footer_len, size_t *needed_out) {
|
|
1104
|
+
const size_t parts[] = {header_len, 1, encoded_len, line_count, footer_len, 1};
|
|
1105
|
+
size_t needed = 0;
|
|
1106
|
+
|
|
1107
|
+
for (size_t i = 0; i < sizeof(parts) / sizeof(parts[0]); ++i) {
|
|
1108
|
+
if (pq_size_add(needed, parts[i], &needed) != PQ_SUCCESS)
|
|
1109
|
+
return PQ_ERROR_BUFFER;
|
|
1110
|
+
}
|
|
1111
|
+
*needed_out = needed;
|
|
1112
|
+
return PQ_SUCCESS;
|
|
1113
|
+
}
|
|
1114
|
+
|
|
1115
|
+
static size_t pq_write_pem_body(char *output, const char *header, size_t header_len,
|
|
1116
|
+
const unsigned char *encoded, size_t encoded_len,
|
|
1117
|
+
const char *footer, size_t footer_len) {
|
|
1118
|
+
char *cursor = output;
|
|
1119
|
+
|
|
1120
|
+
memcpy(cursor, header, header_len);
|
|
1121
|
+
cursor += header_len;
|
|
1122
|
+
*cursor++ = '\n';
|
|
1123
|
+
for (size_t offset = 0; offset < encoded_len; offset += 64) {
|
|
1124
|
+
size_t line_len = encoded_len - offset;
|
|
1125
|
+
if (line_len > 64)
|
|
1126
|
+
line_len = 64;
|
|
1127
|
+
memcpy(cursor, encoded + offset, line_len);
|
|
1128
|
+
cursor += line_len;
|
|
1129
|
+
*cursor++ = '\n';
|
|
1130
|
+
}
|
|
1131
|
+
memcpy(cursor, footer, footer_len);
|
|
1132
|
+
cursor += footer_len;
|
|
1133
|
+
*cursor = '\0';
|
|
1134
|
+
return (size_t)(cursor - output);
|
|
1135
|
+
}
|
|
1136
|
+
|
|
901
1137
|
static int pq_der_to_pem(const char *label, const uint8_t *der, size_t der_len, char **output,
|
|
902
1138
|
size_t *output_len) {
|
|
903
1139
|
char header[64];
|
|
@@ -908,7 +1144,7 @@ static int pq_der_to_pem(const char *label, const uint8_t *der, size_t der_len,
|
|
|
908
1144
|
size_t encoded_len;
|
|
909
1145
|
size_t line_count;
|
|
910
1146
|
size_t needed;
|
|
911
|
-
|
|
1147
|
+
int ret;
|
|
912
1148
|
|
|
913
1149
|
if (!label || !der || !output || !output_len)
|
|
914
1150
|
return PQ_ERROR_BUFFER;
|
|
@@ -919,32 +1155,18 @@ static int pq_der_to_pem(const char *label, const uint8_t *der, size_t der_len,
|
|
|
919
1155
|
footer_len = snprintf(footer, sizeof(footer), "-----END %s-----", label);
|
|
920
1156
|
if (header_len <= 0 || footer_len <= 0)
|
|
921
1157
|
return PQ_ERROR_BUFFER;
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
encoded_len = 4 * ((der_len + 2) / 3);
|
|
926
|
-
if (encoded_len > (size_t)INT_MAX)
|
|
927
|
-
return PQ_ERROR_BUFFER;
|
|
928
|
-
|
|
929
|
-
encoded = malloc(encoded_len + 1);
|
|
930
|
-
if (!encoded)
|
|
931
|
-
return PQ_ERROR_NOMEM;
|
|
932
|
-
|
|
933
|
-
if (EVP_EncodeBlock(encoded, der, (int)der_len) != (int)encoded_len) {
|
|
934
|
-
pq_secure_wipe(encoded, encoded_len + 1);
|
|
935
|
-
free(encoded);
|
|
936
|
-
return PQ_ERROR_OPENSSL;
|
|
937
|
-
}
|
|
1158
|
+
ret = pq_base64_encode_der(der, der_len, &encoded, &encoded_len);
|
|
1159
|
+
if (ret != PQ_SUCCESS)
|
|
1160
|
+
return ret;
|
|
938
1161
|
|
|
939
1162
|
line_count = encoded_len == 0 ? 0 : ((encoded_len + 63) / 64);
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
1163
|
+
ret = pq_pem_encoded_size((size_t)header_len, encoded_len, line_count, (size_t)footer_len,
|
|
1164
|
+
&needed);
|
|
1165
|
+
if (ret != PQ_SUCCESS) {
|
|
943
1166
|
pq_secure_wipe(encoded, encoded_len + 1);
|
|
944
1167
|
free(encoded);
|
|
945
|
-
return
|
|
1168
|
+
return ret;
|
|
946
1169
|
}
|
|
947
|
-
needed = (size_t)header_len + 1 + encoded_len + line_count + (size_t)footer_len + 1;
|
|
948
1170
|
|
|
949
1171
|
pem = malloc(needed);
|
|
950
1172
|
if (!pem) {
|
|
@@ -953,26 +1175,9 @@ static int pq_der_to_pem(const char *label, const uint8_t *der, size_t der_len,
|
|
|
953
1175
|
return PQ_ERROR_NOMEM;
|
|
954
1176
|
}
|
|
955
1177
|
|
|
956
|
-
cur = pem;
|
|
957
|
-
memcpy(cur, header, (size_t)header_len);
|
|
958
|
-
cur += header_len;
|
|
959
|
-
*cur++ = '\n';
|
|
960
|
-
|
|
961
|
-
for (size_t offset = 0; offset < encoded_len; offset += 64) {
|
|
962
|
-
size_t line_len = encoded_len - offset;
|
|
963
|
-
if (line_len > 64)
|
|
964
|
-
line_len = 64;
|
|
965
|
-
memcpy(cur, encoded + offset, line_len);
|
|
966
|
-
cur += line_len;
|
|
967
|
-
*cur++ = '\n';
|
|
968
|
-
}
|
|
969
|
-
|
|
970
|
-
memcpy(cur, footer, (size_t)footer_len);
|
|
971
|
-
cur += footer_len;
|
|
972
|
-
*cur = '\0';
|
|
973
|
-
|
|
974
1178
|
*output = pem;
|
|
975
|
-
*output_len = (size_t)
|
|
1179
|
+
*output_len = pq_write_pem_body(pem, header, (size_t)header_len, encoded, encoded_len, footer,
|
|
1180
|
+
(size_t)footer_len);
|
|
976
1181
|
pq_secure_wipe(encoded, encoded_len + 1);
|
|
977
1182
|
free(encoded);
|
|
978
1183
|
return PQ_SUCCESS;
|
|
@@ -980,10 +1185,7 @@ static int pq_der_to_pem(const char *label, const uint8_t *der, size_t der_len,
|
|
|
980
1185
|
|
|
981
1186
|
static int pq_pem_to_der(const char *label, const char *input, size_t input_len, uint8_t **der_out,
|
|
982
1187
|
size_t *der_len_out) {
|
|
983
|
-
char
|
|
984
|
-
int header_len, footer_len;
|
|
985
|
-
const char *body_start, *footer_pos;
|
|
986
|
-
const char *tail;
|
|
1188
|
+
const char *body_start;
|
|
987
1189
|
uint8_t *der = NULL;
|
|
988
1190
|
unsigned char *compact = NULL;
|
|
989
1191
|
size_t body_len = 0;
|
|
@@ -991,79 +1193,21 @@ static int pq_pem_to_der(const char *label, const char *input, size_t input_len,
|
|
|
991
1193
|
size_t der_cap = 0;
|
|
992
1194
|
int decoded_len = 0;
|
|
993
1195
|
int padding = 0;
|
|
994
|
-
int
|
|
1196
|
+
int ret;
|
|
995
1197
|
|
|
996
1198
|
if (!label || !input || !der_out || !der_len_out)
|
|
997
1199
|
return PQ_ERROR_BUFFER;
|
|
998
1200
|
*der_out = NULL;
|
|
999
1201
|
*der_len_out = 0;
|
|
1000
1202
|
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
if (
|
|
1006
|
-
return
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
body_start = input + header_len;
|
|
1011
|
-
while ((size_t)(body_start - input) < input_len && pq_is_pem_whitespace(*body_start))
|
|
1012
|
-
body_start++;
|
|
1013
|
-
|
|
1014
|
-
footer_pos = pq_find_pem_footer(body_start, input_len - (size_t)(body_start - input), footer,
|
|
1015
|
-
(size_t)footer_len);
|
|
1016
|
-
if (!footer_pos)
|
|
1017
|
-
return PQ_ERROR_BUFFER;
|
|
1018
|
-
|
|
1019
|
-
tail = footer_pos + footer_len;
|
|
1020
|
-
while ((size_t)(tail - input) < input_len) {
|
|
1021
|
-
if (!pq_is_pem_whitespace(*tail))
|
|
1022
|
-
return PQ_ERROR_BUFFER;
|
|
1023
|
-
tail++;
|
|
1024
|
-
}
|
|
1025
|
-
|
|
1026
|
-
body_len = (size_t)(footer_pos - body_start);
|
|
1027
|
-
if (body_len > (size_t)INT_MAX)
|
|
1028
|
-
return PQ_ERROR_BUFFER;
|
|
1029
|
-
|
|
1030
|
-
compact = malloc(body_len ? body_len : 1);
|
|
1031
|
-
if (!compact)
|
|
1032
|
-
return PQ_ERROR_NOMEM;
|
|
1033
|
-
|
|
1034
|
-
for (size_t i = 0; i < body_len; ++i) {
|
|
1035
|
-
unsigned char c = (unsigned char)body_start[i];
|
|
1036
|
-
int value;
|
|
1037
|
-
if (pq_is_pem_whitespace((char)c))
|
|
1038
|
-
continue;
|
|
1039
|
-
|
|
1040
|
-
value = pq_base64_char_value(c);
|
|
1041
|
-
if (value < 0) {
|
|
1042
|
-
pq_secure_wipe(compact, body_len);
|
|
1043
|
-
free(compact);
|
|
1044
|
-
return PQ_ERROR_BUFFER;
|
|
1045
|
-
}
|
|
1046
|
-
if (c == '=') {
|
|
1047
|
-
saw_padding = 1;
|
|
1048
|
-
padding++;
|
|
1049
|
-
if (padding > 2) {
|
|
1050
|
-
pq_secure_wipe(compact, body_len);
|
|
1051
|
-
free(compact);
|
|
1052
|
-
return PQ_ERROR_BUFFER;
|
|
1053
|
-
}
|
|
1054
|
-
} else if (saw_padding) {
|
|
1055
|
-
pq_secure_wipe(compact, body_len);
|
|
1056
|
-
free(compact);
|
|
1057
|
-
return PQ_ERROR_BUFFER;
|
|
1058
|
-
}
|
|
1059
|
-
compact[compact_len++] = c;
|
|
1060
|
-
}
|
|
1061
|
-
|
|
1062
|
-
if (compact_len == 0 || (compact_len % 4) != 0 || compact_len > (size_t)INT_MAX) {
|
|
1063
|
-
pq_secure_wipe(compact, body_len);
|
|
1064
|
-
free(compact);
|
|
1065
|
-
return PQ_ERROR_BUFFER;
|
|
1066
|
-
}
|
|
1203
|
+
ret = pq_pem_body_bounds(label, input, input_len, &body_start, &body_len);
|
|
1204
|
+
if (ret != PQ_SUCCESS)
|
|
1205
|
+
return ret;
|
|
1206
|
+
ret = pq_pem_compact_body(body_start, body_len, &compact, &compact_len);
|
|
1207
|
+
if (ret != PQ_SUCCESS)
|
|
1208
|
+
return ret;
|
|
1209
|
+
for (size_t i = compact_len; i > 0 && compact[i - 1] == '='; --i)
|
|
1210
|
+
padding++;
|
|
1067
1211
|
|
|
1068
1212
|
der_cap = (compact_len / 4) * 3;
|
|
1069
1213
|
der = malloc(der_cap ? der_cap : 1);
|
|
@@ -1356,11 +1500,224 @@ cleanup:
|
|
|
1356
1500
|
return ret;
|
|
1357
1501
|
}
|
|
1358
1502
|
|
|
1503
|
+
static int pq_der_tlv_size(size_t value_len, size_t *tlv_len_out) {
|
|
1504
|
+
size_t header_len;
|
|
1505
|
+
|
|
1506
|
+
if (pq_size_add(1, pq_der_length_octets(value_len), &header_len) != PQ_SUCCESS ||
|
|
1507
|
+
pq_size_add(header_len, value_len, tlv_len_out) != PQ_SUCCESS)
|
|
1508
|
+
return PQ_ERROR_BUFFER;
|
|
1509
|
+
return PQ_SUCCESS;
|
|
1510
|
+
}
|
|
1511
|
+
|
|
1512
|
+
static int pq_der_write_tlv_header(uint8_t tag, uint8_t **cursor, size_t value_len) {
|
|
1513
|
+
if (!cursor || !*cursor)
|
|
1514
|
+
return PQ_ERROR_BUFFER;
|
|
1515
|
+
*(*cursor)++ = tag;
|
|
1516
|
+
return pq_der_write_length(cursor, value_len);
|
|
1517
|
+
}
|
|
1518
|
+
|
|
1519
|
+
static int pq_der_write_tlv(uint8_t **cursor, uint8_t tag, const uint8_t *value, size_t value_len) {
|
|
1520
|
+
int ret = pq_der_write_tlv_header(tag, cursor, value_len);
|
|
1521
|
+
|
|
1522
|
+
if (ret != PQ_SUCCESS)
|
|
1523
|
+
return ret;
|
|
1524
|
+
if (value_len > 0) {
|
|
1525
|
+
if (!value)
|
|
1526
|
+
return PQ_ERROR_BUFFER;
|
|
1527
|
+
memcpy(*cursor, value, value_len);
|
|
1528
|
+
*cursor += value_len;
|
|
1529
|
+
}
|
|
1530
|
+
return PQ_SUCCESS;
|
|
1531
|
+
}
|
|
1532
|
+
|
|
1533
|
+
typedef struct {
|
|
1534
|
+
size_t inner_len;
|
|
1535
|
+
size_t total_len;
|
|
1536
|
+
} pq_pkcs8_size_t;
|
|
1537
|
+
|
|
1538
|
+
static int pq_pkcs8_private_key_info_size(size_t oid_len, size_t private_key_len,
|
|
1539
|
+
pq_pkcs8_size_t *size_out) {
|
|
1540
|
+
size_t algorithm_len, private_len, inner_len, total_len;
|
|
1541
|
+
|
|
1542
|
+
if (pq_der_tlv_size(oid_len, &algorithm_len) != PQ_SUCCESS ||
|
|
1543
|
+
pq_der_tlv_size(private_key_len, &private_len) != PQ_SUCCESS ||
|
|
1544
|
+
pq_size_add(3, algorithm_len, &inner_len) != PQ_SUCCESS ||
|
|
1545
|
+
pq_size_add(inner_len, private_len, &inner_len) != PQ_SUCCESS ||
|
|
1546
|
+
pq_der_tlv_size(inner_len, &total_len) != PQ_SUCCESS)
|
|
1547
|
+
return PQ_ERROR_BUFFER;
|
|
1548
|
+
size_out->inner_len = inner_len;
|
|
1549
|
+
size_out->total_len = total_len;
|
|
1550
|
+
return PQ_SUCCESS;
|
|
1551
|
+
}
|
|
1552
|
+
|
|
1553
|
+
typedef struct {
|
|
1554
|
+
size_t oid_offset;
|
|
1555
|
+
size_t oid_tlv_len;
|
|
1556
|
+
size_t private_key_offset;
|
|
1557
|
+
size_t private_key_len;
|
|
1558
|
+
size_t outer_end;
|
|
1559
|
+
size_t content_offset;
|
|
1560
|
+
} pq_pkcs8_parts_t;
|
|
1561
|
+
|
|
1562
|
+
static int pq_pkcs8_parse_outer(const uint8_t *input, size_t input_len, pq_pkcs8_parts_t *parts) {
|
|
1563
|
+
size_t offset = 0, outer_offset = 0, outer_len = 0;
|
|
1564
|
+
size_t version_offset = 0, version_len = 0;
|
|
1565
|
+
int ret;
|
|
1566
|
+
|
|
1567
|
+
ret = pq_der_expect_tlv(input, input_len, &offset, 0x30, &outer_offset, &outer_len);
|
|
1568
|
+
if (ret != PQ_SUCCESS)
|
|
1569
|
+
return ret;
|
|
1570
|
+
if (offset != input_len ||
|
|
1571
|
+
pq_size_add(outer_offset, outer_len, &parts->outer_end) != PQ_SUCCESS ||
|
|
1572
|
+
parts->outer_end != input_len)
|
|
1573
|
+
return PQ_ERROR_BUFFER;
|
|
1574
|
+
offset = outer_offset;
|
|
1575
|
+
ret = pq_der_expect_tlv(input, parts->outer_end, &offset, 0x02, &version_offset, &version_len);
|
|
1576
|
+
if (ret != PQ_SUCCESS)
|
|
1577
|
+
return ret;
|
|
1578
|
+
if (version_len != 1 || input[version_offset] != 0)
|
|
1579
|
+
return PQ_ERROR_BUFFER;
|
|
1580
|
+
parts->content_offset = offset;
|
|
1581
|
+
return PQ_SUCCESS;
|
|
1582
|
+
}
|
|
1583
|
+
|
|
1584
|
+
static int pq_pkcs8_parse_algorithm(const uint8_t *input, size_t outer_end, size_t *offset,
|
|
1585
|
+
pq_pkcs8_parts_t *parts) {
|
|
1586
|
+
size_t alg_offset = 0, alg_len = 0, alg_end;
|
|
1587
|
+
size_t alg_cursor, oid_offset = 0, oid_len = 0;
|
|
1588
|
+
int ret;
|
|
1589
|
+
|
|
1590
|
+
ret = pq_der_expect_tlv(input, outer_end, offset, 0x30, &alg_offset, &alg_len);
|
|
1591
|
+
if (ret != PQ_SUCCESS || pq_size_add(alg_offset, alg_len, &alg_end) != PQ_SUCCESS)
|
|
1592
|
+
return PQ_ERROR_BUFFER;
|
|
1593
|
+
alg_cursor = alg_offset;
|
|
1594
|
+
parts->oid_offset = alg_cursor;
|
|
1595
|
+
ret = pq_der_expect_tlv(input, alg_end, &alg_cursor, 0x06, &oid_offset, &oid_len);
|
|
1596
|
+
if (ret != PQ_SUCCESS || alg_cursor != alg_end)
|
|
1597
|
+
return PQ_ERROR_BUFFER;
|
|
1598
|
+
parts->oid_tlv_len = alg_cursor - parts->oid_offset;
|
|
1599
|
+
return PQ_SUCCESS;
|
|
1600
|
+
}
|
|
1601
|
+
|
|
1602
|
+
static int pq_pkcs8_parse_parts(const uint8_t *input, size_t input_len, pq_pkcs8_parts_t *parts) {
|
|
1603
|
+
size_t offset;
|
|
1604
|
+
int ret = pq_pkcs8_parse_outer(input, input_len, parts);
|
|
1605
|
+
|
|
1606
|
+
if (ret != PQ_SUCCESS)
|
|
1607
|
+
return ret;
|
|
1608
|
+
offset = parts->content_offset;
|
|
1609
|
+
ret = pq_pkcs8_parse_algorithm(input, parts->outer_end, &offset, parts);
|
|
1610
|
+
if (ret != PQ_SUCCESS)
|
|
1611
|
+
return ret;
|
|
1612
|
+
ret = pq_der_expect_tlv(input, parts->outer_end, &offset, 0x04, &parts->private_key_offset,
|
|
1613
|
+
&parts->private_key_len);
|
|
1614
|
+
if (ret != PQ_SUCCESS || offset != parts->outer_end)
|
|
1615
|
+
return PQ_ERROR_BUFFER;
|
|
1616
|
+
return PQ_SUCCESS;
|
|
1617
|
+
}
|
|
1618
|
+
|
|
1619
|
+
static int pq_x509_sig_to_der(X509_SIG *encrypted, unsigned char **der_out, size_t *der_len_out) {
|
|
1620
|
+
unsigned char *der;
|
|
1621
|
+
unsigned char *cursor;
|
|
1622
|
+
int der_len = i2d_X509_SIG(encrypted, NULL);
|
|
1623
|
+
|
|
1624
|
+
if (der_len <= 0)
|
|
1625
|
+
return PQ_ERROR_OPENSSL;
|
|
1626
|
+
der = malloc((size_t)der_len);
|
|
1627
|
+
if (!der)
|
|
1628
|
+
return PQ_ERROR_NOMEM;
|
|
1629
|
+
cursor = der;
|
|
1630
|
+
if (i2d_X509_SIG(encrypted, &cursor) != der_len || (size_t)(cursor - der) != (size_t)der_len) {
|
|
1631
|
+
pq_secure_wipe(der, (size_t)der_len);
|
|
1632
|
+
free(der);
|
|
1633
|
+
return PQ_ERROR_OPENSSL;
|
|
1634
|
+
}
|
|
1635
|
+
*der_out = der;
|
|
1636
|
+
*der_len_out = (size_t)der_len;
|
|
1637
|
+
return PQ_SUCCESS;
|
|
1638
|
+
}
|
|
1639
|
+
|
|
1640
|
+
static int pq_pkcs8_info_to_der(PKCS8_PRIV_KEY_INFO *p8, unsigned char **der_out,
|
|
1641
|
+
size_t *der_len_out) {
|
|
1642
|
+
unsigned char *der;
|
|
1643
|
+
unsigned char *cursor;
|
|
1644
|
+
int der_len = i2d_PKCS8_PRIV_KEY_INFO(p8, NULL);
|
|
1645
|
+
|
|
1646
|
+
if (der_len <= 0)
|
|
1647
|
+
return PQ_ERROR_OPENSSL;
|
|
1648
|
+
der = malloc((size_t)der_len);
|
|
1649
|
+
if (!der)
|
|
1650
|
+
return PQ_ERROR_NOMEM;
|
|
1651
|
+
cursor = der;
|
|
1652
|
+
if (i2d_PKCS8_PRIV_KEY_INFO(p8, &cursor) != der_len ||
|
|
1653
|
+
(size_t)(cursor - der) != (size_t)der_len) {
|
|
1654
|
+
pq_secure_wipe(der, (size_t)der_len);
|
|
1655
|
+
free(der);
|
|
1656
|
+
return PQ_ERROR_OPENSSL;
|
|
1657
|
+
}
|
|
1658
|
+
*der_out = der;
|
|
1659
|
+
*der_len_out = (size_t)der_len;
|
|
1660
|
+
return PQ_SUCCESS;
|
|
1661
|
+
}
|
|
1662
|
+
|
|
1663
|
+
static int pq_pkcs8_encrypt(const uint8_t *plain_der, size_t plain_der_len, const char *passphrase,
|
|
1664
|
+
size_t passphrase_len, int iterations, X509_SIG **encrypted_out) {
|
|
1665
|
+
const unsigned char *cursor = plain_der;
|
|
1666
|
+
PKCS8_PRIV_KEY_INFO *p8 = NULL;
|
|
1667
|
+
X509_SIG *encrypted = NULL;
|
|
1668
|
+
unsigned char salt[16];
|
|
1669
|
+
int ret = PQ_ERROR_OPENSSL;
|
|
1670
|
+
|
|
1671
|
+
p8 = d2i_PKCS8_PRIV_KEY_INFO(NULL, &cursor, (long)plain_der_len);
|
|
1672
|
+
if (!p8 || cursor != plain_der + plain_der_len || RAND_bytes(salt, sizeof(salt)) != 1)
|
|
1673
|
+
goto cleanup;
|
|
1674
|
+
encrypted = PKCS8_encrypt(-1, EVP_aes_256_cbc(), passphrase, (int)passphrase_len, salt,
|
|
1675
|
+
(int)sizeof(salt), iterations, p8);
|
|
1676
|
+
if (!encrypted)
|
|
1677
|
+
goto cleanup;
|
|
1678
|
+
*encrypted_out = encrypted;
|
|
1679
|
+
encrypted = NULL;
|
|
1680
|
+
ret = PQ_SUCCESS;
|
|
1681
|
+
|
|
1682
|
+
cleanup:
|
|
1683
|
+
pq_secure_wipe(salt, sizeof(salt));
|
|
1684
|
+
if (encrypted)
|
|
1685
|
+
X509_SIG_free(encrypted);
|
|
1686
|
+
if (p8)
|
|
1687
|
+
PKCS8_PRIV_KEY_INFO_free(p8);
|
|
1688
|
+
return ret;
|
|
1689
|
+
}
|
|
1690
|
+
|
|
1691
|
+
static int pq_pkcs8_decrypt(const uint8_t *encrypted_der, size_t encrypted_der_len,
|
|
1692
|
+
const char *passphrase, size_t passphrase_len,
|
|
1693
|
+
PKCS8_PRIV_KEY_INFO **p8_out) {
|
|
1694
|
+
const unsigned char *cursor = encrypted_der;
|
|
1695
|
+
X509_SIG *encrypted = d2i_X509_SIG(NULL, &cursor, (long)encrypted_der_len);
|
|
1696
|
+
PKCS8_PRIV_KEY_INFO *p8 = NULL;
|
|
1697
|
+
int ret = PQ_ERROR_OPENSSL;
|
|
1698
|
+
|
|
1699
|
+
if (!encrypted || cursor != encrypted_der + encrypted_der_len)
|
|
1700
|
+
goto cleanup;
|
|
1701
|
+
p8 = PKCS8_decrypt(encrypted, passphrase, (int)passphrase_len);
|
|
1702
|
+
if (!p8)
|
|
1703
|
+
goto cleanup;
|
|
1704
|
+
*p8_out = p8;
|
|
1705
|
+
p8 = NULL;
|
|
1706
|
+
ret = PQ_SUCCESS;
|
|
1707
|
+
|
|
1708
|
+
cleanup:
|
|
1709
|
+
if (p8)
|
|
1710
|
+
PKCS8_PRIV_KEY_INFO_free(p8);
|
|
1711
|
+
if (encrypted)
|
|
1712
|
+
X509_SIG_free(encrypted);
|
|
1713
|
+
return ret;
|
|
1714
|
+
}
|
|
1715
|
+
|
|
1359
1716
|
int pq_pkcs8_private_key_info_to_der(uint8_t **output, size_t *output_len, const char *oid_text,
|
|
1360
1717
|
const uint8_t *private_key, size_t private_key_len) {
|
|
1361
1718
|
uint8_t *oid_der = NULL;
|
|
1362
1719
|
size_t oid_der_len = 0;
|
|
1363
|
-
|
|
1720
|
+
pq_pkcs8_size_t der_size = {0};
|
|
1364
1721
|
uint8_t *buf = NULL;
|
|
1365
1722
|
uint8_t *cur;
|
|
1366
1723
|
int ret;
|
|
@@ -1374,58 +1731,42 @@ int pq_pkcs8_private_key_info_to_der(uint8_t **output, size_t *output_len, const
|
|
|
1374
1731
|
if (ret != PQ_SUCCESS)
|
|
1375
1732
|
return ret;
|
|
1376
1733
|
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
priv_len = 1 + pq_der_length_octets(private_key_len) + private_key_len;
|
|
1380
|
-
if (pq_size_add(3, alg_len, &inner_len) != PQ_SUCCESS ||
|
|
1381
|
-
pq_size_add(inner_len, priv_len, &inner_len) != PQ_SUCCESS) {
|
|
1382
|
-
ret = PQ_ERROR_BUFFER;
|
|
1383
|
-
goto cleanup;
|
|
1384
|
-
}
|
|
1385
|
-
if (pq_size_add(1 + pq_der_length_octets(inner_len), inner_len, &total_len) != PQ_SUCCESS) {
|
|
1386
|
-
ret = PQ_ERROR_BUFFER;
|
|
1734
|
+
ret = pq_pkcs8_private_key_info_size(oid_der_len, private_key_len, &der_size);
|
|
1735
|
+
if (ret != PQ_SUCCESS)
|
|
1387
1736
|
goto cleanup;
|
|
1388
|
-
}
|
|
1389
1737
|
|
|
1390
|
-
buf = malloc(total_len);
|
|
1738
|
+
buf = malloc(der_size.total_len);
|
|
1391
1739
|
if (!buf) {
|
|
1392
1740
|
ret = PQ_ERROR_NOMEM;
|
|
1393
1741
|
goto cleanup;
|
|
1394
1742
|
}
|
|
1395
1743
|
|
|
1396
1744
|
cur = buf;
|
|
1397
|
-
|
|
1398
|
-
ret = pq_der_write_length(&cur, inner_len);
|
|
1745
|
+
ret = pq_der_write_tlv_header(0x30, &cur, der_size.inner_len);
|
|
1399
1746
|
if (ret != PQ_SUCCESS)
|
|
1400
1747
|
goto cleanup;
|
|
1401
1748
|
*cur++ = 0x02;
|
|
1402
1749
|
*cur++ = 0x01;
|
|
1403
1750
|
*cur++ = 0x00;
|
|
1404
|
-
|
|
1405
|
-
ret = pq_der_write_length(&cur, alg_body_len);
|
|
1751
|
+
ret = pq_der_write_tlv(&cur, 0x30, oid_der, oid_der_len);
|
|
1406
1752
|
if (ret != PQ_SUCCESS)
|
|
1407
1753
|
goto cleanup;
|
|
1408
|
-
|
|
1409
|
-
cur += oid_der_len;
|
|
1410
|
-
*cur++ = 0x04;
|
|
1411
|
-
ret = pq_der_write_length(&cur, private_key_len);
|
|
1754
|
+
ret = pq_der_write_tlv(&cur, 0x04, private_key, private_key_len);
|
|
1412
1755
|
if (ret != PQ_SUCCESS)
|
|
1413
1756
|
goto cleanup;
|
|
1414
|
-
|
|
1415
|
-
cur += private_key_len;
|
|
1416
|
-
if ((size_t)(cur - buf) != total_len) {
|
|
1757
|
+
if ((size_t)(cur - buf) != der_size.total_len) {
|
|
1417
1758
|
ret = PQ_ERROR_BUFFER;
|
|
1418
1759
|
goto cleanup;
|
|
1419
1760
|
}
|
|
1420
1761
|
|
|
1421
1762
|
*output = buf;
|
|
1422
|
-
*output_len = total_len;
|
|
1763
|
+
*output_len = der_size.total_len;
|
|
1423
1764
|
buf = NULL;
|
|
1424
1765
|
ret = PQ_SUCCESS;
|
|
1425
1766
|
|
|
1426
1767
|
cleanup:
|
|
1427
1768
|
if (buf) {
|
|
1428
|
-
pq_secure_wipe(buf, total_len);
|
|
1769
|
+
pq_secure_wipe(buf, der_size.total_len);
|
|
1429
1770
|
free(buf);
|
|
1430
1771
|
}
|
|
1431
1772
|
if (oid_der) {
|
|
@@ -1438,11 +1779,7 @@ cleanup:
|
|
|
1438
1779
|
int pq_pkcs8_private_key_info_from_der(char **oid_text_out, uint8_t **private_key_out,
|
|
1439
1780
|
size_t *private_key_len_out, const uint8_t *input,
|
|
1440
1781
|
size_t input_len) {
|
|
1441
|
-
|
|
1442
|
-
size_t outer_off = 0, outer_len = 0, outer_end;
|
|
1443
|
-
size_t alg_off = 0, alg_len = 0, alg_end;
|
|
1444
|
-
size_t oid_off = 0, oid_len = 0;
|
|
1445
|
-
size_t priv_off = 0, priv_len = 0;
|
|
1782
|
+
pq_pkcs8_parts_t parts = {0};
|
|
1446
1783
|
uint8_t *private_key = NULL;
|
|
1447
1784
|
char *oid_text = NULL;
|
|
1448
1785
|
int ret;
|
|
@@ -1453,58 +1790,23 @@ int pq_pkcs8_private_key_info_from_der(char **oid_text_out, uint8_t **private_ke
|
|
|
1453
1790
|
*private_key_out = NULL;
|
|
1454
1791
|
*private_key_len_out = 0;
|
|
1455
1792
|
|
|
1456
|
-
ret =
|
|
1457
|
-
if (ret != PQ_SUCCESS)
|
|
1458
|
-
return ret;
|
|
1459
|
-
outer_end = outer_off + outer_len;
|
|
1460
|
-
if (offset != input_len || outer_end != input_len)
|
|
1461
|
-
return PQ_ERROR_BUFFER;
|
|
1462
|
-
|
|
1463
|
-
offset = outer_off;
|
|
1464
|
-
{
|
|
1465
|
-
size_t version_off = 0, version_len = 0;
|
|
1466
|
-
ret = pq_der_expect_tlv(input, outer_end, &offset, 0x02, &version_off, &version_len);
|
|
1467
|
-
if (ret != PQ_SUCCESS)
|
|
1468
|
-
return ret;
|
|
1469
|
-
if (version_len != 1 || input[version_off] != 0x00)
|
|
1470
|
-
return PQ_ERROR_BUFFER;
|
|
1471
|
-
}
|
|
1472
|
-
|
|
1473
|
-
ret = pq_der_expect_tlv(input, outer_end, &offset, 0x30, &alg_off, &alg_len);
|
|
1793
|
+
ret = pq_pkcs8_parse_parts(input, input_len, &parts);
|
|
1474
1794
|
if (ret != PQ_SUCCESS)
|
|
1475
1795
|
return ret;
|
|
1476
|
-
|
|
1477
|
-
{
|
|
1478
|
-
size_t alg_cursor = alg_off;
|
|
1479
|
-
size_t oid_tlv_start = alg_cursor;
|
|
1480
|
-
ret = pq_der_expect_tlv(input, alg_end, &alg_cursor, 0x06, &oid_off, &oid_len);
|
|
1481
|
-
if (ret != PQ_SUCCESS)
|
|
1482
|
-
return ret;
|
|
1483
|
-
if (alg_cursor != alg_end)
|
|
1484
|
-
return PQ_ERROR_BUFFER;
|
|
1485
|
-
ret = pq_oid_der_to_text(input + oid_tlv_start, alg_cursor - oid_tlv_start, &oid_text);
|
|
1486
|
-
}
|
|
1796
|
+
ret = pq_oid_der_to_text(input + parts.oid_offset, parts.oid_tlv_len, &oid_text);
|
|
1487
1797
|
if (ret != PQ_SUCCESS)
|
|
1488
1798
|
return ret;
|
|
1489
|
-
|
|
1490
|
-
ret = pq_der_expect_tlv(input, outer_end, &offset, 0x04, &priv_off, &priv_len);
|
|
1491
|
-
if (ret != PQ_SUCCESS)
|
|
1492
|
-
goto cleanup;
|
|
1493
|
-
if (offset != outer_end) {
|
|
1494
|
-
ret = PQ_ERROR_BUFFER;
|
|
1495
|
-
goto cleanup;
|
|
1496
|
-
}
|
|
1497
|
-
private_key = malloc(priv_len ? priv_len : 1);
|
|
1799
|
+
private_key = malloc(parts.private_key_len ? parts.private_key_len : 1);
|
|
1498
1800
|
if (!private_key) {
|
|
1499
1801
|
ret = PQ_ERROR_NOMEM;
|
|
1500
1802
|
goto cleanup;
|
|
1501
1803
|
}
|
|
1502
|
-
if (
|
|
1503
|
-
memcpy(private_key, input +
|
|
1804
|
+
if (parts.private_key_len)
|
|
1805
|
+
memcpy(private_key, input + parts.private_key_offset, parts.private_key_len);
|
|
1504
1806
|
|
|
1505
1807
|
*oid_text_out = oid_text;
|
|
1506
1808
|
*private_key_out = private_key;
|
|
1507
|
-
*private_key_len_out =
|
|
1809
|
+
*private_key_len_out = parts.private_key_len;
|
|
1508
1810
|
oid_text = NULL;
|
|
1509
1811
|
private_key = NULL;
|
|
1510
1812
|
ret = PQ_SUCCESS;
|
|
@@ -1513,7 +1815,7 @@ cleanup:
|
|
|
1513
1815
|
if (oid_text)
|
|
1514
1816
|
free(oid_text);
|
|
1515
1817
|
if (private_key) {
|
|
1516
|
-
pq_secure_wipe(private_key,
|
|
1818
|
+
pq_secure_wipe(private_key, parts.private_key_len);
|
|
1517
1819
|
free(private_key);
|
|
1518
1820
|
}
|
|
1519
1821
|
return ret;
|
|
@@ -1523,14 +1825,10 @@ int pq_pkcs8_encrypt_private_key_info_der(uint8_t **output, size_t *output_len,
|
|
|
1523
1825
|
const uint8_t *plain_der, size_t plain_der_len,
|
|
1524
1826
|
const char *passphrase, size_t passphrase_len,
|
|
1525
1827
|
int iterations) {
|
|
1526
|
-
const unsigned char *cursor;
|
|
1527
|
-
PKCS8_PRIV_KEY_INFO *p8 = NULL;
|
|
1528
1828
|
X509_SIG *encrypted = NULL;
|
|
1529
|
-
unsigned char salt[16];
|
|
1530
1829
|
unsigned char *der = NULL;
|
|
1531
|
-
|
|
1532
|
-
int
|
|
1533
|
-
int ret = PQ_ERROR_OPENSSL;
|
|
1830
|
+
size_t der_len = 0;
|
|
1831
|
+
int ret;
|
|
1534
1832
|
|
|
1535
1833
|
if (!output || !output_len || !plain_der || !passphrase || iterations <= 0 ||
|
|
1536
1834
|
passphrase_len > (size_t)INT_MAX || plain_der_len > (size_t)LONG_MAX)
|
|
@@ -1538,61 +1836,36 @@ int pq_pkcs8_encrypt_private_key_info_der(uint8_t **output, size_t *output_len,
|
|
|
1538
1836
|
*output = NULL;
|
|
1539
1837
|
*output_len = 0;
|
|
1540
1838
|
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
if (
|
|
1544
|
-
goto cleanup;
|
|
1545
|
-
if (RAND_bytes(salt, sizeof(salt)) != 1)
|
|
1546
|
-
goto cleanup;
|
|
1547
|
-
|
|
1548
|
-
encrypted = PKCS8_encrypt(-1, EVP_aes_256_cbc(), passphrase, (int)passphrase_len, salt,
|
|
1549
|
-
(int)sizeof(salt), iterations, p8);
|
|
1550
|
-
if (!encrypted)
|
|
1551
|
-
goto cleanup;
|
|
1552
|
-
|
|
1553
|
-
der_len = i2d_X509_SIG(encrypted, NULL);
|
|
1554
|
-
if (der_len <= 0) {
|
|
1555
|
-
ret = PQ_ERROR_OPENSSL;
|
|
1556
|
-
goto cleanup;
|
|
1557
|
-
}
|
|
1558
|
-
der = malloc((size_t)der_len);
|
|
1559
|
-
if (!der) {
|
|
1560
|
-
ret = PQ_ERROR_NOMEM;
|
|
1839
|
+
ret = pq_pkcs8_encrypt(plain_der, plain_der_len, passphrase, passphrase_len, iterations,
|
|
1840
|
+
&encrypted);
|
|
1841
|
+
if (ret != PQ_SUCCESS)
|
|
1561
1842
|
goto cleanup;
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
if (i2d_X509_SIG(encrypted, &der_cursor) != der_len ||
|
|
1565
|
-
(size_t)(der_cursor - der) != (size_t)der_len)
|
|
1843
|
+
ret = pq_x509_sig_to_der(encrypted, &der, &der_len);
|
|
1844
|
+
if (ret != PQ_SUCCESS)
|
|
1566
1845
|
goto cleanup;
|
|
1567
1846
|
|
|
1568
1847
|
*output = der;
|
|
1569
|
-
*output_len =
|
|
1848
|
+
*output_len = der_len;
|
|
1570
1849
|
der = NULL;
|
|
1571
1850
|
ret = PQ_SUCCESS;
|
|
1572
1851
|
|
|
1573
1852
|
cleanup:
|
|
1574
|
-
pq_secure_wipe(salt, sizeof(salt));
|
|
1575
1853
|
if (der) {
|
|
1576
|
-
pq_secure_wipe(der,
|
|
1854
|
+
pq_secure_wipe(der, der_len);
|
|
1577
1855
|
free(der);
|
|
1578
1856
|
}
|
|
1579
1857
|
if (encrypted)
|
|
1580
1858
|
X509_SIG_free(encrypted);
|
|
1581
|
-
if (p8)
|
|
1582
|
-
PKCS8_PRIV_KEY_INFO_free(p8);
|
|
1583
1859
|
return ret;
|
|
1584
1860
|
}
|
|
1585
1861
|
|
|
1586
1862
|
int pq_pkcs8_decrypt_private_key_info_der(uint8_t **output, size_t *output_len,
|
|
1587
1863
|
const uint8_t *encrypted_der, size_t encrypted_der_len,
|
|
1588
1864
|
const char *passphrase, size_t passphrase_len) {
|
|
1589
|
-
const unsigned char *cursor;
|
|
1590
|
-
X509_SIG *encrypted = NULL;
|
|
1591
1865
|
PKCS8_PRIV_KEY_INFO *p8 = NULL;
|
|
1592
1866
|
unsigned char *der = NULL;
|
|
1593
|
-
|
|
1594
|
-
int
|
|
1595
|
-
int ret = PQ_ERROR_OPENSSL;
|
|
1867
|
+
size_t der_len = 0;
|
|
1868
|
+
int ret;
|
|
1596
1869
|
|
|
1597
1870
|
if (!output || !output_len || !encrypted_der || !passphrase ||
|
|
1598
1871
|
passphrase_len > (size_t)INT_MAX || encrypted_der_len > (size_t)LONG_MAX)
|
|
@@ -1600,41 +1873,25 @@ int pq_pkcs8_decrypt_private_key_info_der(uint8_t **output, size_t *output_len,
|
|
|
1600
1873
|
*output = NULL;
|
|
1601
1874
|
*output_len = 0;
|
|
1602
1875
|
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
if (!encrypted || cursor != encrypted_der + encrypted_der_len)
|
|
1606
|
-
goto cleanup;
|
|
1607
|
-
p8 = PKCS8_decrypt(encrypted, passphrase, (int)passphrase_len);
|
|
1608
|
-
if (!p8)
|
|
1609
|
-
goto cleanup;
|
|
1610
|
-
|
|
1611
|
-
der_len = i2d_PKCS8_PRIV_KEY_INFO(p8, NULL);
|
|
1612
|
-
if (der_len <= 0)
|
|
1613
|
-
goto cleanup;
|
|
1614
|
-
der = malloc((size_t)der_len);
|
|
1615
|
-
if (!der) {
|
|
1616
|
-
ret = PQ_ERROR_NOMEM;
|
|
1876
|
+
ret = pq_pkcs8_decrypt(encrypted_der, encrypted_der_len, passphrase, passphrase_len, &p8);
|
|
1877
|
+
if (ret != PQ_SUCCESS)
|
|
1617
1878
|
goto cleanup;
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
if (i2d_PKCS8_PRIV_KEY_INFO(p8, &der_cursor) != der_len ||
|
|
1621
|
-
(size_t)(der_cursor - der) != (size_t)der_len)
|
|
1879
|
+
ret = pq_pkcs8_info_to_der(p8, &der, &der_len);
|
|
1880
|
+
if (ret != PQ_SUCCESS)
|
|
1622
1881
|
goto cleanup;
|
|
1623
1882
|
|
|
1624
1883
|
*output = der;
|
|
1625
|
-
*output_len =
|
|
1884
|
+
*output_len = der_len;
|
|
1626
1885
|
der = NULL;
|
|
1627
1886
|
ret = PQ_SUCCESS;
|
|
1628
1887
|
|
|
1629
1888
|
cleanup:
|
|
1630
1889
|
if (der) {
|
|
1631
|
-
pq_secure_wipe(der,
|
|
1890
|
+
pq_secure_wipe(der, der_len);
|
|
1632
1891
|
free(der);
|
|
1633
1892
|
}
|
|
1634
1893
|
if (p8)
|
|
1635
1894
|
PKCS8_PRIV_KEY_INFO_free(p8);
|
|
1636
|
-
if (encrypted)
|
|
1637
|
-
X509_SIG_free(encrypted);
|
|
1638
1895
|
return ret;
|
|
1639
1896
|
}
|
|
1640
1897
|
|