Skip to content

Commit ae02941

Browse files
committed
Add infrastructure to store an explicit public key.
1 parent e93e5b3 commit ae02941

File tree

4 files changed

+89
-1
lines changed

4 files changed

+89
-1
lines changed

src/scitokens.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,21 @@ int scitoken_deserialize(const char *value, SciToken *token, char const* const*
169169
return 0;
170170
}
171171

172+
int scitoken_store_public_ec_key(const char *issuer, const char *keyid, const char *key, char **err_msg)
173+
{
174+
bool success;
175+
try {
176+
success = scitokens::Validator::store_public_ec_key(issuer, keyid, key);
177+
} catch (std::exception &exc) {
178+
if (err_msg) {
179+
*err_msg = strdup(exc.what());
180+
}
181+
return -1;
182+
}
183+
184+
return success ? 0 : -1;
185+
}
186+
172187
Validator validator_create() {
173188
return new Validator();
174189
}

src/scitokens.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,8 @@ void scitoken_set_serialize_mode(SciToken token, SciTokenProfile profile);
6161

6262
int scitoken_deserialize(const char *value, SciToken *token, char const* const* allowed_issuers, char **err_msg);
6363

64+
int scitoken_store_public_ec_key(const char *issuer, const char *keyid, const char *value, char **err_msg);
65+
6466
Validator validator_create();
6567

6668
/**

src/scitokens_internal.cpp

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,15 @@ std::string b64url_decode_nopadding(const std::string &input)
207207
}
208208

209209

210+
// Base64-encode without padding.
211+
std::string b64url_encode_nopadding(const std::string &input)
212+
{
213+
std::string result = jwt::base::encode<local_base64url>(input);
214+
auto pos = result.find("=");
215+
return result.substr(0, pos);
216+
}
217+
218+
210219
std::string
211220
es256_from_coords(const std::string &x_str, const std::string &y_str) {
212221
auto x_decode = b64url_decode_nopadding(x_str);
@@ -484,6 +493,63 @@ Validator::get_public_key_pem(const std::string &issuer, const std::string &kid,
484493
}
485494

486495

496+
bool
497+
scitokens::Validator::store_public_ec_key(const std::string &issuer, const std::string &keyid,
498+
const std::string &public_key)
499+
{
500+
std::unique_ptr<BIO, decltype(&BIO_free_all)> pubkey_bio(BIO_new(BIO_s_mem()), BIO_free_all);
501+
if ((size_t)BIO_write(pubkey_bio.get(), public_key.data(), public_key.size()) != public_key.size()) {
502+
return false;
503+
}
504+
std::unique_ptr<EC_KEY, decltype(&EC_KEY_free)> pkey
505+
(PEM_read_bio_EC_PUBKEY(pubkey_bio.get(), nullptr, nullptr, nullptr), EC_KEY_free);
506+
if (!pkey) {return false;}
507+
508+
EC_GROUP *params = (EC_GROUP *)EC_KEY_get0_group(pkey.get());
509+
if (!params) {
510+
throw UnsupportedKeyException("Unable to get OpenSSL EC group");
511+
}
512+
513+
const EC_POINT *point = EC_KEY_get0_public_key(pkey.get());
514+
if (!point) {
515+
throw UnsupportedKeyException("Unable to get OpenSSL EC point");
516+
}
517+
518+
std::unique_ptr<BIGNUM, decltype(&BN_free)> x_bignum(BN_new(), BN_free);
519+
std::unique_ptr<BIGNUM, decltype(&BN_free)> y_bignum(BN_new(), BN_free);
520+
if (!EC_POINT_get_affine_coordinates_GFp(params, point, x_bignum.get(), y_bignum.get(), nullptr)) {
521+
throw UnsupportedKeyException("Unable to get OpenSSL affine coordinates");
522+
}
523+
524+
auto x_num = BN_num_bytes(x_bignum.get());
525+
auto y_num = BN_num_bytes(y_bignum.get());
526+
std::vector<unsigned char> x_bin; x_bin.reserve(x_num);
527+
std::vector<unsigned char> y_bin; y_bin.reserve(y_num);
528+
BN_bn2bin(x_bignum.get(), &x_bin[0]);
529+
BN_bn2bin(y_bignum.get(), &y_bin[0]);
530+
std::string x_str(reinterpret_cast<char*>(&x_bin[0]), x_num);
531+
std::string y_str(reinterpret_cast<char*>(&y_bin[0]), y_num);
532+
533+
picojson::object key_obj;
534+
key_obj["alg"] = picojson::value("ES256");
535+
key_obj["kid"] = picojson::value(keyid);
536+
key_obj["use"] = picojson::value("sig");
537+
key_obj["kty"] = picojson::value("EC");
538+
key_obj["x"] = picojson::value(b64url_encode_nopadding(x_str));
539+
key_obj["y"] = picojson::value(b64url_encode_nopadding(y_str));
540+
std::vector<picojson::value> key_list;
541+
key_list.emplace_back(key_obj);
542+
543+
picojson::object top_obj;
544+
top_obj["keys"] = picojson::value(key_list);
545+
546+
picojson::value top_value(top_obj);
547+
548+
auto now = std::time(NULL);
549+
return store_public_keys(issuer, top_value, now + 600, now + 4*3600);
550+
}
551+
552+
487553
bool
488554
scitokens::Enforcer::scope_validator(const jwt::claim &claim, void *myself) {
489555
auto me = reinterpret_cast<scitokens::Enforcer*>(myself);

src/scitokens_internal.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -434,11 +434,16 @@ class Validator {
434434
m_validate_profile = profile;
435435
}
436436

437+
/**
438+
* Store the contents of a public EC key for a given issuer.
439+
*/
440+
static bool store_public_ec_key(const std::string &issuer, const std::string &kid, const std::string &key);
441+
437442
private:
438443
void get_public_key_pem(const std::string &issuer, const std::string &kid, std::string &public_pem, std::string &algorithm);
439444
void get_public_keys_from_web(const std::string &issuer, picojson::value &keys, int64_t &next_update, int64_t &expires);
440445
bool get_public_keys_from_db(const std::string issuer, int64_t now, picojson::value &keys, int64_t &next_update);
441-
bool store_public_keys(const std::string &issuer, const picojson::value &keys, int64_t next_update, int64_t expires);
446+
static bool store_public_keys(const std::string &issuer, const picojson::value &keys, int64_t next_update, int64_t expires);
442447

443448
bool m_validate_all_claims{true};
444449
SciToken::Profile m_profile{SciToken::Profile::COMPAT};

0 commit comments

Comments
 (0)