return dcrypt_openssl_public_key_id_evp(priv, md, result, error_r);
}
+static bool
+dcrypt_openssl_digest(const char *algorithm, const void *data, size_t data_len,
+ buffer_t *digest_r, const char **error_r)
+{
+ bool ret;
+ EVP_MD_CTX *mdctx;
+ const EVP_MD *md = EVP_get_digestbyname(algorithm);
+ if (md == NULL)
+ return dcrypt_openssl_error(error_r);
+ unsigned int md_size = EVP_MD_size(md);
+ if ((mdctx = EVP_MD_CTX_create()) == NULL)
+ return dcrypt_openssl_error(error_r);
+ unsigned char *buf = buffer_append_space_unsafe(digest_r, md_size);
+ if (EVP_DigestInit_ex(mdctx, EVP_sha256(), NULL) != 1 ||
+ EVP_DigestUpdate(mdctx, data, data_len) != 1 ||
+ EVP_DigestFinal_ex(mdctx, buf, &md_size) != 1) {
+ ret = dcrypt_openssl_error(error_r);
+ } else {
+ ret = TRUE;
+ }
+ return ret;
+}
+
+static bool
+dcrypt_openssl_sign_ecdsa(struct dcrypt_private_key *key, const char *algorithm,
+ const void *data, size_t data_len, buffer_t *signature_r,
+ const char **error_r)
+{
+ EVP_PKEY *pkey = key->key;
+ EC_KEY *ec_key = EVP_PKEY_get0_EC_KEY(pkey);
+ bool ret;
+
+ /* digest data */
+ buffer_t *digest = t_buffer_create(64);
+ if (!dcrypt_openssl_digest(algorithm, data, data_len, digest, error_r))
+ return FALSE;
+
+ /* sign data */
+ ECDSA_SIG *ec_sig;
+ if ((ec_sig = ECDSA_do_sign(digest->data, digest->used, ec_key)) == NULL)
+ return dcrypt_openssl_error(error_r);
+
+ /* export signature */
+ const BIGNUM *r;
+ const BIGNUM *s;
+
+ ECDSA_SIG_get0(ec_sig, &r, &s);
+
+ /* write r */
+ int bytes = BN_num_bytes(r);
+ unsigned char *buf = buffer_append_space_unsafe(signature_r, bytes);
+ if (BN_bn2bin(r, buf) != bytes) {
+ ret = dcrypt_openssl_error(error_r);
+ } else {
+ bytes = BN_num_bytes(s);
+ buf = buffer_append_space_unsafe(signature_r, bytes);
+ if (BN_bn2bin(s, buf) != bytes) {
+ ret = dcrypt_openssl_error(error_r);
+ } else {
+ ret = TRUE;
+ }
+ }
+
+ ECDSA_SIG_free(ec_sig);
+
+ return ret;
+}
+
static bool
dcrypt_openssl_sign(struct dcrypt_private_key *key, const char *algorithm,
+ enum dcrypt_signature_format format,
const void *data, size_t data_len, buffer_t *signature_r,
enum dcrypt_padding padding, const char **error_r)
{
+ switch (format) {
+ case DCRYPT_SIGNATURE_FORMAT_DSS:
+ break;
+ case DCRYPT_SIGNATURE_FORMAT_X962:
+ if (EVP_PKEY_base_id(key->key) == EVP_PKEY_RSA) {
+ *error_r = "Format does not support RSA";
+ return FALSE;
+ }
+ return dcrypt_openssl_sign_ecdsa(key, algorithm,
+ data, data_len, signature_r, error_r);
+ default:
+ i_unreached();
+ }
+
EVP_PKEY_CTX *pctx = NULL;
EVP_MD_CTX *dctx;
bool ret;
return ret;
}
+static bool
+dcrypt_openssl_verify_ecdsa(struct dcrypt_public_key *key, const char *algorithm,
+ const void *data, size_t data_len,
+ const unsigned char *signature, size_t signature_len,
+ bool *valid_r, const char **error_r)
+{
+ EVP_PKEY *pkey = key->key;
+ EC_KEY *ec_key = EVP_PKEY_get0_EC_KEY(pkey);
+ int ec;
+
+ /* digest data */
+ buffer_t *digest = t_buffer_create(64);
+ if (!dcrypt_openssl_digest(algorithm, data, data_len, digest, error_r))
+ return FALSE;
+
+ BIGNUM *r = BN_new();
+ BIGNUM *s = BN_new();
+ /* attempt to decode BIGNUMs */
+ if (BN_bin2bn(signature, signature_len / 2, r) == NULL) {
+ BN_free(r);
+ BN_free(s);
+ return dcrypt_openssl_error(error_r);
+ }
+ /* then next */
+ if (BN_bin2bn(CONST_PTR_OFFSET(signature, signature_len / 2),
+ signature_len / 2, s) == NULL) {
+ BN_free(r);
+ BN_free(s);
+ return dcrypt_openssl_error(error_r);
+ }
+
+ /* reconstruct signature */
+ ECDSA_SIG *ec_sig = ECDSA_SIG_new();
+ ECDSA_SIG_set0(ec_sig, r, s);
+
+ /* verify it */
+ ec = ECDSA_do_verify(digest->data, digest->used, ec_sig, ec_key);
+ ECDSA_SIG_free(ec_sig);
+
+ if (ec == 1) {
+ *valid_r = TRUE;
+ } else if (ec == 0) {
+ *valid_r = FALSE;
+ } else {
+ return dcrypt_openssl_error(error_r);
+ }
+ return TRUE;
+}
+
static bool
dcrypt_openssl_verify(struct dcrypt_public_key *key, const char *algorithm,
+ enum dcrypt_signature_format format,
const void *data, size_t data_len,
const unsigned char *signature, size_t signature_len,
bool *valid_r, enum dcrypt_padding padding,
const char **error_r)
{
+ switch (format) {
+ case DCRYPT_SIGNATURE_FORMAT_DSS:
+ break;
+ case DCRYPT_SIGNATURE_FORMAT_X962:
+ if (EVP_PKEY_base_id(key->key) == EVP_PKEY_RSA) {
+ *error_r = "Format does not support RSA";
+ return FALSE;
+ }
+ return dcrypt_openssl_verify_ecdsa(key, algorithm,
+ data, data_len, signature, signature_len,
+ valid_r, error_r);
+ default:
+ i_unreached();
+ }
+
EVP_PKEY_CTX *pctx = NULL;
EVP_MD_CTX *dctx;
bool ret;
void (*key_set_usage_private)(struct dcrypt_private_key *key,
enum dcrypt_key_usage usage);
bool (*sign)(struct dcrypt_private_key *key, const char *algorithm,
+ enum dcrypt_signature_format format,
const void *data, size_t data_len, buffer_t *signature_r,
enum dcrypt_padding padding, const char **error_r);
bool (*verify)(struct dcrypt_public_key *key, const char *algorithm,
+ enum dcrypt_signature_format format,
const void *data, size_t data_len,
const unsigned char *signature, size_t signature_len,
bool *valid_r, enum dcrypt_padding padding,
}
bool dcrypt_sign(struct dcrypt_private_key *key, const char *algorithm,
+ enum dcrypt_signature_format format,
const void *data, size_t data_len, buffer_t *signature_r,
enum dcrypt_padding padding, const char **error_r)
{
return FALSE;
}
- return dcrypt_vfs->sign(key, algorithm, data, data_len,
+ return dcrypt_vfs->sign(key, algorithm, format, data, data_len,
signature_r, padding, error_r);
}
bool dcrypt_verify(struct dcrypt_public_key *key, const char *algorithm,
+ enum dcrypt_signature_format format,
const void *data, size_t data_len,
const unsigned char *signature, size_t signature_len,
bool *valid_r, enum dcrypt_padding padding,
return FALSE;
}
- return dcrypt_vfs->verify(key, algorithm, data, data_len,
+ return dcrypt_vfs->verify(key, algorithm, format, data, data_len,
signature, signature_len,
valid_r, padding, error_r);
}
DCRYPT_KEY_USAGE_SIGN,
};
+enum dcrypt_signature_format {
+ DCRYPT_SIGNATURE_FORMAT_DSS,
+ DCRYPT_SIGNATURE_FORMAT_X962,
+};
+
/* this parameter makes sense with RSA only
default for RSA means either PSS (sign/verify)
or OAEP (encrypt/decrypt).
/* returns false on error, true on success */
bool dcrypt_sign(struct dcrypt_private_key *key, const char *algorithm,
+ enum dcrypt_signature_format format,
const void *data, size_t data_len, buffer_t *signature_r,
enum dcrypt_padding padding, const char **error_r);
/* check valid_r for signature validity
false return means it wasn't able to verify it for other reasons */
bool dcrypt_verify(struct dcrypt_public_key *key, const char *algorithm,
+ enum dcrypt_signature_format format,
const void *data, size_t data_len,
const unsigned char *signature, size_t signature_len,
bool *valid_r, enum dcrypt_padding padding,
if (priv_key == NULL)
i_fatal("%s", error);
dcrypt_key_convert_private_to_public(priv_key, &pub_key);
- test_assert(dcrypt_sign(priv_key,
- "sha256", data, strlen(data), signature, 0, &error));
+ test_assert(dcrypt_sign(priv_key, "sha256", DCRYPT_SIGNATURE_FORMAT_DSS,
+ data, strlen(data), signature, 0, &error));
/* verify signature */
- test_assert(dcrypt_verify(pub_key,
- "sha256", data, strlen(data),
+ test_assert(dcrypt_verify(pub_key, "sha256", DCRYPT_SIGNATURE_FORMAT_DSS,
+ data, strlen(data),
signature->data, signature->used, &valid, 0, &error) && valid);
dcrypt_key_unref_public(&pub_key);
if (priv_key == NULL)
i_fatal("%s", error);
dcrypt_key_convert_private_to_public(priv_key, &pub_key);
- test_assert(dcrypt_sign(priv_key,
- "sha256", data, strlen(data), signature, 0, &error));
+ test_assert(dcrypt_sign(priv_key, "sha256", DCRYPT_SIGNATURE_FORMAT_DSS,
+ data, strlen(data), signature, 0, &error));
/* verify signature */
- test_assert(dcrypt_verify(pub_key,
- "sha256", data, strlen(data), signature->data,
+ test_assert(dcrypt_verify(pub_key, "sha256", DCRYPT_SIGNATURE_FORMAT_DSS,
+ data, strlen(data), signature->data,
signature->used, &valid, 0, &error) && valid);
dcrypt_key_unref_public(&pub_key);
test_assert(dcrypt_key_load_public(&pair.pub, pub_key_pem, NULL));
test_assert(dcrypt_key_load_private(&pair.priv, priv_key_pem, NULL, NULL, NULL));
/* validate signature */
- test_assert(dcrypt_verify(pair.pub, "sha256", input, strlen(input),
+ test_assert(dcrypt_verify(pair.pub, "sha256", DCRYPT_SIGNATURE_FORMAT_DSS,
+ input, strlen(input),
sig, sizeof(sig), &valid, 0, &error) &&
valid == TRUE);
"\"crv\":\"P-256\","
"\"x\":\"Kp0Y4-Wpt-D9t_2XenFIj0LmvaZByLG69yOisek4aMI\","
"\"y\":\"wjEPB5BhH5SRPw1cCN5grWrLCphrW19fCFR8p7c9O5o\","
- "\"use\":\"sig\","
- "\"kid\":\"123\","
+ "\"use\":\"sig\","
+ "\"kid\":\"123\","
"\"d\":\"Po2z9rs86J2Qb_xWprr4idsWNPlgKf3G8-mftnE2ync\"}";
/* Acquired using another tool */
const char *pem_key =
test_assert(dcrypt_key_load_public(&pub_key, key, &error));
if (pub_key == NULL)
i_fatal("%s", error);
- test_assert(dcrypt_verify(pub_key, "sha256", data, strlen(data),
+ test_assert(dcrypt_verify(pub_key, "sha256", DCRYPT_SIGNATURE_FORMAT_DSS,
+ data, strlen(data),
sig, sizeof(sig), &valid, DCRYPT_PADDING_RSA_PKCS1, &error) &&
valid);
dcrypt_key_unref_public(&pub_key);
test_end();
}
+/* Sample values from RFC8292 */
+static void test_static_verify_ecdsa_x962(void)
+{
+ const char *error = NULL;
+ bool valid;
+ struct dcrypt_public_key *pub_key = NULL;
+
+ test_begin("static verify (ecdsa x9.62)");
+ const char *data =
+ "eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiJ9.eyJhdWQiOiJodHRwczovL3B1c"
+ "2guZXhhbXBsZS5uZXQiLCJleHAiOjE0NTM1MjM3NjgsInN1YiI6Im1haWx0bzp"
+ "wdXNoQGV4YW1wbGUuY29tIn0";
+ const unsigned char sig[] = {
+ 0x8b,0x70,0x98,0x6f,0xbb,0x78,0xc5,0xfc,0x42,0x0e,0xab,
+ 0xa9,0xb4,0x53,0x9e,0xa4,0x2f,0x46,0x02,0xef,0xc7,0x2c,
+ 0x69,0x0c,0x94,0xcb,0x82,0x19,0x22,0xb6,0xae,0x98,0x94,
+ 0x7e,0x72,0xbd,0xa2,0x31,0x70,0x0d,0x76,0xf5,0x26,0xb1,
+ 0x2b,0xb6,0x6c,0xac,0x6b,0x33,0x63,0x8e,0xf5,0xb6,0x2f,
+ 0xd3,0xa4,0x49,0x21,0xf3,0xbe,0x80,0xf5,0xa0
+ };
+ const char *key =
+"-----BEGIN PUBLIC KEY-----\n"
+"MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEDUfHPKLVFQzVvnCPGyfucbECzPDa\n"
+"7rWbXriLcysAjEcXpgrmHhINiJz51G5T9EI8J8Dlqr2iNLCTljYSYKUE+w==\n"
+"-----END PUBLIC KEY-----";
+
+ test_assert(dcrypt_key_load_public(&pub_key, key, &error));
+ if (pub_key == NULL)
+ i_fatal("%s", error);
+ test_assert(dcrypt_verify(pub_key, "sha256", DCRYPT_SIGNATURE_FORMAT_X962,
+ data, strlen(data),
+ sig, sizeof(sig), &valid, DCRYPT_PADDING_RSA_PKCS1, &error) &&
+ valid);
+ dcrypt_key_unref_public(&pub_key);
+
+ test_end();
+}
+
+
int main(void)
{
struct dcrypt_settings set = {
test_sign_verify_ecdsa,
test_static_verify_ecdsa,
test_static_verify_rsa,
+ test_static_verify_ecdsa_x962,
NULL
};