diff --git a/lasso/id-ff/server.c b/lasso/id-ff/server.c index e5d75c4a..c95e2713 100644 --- a/lasso/id-ff/server.c +++ b/lasso/id-ff/server.c @@ -49,6 +49,7 @@ #define RSA_SHA1 "RSA_SHA1" #define DSA_SHA1 "DSA_SHA1" +#define HMAC_SHA1 "HMAC_SHA1" /*****************************************************************************/ /* public methods */ @@ -273,7 +274,7 @@ static xmlNode* get_xmlNode(LassoNode *node, gboolean lasso_dump) { LassoServer *server = LASSO_SERVER(node); - char *signature_methods[] = { NULL, "RSA_SHA1", "DSA_SHA1"}; + char *signature_methods[] = { NULL, RSA_SHA1, DSA_SHA1, HMAC_SHA1}; xmlNode *xmlnode = NULL, *ret_xmlnode = NULL; xmlnode = parent_class->get_xmlNode(node, lasso_dump); @@ -327,6 +328,8 @@ init_from_xml(LassoNode *node, xmlNode *xmlnode) server->signature_method = LASSO_SIGNATURE_METHOD_RSA_SHA1; else if (lasso_strisequal((char*) s, DSA_SHA1)) server->signature_method = LASSO_SIGNATURE_METHOD_DSA_SHA1; + else if (lasso_strisequal((char*) s, HMAC_SHA1)) + server->signature_method = LASSO_SIGNATURE_METHOD_HMAC_SHA1; else { warning("Unable to rebuild a LassoServer object from XML, bad SignatureMethod: %s", s); diff --git a/lasso/xml/private.h b/lasso/xml/private.h index cc46ac64..0a301112 100644 --- a/lasso/xml/private.h +++ b/lasso/xml/private.h @@ -264,6 +264,12 @@ void lasso_node_get_encryption(LassoNode *node, xmlSecKey **encryption_public_ke LassoEncryptionSymKeyType *encryption_sym_key_type); gboolean lasso_base64_decode(const char *from, char **buffer, int *buffer_len); +xmlSecKeyPtr +lasso_create_hmac_key(const xmlSecByte * buf, xmlSecSize size); + +lasso_error_t +lasso_get_hmac_key(const xmlSecKey *key, void **buffer, size_t *size); + LassoSignatureContext lasso_make_signature_context_from_buffer(const char *buffer, size_t length, const char *password, LassoSignatureMethod signature_method, const char *certificate); diff --git a/lasso/xml/tools.c b/lasso/xml/tools.c index 47062a66..156de4b6 100644 --- a/lasso/xml/tools.c +++ b/lasso/xml/tools.c @@ -479,6 +479,9 @@ lasso_query_sign(char *query, LassoSignatureContext context) char *new_query = NULL, *s_new_query = NULL; int status = 0; const xmlChar *algo_href = NULL; + char *hmac_key; + size_t hmac_key_length; + const EVP_MD *md; xmlSecKey *key; xmlSecKeyData *key_data; unsigned int sigret_size = 0; @@ -500,6 +503,9 @@ lasso_query_sign(char *query, LassoSignatureContext context) case LASSO_SIGNATURE_METHOD_DSA_SHA1: algo_href = xmlSecHrefDsaSha1; break; + case LASSO_SIGNATURE_METHOD_HMAC_SHA1: + algo_href = xmlSecHrefHmacSha1; + break; case LASSO_SIGNATURE_METHOD_NONE: case LASSO_SIGNATURE_METHOD_LAST: g_assert_not_reached(); @@ -531,6 +537,18 @@ lasso_query_sign(char *query, LassoSignatureContext context) /* alloc memory for sigret */ sigret_size = DSA_size(dsa); break; + case LASSO_SIGNATURE_METHOD_HMAC_SHA1: + lasso_get_hmac_key(key, (void**)&hmac_key, + &hmac_key_length); + g_assert(hmac_key); + md = EVP_sha1(); + sigret_size = EVP_MD_size(md); + /* key should be at least 128 bits long */ + if (hmac_key_length < 16) { + critical("HMAC key should be at least 128 bits long"); + goto done; + } + break; default: g_assert_not_reached(); } @@ -546,6 +564,11 @@ lasso_query_sign(char *query, LassoSignatureContext context) status = DSA_sign(NID_sha1, (unsigned char*)digest, 20, sigret, &siglen, dsa); break; + case LASSO_SIGNATURE_METHOD_HMAC_SHA1: + HMAC(md, hmac_key, hmac_key_length, (unsigned char *)new_query, + strlen(new_query), sigret, &siglen); + status = 1; + break; case LASSO_SIGNATURE_METHOD_LAST: case LASSO_SIGNATURE_METHOD_NONE: g_assert_not_reached(); @@ -566,6 +589,7 @@ lasso_query_sign(char *query, LassoSignatureContext context) switch (sign_method) { case LASSO_SIGNATURE_METHOD_RSA_SHA1: case LASSO_SIGNATURE_METHOD_DSA_SHA1: + case LASSO_SIGNATURE_METHOD_HMAC_SHA1: s_new_query = g_strdup_printf("%s&Signature=%s", new_query, (char*) e_b64_sigret); break; @@ -613,6 +637,9 @@ lasso_query_verify_helper(const char *signed_content, const char *b64_signature, char *digest = NULL; xmlSecByte *signature = NULL; int key_size = 0; + unsigned char *hmac_key = NULL; + unsigned int hmac_key_length = 0; + const EVP_MD *md = NULL; lasso_error_t rc = 0; LassoSignatureMethod method = LASSO_SIGNATURE_METHOD_NONE; @@ -627,6 +654,11 @@ lasso_query_verify_helper(const char *signed_content, const char *b64_signature, dsa = xmlSecOpenSSLKeyDataDsaGetDsa(key->value); key_size = DSA_size(dsa); method = LASSO_SIGNATURE_METHOD_DSA_SHA1; + } else if (lasso_strisequal(algorithm, (char*)xmlSecHrefHmacSha1)) { + lasso_check_good_rc(lasso_get_hmac_key(key, (void**)&hmac_key, &hmac_key_length)); + md = EVP_sha1(); + key_size = EVP_MD_size(md); + method = LASSO_SIGNATURE_METHOD_HMAC_SHA1; } else { goto_cleanup_with_rc(LASSO_DS_ERROR_INVALID_SIGALG); } @@ -665,6 +697,15 @@ lasso_query_verify_helper(const char *signed_content, const char *b64_signature, key_size, dsa) == 1, LASSO_DS_ERROR_INVALID_SIGNATURE); break; + case LASSO_SIGNATURE_METHOD_HMAC_SHA1: + digest = g_malloc(key_size); + HMAC(md, hmac_key, hmac_key_length, (unsigned char*)signed_content, + strlen(signed_content), (unsigned char*)digest, NULL); + + goto_cleanup_if_fail_with_rc(lasso_crypto_memequal(digest, signature, + key_size), + LASSO_DS_ERROR_INVALID_SIGNATURE); + break; case LASSO_SIGNATURE_METHOD_NONE: case LASSO_SIGNATURE_METHOD_LAST: g_assert_not_reached(); @@ -1164,6 +1205,7 @@ lasso_saml_constrain_dsigctxt(xmlSecDSigCtxPtr dsigCtx) { if((xmlSecDSigCtxEnableSignatureTransform(dsigCtx, xmlSecTransformInclC14NId) < 0) || (xmlSecDSigCtxEnableSignatureTransform(dsigCtx, xmlSecTransformExclC14NId) < 0) || (xmlSecDSigCtxEnableSignatureTransform(dsigCtx, xmlSecTransformSha1Id) < 0) || + (xmlSecDSigCtxEnableSignatureTransform(dsigCtx, xmlSecTransformHmacSha1Id) < 0) || (xmlSecDSigCtxEnableSignatureTransform(dsigCtx, xmlSecTransformDsaSha1Id) < 0) || (xmlSecDSigCtxEnableSignatureTransform(dsigCtx, xmlSecTransformRsaSha1Id) < 0)) { @@ -1181,6 +1223,7 @@ lasso_saml_constrain_dsigctxt(xmlSecDSigCtxPtr dsigCtx) { /* Limit possible key info to X509, RSA and DSA */ if((xmlSecPtrListAdd(&(dsigCtx->keyInfoReadCtx.enabledKeyData), BAD_CAST xmlSecKeyDataX509Id) < 0) || + (xmlSecPtrListAdd(&(dsigCtx->keyInfoReadCtx.enabledKeyData), BAD_CAST xmlSecKeyDataHmacId) < 0) || (xmlSecPtrListAdd(&(dsigCtx->keyInfoReadCtx.enabledKeyData), BAD_CAST xmlSecKeyDataRsaId) < 0) || (xmlSecPtrListAdd(&(dsigCtx->keyInfoReadCtx.enabledKeyData), BAD_CAST xmlSecKeyDataDsaId) < 0)) { message(G_LOG_LEVEL_CRITICAL, "Error: failed to limit allowed key data"); @@ -1912,6 +1955,12 @@ _lasso_xmlsec_load_key_from_buffer(const char *buffer, size_t length, const char key_formats[i], password, NULL, NULL); } break; + case LASSO_SIGNATURE_METHOD_HMAC_SHA1: + private_key = xmlSecKeyReadMemory(xmlSecKeyDataHmacId, (xmlSecByte*)buffer, length); + if (private_key) { + xmlSecKeySetName(private_key, BAD_CAST "shared"); + } + break; case LASSO_SIGNATURE_METHOD_LAST: case LASSO_SIGNATURE_METHOD_NONE: g_assert_not_reached(); @@ -2264,6 +2313,40 @@ lasso_log_remove_handler(guint handler_id) g_log_remove_handler(LASSO_LOG_DOMAIN, handler_id); } +/** + * lasso_get_hmac_key: + * @key: an #xmlSecKey object + * @buffer: a byte buffer of size @size + * @size: the size of @buffer as bytes + * + * Extract the symetric HMAC key from the #xmlSecKey structure and place a pointer to i into the + * buffer variable. + * + * Return value: 0 if successful, an error code otherwise. + */ +lasso_error_t +lasso_get_hmac_key(const xmlSecKey *key, void **buffer, size_t *size) +{ + xmlSecKeyDataPtr key_data; + xmlSecBufferPtr key_data_buffer; + + lasso_null_param(key); + lasso_null_param(buffer); + lasso_null_param(size); + + if (key->value->id != xmlSecKeyDataHmacId) { + return LASSO_PARAM_ERROR_INVALID_VALUE; + } + key_data = xmlSecKeyGetValue((xmlSecKeyPtr)key); + g_return_val_if_fail(key_data, LASSO_PARAM_ERROR_INVALID_VALUE); + key_data_buffer = xmlSecKeyDataBinaryValueGetBuffer(key_data); + g_return_val_if_fail(key_data_buffer, LASSO_PARAM_ERROR_INVALID_VALUE); + *buffer = xmlSecBufferGetData(key_data_buffer); + *size = xmlSecBufferGetSize(key_data_buffer); + g_return_val_if_fail(*buffer && *size, LASSO_PARAM_ERROR_INVALID_VALUE); + return 0; +} + /** * lasso_make_signature_context_from_buffer: * @buffer: a byte buffer of size @length diff --git a/lasso/xml/xml.c b/lasso/xml/xml.c index 5eda2415..ed6aa5ce 100644 --- a/lasso/xml/xml.c +++ b/lasso/xml/xml.c @@ -2758,6 +2758,9 @@ lasso_node_add_signature_template(LassoNode *node, xmlNode *xmlnode, case LASSO_SIGNATURE_METHOD_DSA_SHA1: transform_id = xmlSecTransformDsaSha1Id; break; + case LASSO_SIGNATURE_METHOD_HMAC_SHA1: + transform_id = xmlSecTransformHmacSha1Id; + break; default: g_assert_not_reached(); } @@ -2789,6 +2792,13 @@ lasso_node_add_signature_template(LassoNode *node, xmlNode *xmlnode, xmlSecTmplKeyInfoAddKeyValue(key_info); } break; + case LASSO_SIGNATURE_METHOD_HMAC_SHA1: + if (context.signature_key->name) { + key_info = xmlSecTmplSignatureEnsureKeyInfo(signature, NULL); + xmlSecTmplKeyInfoAddKeyName(key_info, NULL); + + } + break; default: g_assert_not_reached(); } diff --git a/lasso/xml/xml.h b/lasso/xml/xml.h index cf735e7b..a0630aab 100644 --- a/lasso/xml/xml.h +++ b/lasso/xml/xml.h @@ -93,6 +93,7 @@ typedef enum { * LassoSignatureMethod: * @LASSO_SIGNATURE_METHOD_RSA_SHA1: sign using a RSA private key * @LASSO_SIGNATURE_METHOD_DSA_SHA1: sign using a DSA private key + * @LASSO_SIGNATURE_METHOD_HMAC_SHA1: sign using a an HMAC-SHA1 secret key * * Signature method. **/ @@ -100,6 +101,7 @@ typedef enum { LASSO_SIGNATURE_METHOD_NONE = 0, LASSO_SIGNATURE_METHOD_RSA_SHA1, LASSO_SIGNATURE_METHOD_DSA_SHA1, + LASSO_SIGNATURE_METHOD_HMAC_SHA1, LASSO_SIGNATURE_METHOD_LAST } LassoSignatureMethod; diff --git a/tests/login_tests_saml2.c b/tests/login_tests_saml2.c index 448e1fa6..bd174e50 100644 --- a/tests/login_tests_saml2.c +++ b/tests/login_tests_saml2.c @@ -900,6 +900,103 @@ START_TEST(test06_sso_sp_with_key_rollover) } END_TEST +#define test07_make_context(ctx, server_prefix, provider_role, provider_prefix, key) \ + ctx = lasso_server_new( \ + TESTSDATADIR server_prefix "/metadata.xml", \ + NULL, \ + NULL, /* Secret key to unlock private key */ \ + NULL); \ + check_not_null(ctx); \ + check_good_rc(lasso_server_add_provider( \ + ctx, \ + provider_role, \ + TESTSDATADIR provider_prefix "/metadata.xml", \ + NULL, \ + NULL)); \ + providers = g_hash_table_get_values(ctx->providers); \ + check_not_null(providers); \ + lasso_provider_set_specific_signing_key(LASSO_PROVIDER(providers->data), \ + key); \ + lasso_provider_add_key(LASSO_PROVIDER(providers->data), key, FALSE); \ + g_list_free(providers); + +static void +sso_initiated_by_sp(LassoServer *idp_context, LassoServer *sp_context) +{ + LassoLogin *idp_login_context; + LassoLogin *sp_login_context; + char *authn_request_query; + + check_not_null(idp_login_context = lasso_login_new(idp_context)); + check_not_null(sp_login_context = lasso_login_new(sp_context)) + + /* Create response */ + check_good_rc(lasso_login_init_authn_request(sp_login_context, NULL, LASSO_HTTP_METHOD_REDIRECT)); + + lasso_assign_string(LASSO_SAMLP2_AUTHN_REQUEST(sp_login_context->parent.request)->ProtocolBinding, + LASSO_SAML2_METADATA_BINDING_POST); + lasso_assign_string(LASSO_SAMLP2_AUTHN_REQUEST(sp_login_context->parent.request)->NameIDPolicy->Format, + LASSO_SAML2_NAME_IDENTIFIER_FORMAT_PERSISTENT); + LASSO_SAMLP2_AUTHN_REQUEST(sp_login_context->parent.request)->NameIDPolicy->AllowCreate = 1; + check_good_rc(lasso_login_build_authn_request_msg(sp_login_context)); + check_not_null(sp_login_context->parent.msg_url); + printf("authn_request: %s", sp_login_context->parent.msg_url); + authn_request_query = strchr(sp_login_context->parent.msg_url, '?'); + check_not_null(authn_request_query); + authn_request_query += 1; + check_good_rc(lasso_login_process_authn_request_msg(idp_login_context, authn_request_query)); + + check_good_rc(lasso_login_validate_request_msg(idp_login_context, + 1, /* authentication_result */ + 0 /* is_consent_obtained */ + )); + + check_good_rc(lasso_login_build_assertion(idp_login_context, + LASSO_SAML_AUTHENTICATION_METHOD_PASSWORD, + "FIXME: authenticationInstant", + "FIXME: reauthenticateOnOrAfter", + "FIXME: notBefore", + "FIXME: notOnOrAfter")); + check_good_rc(lasso_login_build_authn_response_msg(idp_login_context)); + check_not_null(idp_login_context->parent.msg_body); + check_not_null(idp_login_context->parent.msg_url); + printf("Xml Response: %s\n", lasso_node_export_to_xml(idp_login_context->parent.response)); + + /* Process response */ + check_good_rc(lasso_login_process_authn_response_msg(sp_login_context, + idp_login_context->parent.msg_body)); + check_good_rc(lasso_login_accept_sso(sp_login_context)); + + /* Cleanup */ + lasso_release_gobject(idp_login_context); + lasso_release_gobject(sp_login_context); +} + +START_TEST(test07_sso_sp_with_hmac_sha1_signatures) +{ + LassoServer *idp_context = NULL; + LassoServer *sp_context = NULL; + GList *providers; + LassoKey *key = NULL; + + /* Create the shared key */ + key = lasso_key_new_for_signature_from_memory("xxxxxxxxxxxxxxxx", 16, + NULL, LASSO_SIGNATURE_METHOD_HMAC_SHA1, NULL); + check_true(LASSO_IS_KEY(key)); + + /* Create an IdP context for IdP initiated SSO with provider metadata 1 */ + test07_make_context(idp_context, "idp6-saml2", LASSO_PROVIDER_ROLE_SP, "sp6-saml2", key) + test07_make_context(sp_context, "sp6-saml2", LASSO_PROVIDER_ROLE_IDP, "idp6-saml2", key) + + sso_initiated_by_sp(idp_context, sp_context); + + /* Cleanup */ + lasso_release_gobject(idp_context); + lasso_release_gobject(sp_context); + lasso_release_gobject(key); +} +END_TEST + Suite* login_saml2_suite() { @@ -910,18 +1007,21 @@ login_saml2_suite() TCase *tc_spSloSoap = tcase_create("Login initiated by service provider without key loading and with SLO SOAP"); TCase *tc_idpKeyRollover = tcase_create("Login initiated by idp, idp use two differents signing keys (simulate key roll-over)"); TCase *tc_spKeyRollover = tcase_create("Login initiated by idp, sp use two differents encrypting keys (simulate key roll-over)"); + TCase *tc_hmacSignature = tcase_create("Login initiated by sp, using shared-key signature"); suite_add_tcase(s, tc_generate); suite_add_tcase(s, tc_spLogin); suite_add_tcase(s, tc_spLoginMemory); suite_add_tcase(s, tc_spSloSoap); suite_add_tcase(s, tc_idpKeyRollover); suite_add_tcase(s, tc_spKeyRollover); + suite_add_tcase(s, tc_hmacSignature); tcase_add_test(tc_generate, test01_saml2_generateServersContextDumps); tcase_add_test(tc_spLogin, test02_saml2_serviceProviderLogin); tcase_add_test(tc_spLoginMemory, test03_saml2_serviceProviderLogin); tcase_add_test(tc_spSloSoap, test04_sso_then_slo_soap); tcase_add_test(tc_idpKeyRollover, test05_sso_idp_with_key_rollover); tcase_add_test(tc_spKeyRollover, test06_sso_sp_with_key_rollover); + tcase_add_test(tc_hmacSignature, test07_sso_sp_with_hmac_sha1_signatures); return s; }