1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 #include <openssl/pem.h>
6 #include "user-record-sign.h"
9 static int user_record_signable_json(UserRecord
*ur
, char **ret
) {
10 _cleanup_(user_record_unrefp
) UserRecord
*reduced
= NULL
;
11 _cleanup_(json_variant_unrefp
) JsonVariant
*j
= NULL
;
17 r
= user_record_clone(ur
, USER_RECORD_REQUIRE_REGULAR
|USER_RECORD_ALLOW_PRIVILEGED
|USER_RECORD_ALLOW_PER_MACHINE
|USER_RECORD_STRIP_SECRET
|USER_RECORD_STRIP_BINDING
|USER_RECORD_STRIP_STATUS
|USER_RECORD_STRIP_SIGNATURE
, &reduced
);
21 j
= json_variant_ref(reduced
->json
);
23 r
= json_variant_normalize(&j
);
27 return json_variant_format(j
, 0, ret
);
30 DEFINE_TRIVIAL_CLEANUP_FUNC(EVP_MD_CTX
*, EVP_MD_CTX_free
);
32 int user_record_sign(UserRecord
*ur
, EVP_PKEY
*private_key
, UserRecord
**ret
) {
33 _cleanup_(json_variant_unrefp
) JsonVariant
*encoded
= NULL
, *v
= NULL
;
34 _cleanup_(user_record_unrefp
) UserRecord
*signed_ur
= NULL
;
35 _cleanup_(EVP_MD_CTX_freep
) EVP_MD_CTX
*md_ctx
= NULL
;
36 _cleanup_free_
char *text
= NULL
, *key
= NULL
;
37 size_t signature_size
= 0, key_size
= 0;
38 _cleanup_free_
void *signature
= NULL
;
39 _cleanup_fclose_
FILE *mf
= NULL
;
46 r
= user_record_signable_json(ur
, &text
);
50 md_ctx
= EVP_MD_CTX_new();
54 if (EVP_DigestSignInit(md_ctx
, NULL
, NULL
, NULL
, private_key
) <= 0)
57 /* Request signature size */
58 if (EVP_DigestSign(md_ctx
, NULL
, &signature_size
, (uint8_t*) text
, strlen(text
)) <= 0)
61 signature
= malloc(signature_size
);
65 if (EVP_DigestSign(md_ctx
, signature
, &signature_size
, (uint8_t*) text
, strlen(text
)) <= 0)
68 mf
= open_memstream_unlocked(&key
, &key_size
);
72 if (PEM_write_PUBKEY(mf
, private_key
) <= 0)
75 r
= fflush_and_check(mf
);
79 r
= json_build(&encoded
, JSON_BUILD_ARRAY(
80 JSON_BUILD_OBJECT(JSON_BUILD_PAIR("data", JSON_BUILD_BASE64(signature
, signature_size
)),
81 JSON_BUILD_PAIR("key", JSON_BUILD_STRING(key
)))));
85 v
= json_variant_ref(ur
->json
);
87 r
= json_variant_set_field(&v
, "signature", encoded
);
92 json_variant_dump(v
, JSON_FORMAT_PRETTY
|JSON_FORMAT_COLOR_AUTO
, NULL
, NULL
);
94 signed_ur
= user_record_new();
98 r
= user_record_load(signed_ur
, v
, USER_RECORD_LOAD_FULL
);
102 *ret
= TAKE_PTR(signed_ur
);
106 int user_record_verify(UserRecord
*ur
, EVP_PKEY
*public_key
) {
107 _cleanup_free_
char *text
= NULL
;
108 unsigned n_good
= 0, n_bad
= 0;
109 JsonVariant
*array
, *e
;
115 array
= json_variant_by_key(ur
->json
, "signature");
117 return USER_RECORD_UNSIGNED
;
119 if (!json_variant_is_array(array
))
122 if (json_variant_elements(array
) == 0)
123 return USER_RECORD_UNSIGNED
;
125 r
= user_record_signable_json(ur
, &text
);
129 JSON_VARIANT_ARRAY_FOREACH(e
, array
) {
130 _cleanup_(EVP_MD_CTX_freep
) EVP_MD_CTX
*md_ctx
= NULL
;
131 _cleanup_free_
void *signature
= NULL
;
132 size_t signature_size
= 0;
135 if (!json_variant_is_object(e
))
138 data
= json_variant_by_key(e
, "data");
142 r
= json_variant_unbase64(data
, &signature
, &signature_size
);
146 md_ctx
= EVP_MD_CTX_new();
150 if (EVP_DigestVerifyInit(md_ctx
, NULL
, NULL
, NULL
, public_key
) <= 0)
153 if (EVP_DigestVerify(md_ctx
, signature
, signature_size
, (uint8_t*) text
, strlen(text
)) <= 0) {
161 return n_good
> 0 ? (n_bad
== 0 ? USER_RECORD_SIGNED_EXCLUSIVE
: USER_RECORD_SIGNED
) :
162 (n_bad
== 0 ? USER_RECORD_UNSIGNED
: USER_RECORD_FOREIGN
);
165 int user_record_has_signature(UserRecord
*ur
) {
168 array
= json_variant_by_key(ur
->json
, "signature");
172 if (!json_variant_is_array(array
))
175 return json_variant_elements(array
) > 0;