static int check (const uint32_t *input, size_t input_length, const uint32_t *expected, size_t expected_length) { size_t length; uint32_t *result; /* Test return conventions with resultbuf == NULL. */ result = u32_normalize (UNINORM_NFKC, input, input_length, NULL, &length); if (!(result != NULL)) return 1; if (!(length == expected_length)) return 2; if (!(u32_cmp (result, expected, expected_length) == 0)) return 3; free (result); /* Test return conventions with resultbuf too small. */ if (expected_length > 0) { uint32_t *preallocated; length = expected_length - 1; preallocated = (uint32_t *) malloc (length * sizeof (uint32_t)); result = u32_normalize (UNINORM_NFKC, input, input_length, preallocated, &length); if (!(result != NULL)) return 4; if (!(result != preallocated)) return 5; if (!(length == expected_length)) return 6; if (!(u32_cmp (result, expected, expected_length) == 0)) return 7; free (result); free (preallocated); } /* Test return conventions with resultbuf large enough. */ { uint32_t *preallocated; length = expected_length; preallocated = (uint32_t *) malloc (length * sizeof (uint32_t)); result = u32_normalize (UNINORM_NFKC, input, input_length, preallocated, &length); if (!(result != NULL)) return 8; if (!(preallocated == NULL || result == preallocated)) return 9; if (!(length == expected_length)) return 10; if (!(u32_cmp (result, expected, expected_length) == 0)) return 11; free (preallocated); } return 0; }
static int check (const uint32_t *c1, size_t c1_length, const uint32_t *c2, size_t c2_length, const uint32_t *c3, size_t c3_length, const uint32_t *c4, size_t c4_length, const uint32_t *c5, size_t c5_length) { /* Check c3 == NFD(c1) == NFD(c2) == NFD(c3) c5 == NFD(c4) == NFD(c5) */ { size_t length; uint32_t *result; result = u32_normalize (UNINORM_NFD, c1, c1_length, NULL, &length); if (!(result != NULL && length == c3_length && u32_cmp (result, c3, c3_length) == 0)) return 1; free (result); } { size_t length; uint32_t *result; result = u32_normalize (UNINORM_NFD, c2, c2_length, NULL, &length); if (!(result != NULL && length == c3_length && u32_cmp (result, c3, c3_length) == 0)) return 2; free (result); } { size_t length; uint32_t *result; result = u32_normalize (UNINORM_NFD, c3, c3_length, NULL, &length); if (!(result != NULL && length == c3_length && u32_cmp (result, c3, c3_length) == 0)) return 3; free (result); } { size_t length; uint32_t *result; result = u32_normalize (UNINORM_NFD, c4, c4_length, NULL, &length); if (!(result != NULL && length == c5_length && u32_cmp (result, c5, c5_length) == 0)) return 4; free (result); } { size_t length; uint32_t *result; result = u32_normalize (UNINORM_NFD, c5, c5_length, NULL, &length); if (!(result != NULL && length == c5_length && u32_cmp (result, c5, c5_length) == 0)) return 5; free (result); } return 0; }
/* Checks whether the provided string is in the valid set of FreeFormClass (RFC7564 * as an RFC7613 requirement), and converts all spaces to the ASCII-space. */ static int check_for_valid_freeformclass(uint32_t *ucs4, unsigned ucs4_size) { unsigned i; int rc; uint32_t tmp[4]; size_t tmp_size; uint32_t *nrm; uc_general_category_t cat; unsigned is_invalid; /* make the union of Valid categories, excluding any invalid (i.e., control) */ cat = uc_general_category_or(UC_CATEGORY_Ll, UC_CATEGORY_Lu); /* LetterDigits */ cat = uc_general_category_or(cat, UC_CATEGORY_Lo); cat = uc_general_category_or(cat, UC_CATEGORY_Nd); cat = uc_general_category_or(cat, UC_CATEGORY_Lm); cat = uc_general_category_or(cat, UC_CATEGORY_Mn); cat = uc_general_category_or(cat, UC_CATEGORY_Mc); cat = uc_general_category_or(cat, UC_CATEGORY_Lt); /* OtherLetterDigits */ cat = uc_general_category_or(cat, UC_CATEGORY_Nl); cat = uc_general_category_or(cat, UC_CATEGORY_No); cat = uc_general_category_or(cat, UC_CATEGORY_Me); cat = uc_general_category_or(cat, UC_CATEGORY_Sm); /* Symbols */ cat = uc_general_category_or(cat, UC_CATEGORY_Sc); cat = uc_general_category_or(cat, UC_CATEGORY_So); cat = uc_general_category_or(cat, UC_CATEGORY_Sk); cat = uc_general_category_or(cat, UC_CATEGORY_Pc); /* Punctuation */ cat = uc_general_category_or(cat, UC_CATEGORY_Pd); cat = uc_general_category_or(cat, UC_CATEGORY_Ps); cat = uc_general_category_or(cat, UC_CATEGORY_Pe); cat = uc_general_category_or(cat, UC_CATEGORY_Pi); cat = uc_general_category_or(cat, UC_CATEGORY_Pf); cat = uc_general_category_or(cat, UC_CATEGORY_Po); cat = uc_general_category_or(cat, UC_CATEGORY_Zs); /* Spaces */ cat = uc_general_category_and_not(cat, UC_CATEGORY_Cc); /* Not in Control */ /* check for being in the allowed sets in rfc7564#section-4.3 */ for (i=0;i<ucs4_size;i++) { is_invalid = 0; /* Disallowed o Old Hangul Jamo characters, i.e., the OldHangulJamo ("I") category [FIXME: not handled in this code] o Control characters, i.e., the Controls ("L") category o Ignorable characters, i.e., the PrecisIgnorableProperties ("M") */ if (uc_is_property_default_ignorable_code_point(ucs4[i]) || uc_is_property_not_a_character(ucs4[i])) { return gnutls_assert_val(GNUTLS_E_INVALID_UTF8_STRING); } /* Contextual rules - we do not implement them / we reject chars from these sets o A number of characters from the Exceptions ("F") category defined o Joining characters, i.e., the JoinControl ("H") category defined */ rc = is_allowed_exception(ucs4[i]); if (rc == 0 || uc_is_property_join_control(ucs4[i])) return gnutls_assert_val(GNUTLS_E_INVALID_UTF8_STRING); if (rc == 1) /* exceptionally allowed, continue */ continue; /* Replace all spaces; an RFC7613 requirement */ if (uc_is_general_category(ucs4[i], UC_CATEGORY_Zs)) /* replace */ ucs4[i] = 0x20; /* Valid */ if ((ucs4[i] < 0x21 || ucs4[i] > 0x7E) && !uc_is_general_category(ucs4[i], cat)) is_invalid = 1; /* HasCompat */ if (is_invalid) { tmp_size = sizeof(tmp)/sizeof(tmp[0]); nrm = u32_normalize(UNINORM_NFKC, &ucs4[i], 1, tmp, &tmp_size); if (nrm == NULL || (tmp_size == 1 && nrm[0] == ucs4[i])) return gnutls_assert_val(GNUTLS_E_INVALID_UTF8_STRING); } } return 0; }
/** * gnutls_utf8_password_normalize: * @password: contain the UTF-8 formatted password * @plen: the length of the provided password * @out: the result in an null-terminated allocated string * @flags: should be zero * * This function will convert the provided UTF-8 password according * to the normalization rules in RFC7613. * * If the flag %GNUTLS_UTF8_IGNORE_ERRS is specified, any UTF-8 encoding * errors will be ignored, and in that case the output will be a copy of the input. * * Returns: %GNUTLS_E_INVALID_UTF8_STRING on invalid UTF-8 data, or 0 on success. * * Since: 3.5.7 **/ int gnutls_utf8_password_normalize(const unsigned char *password, unsigned plen, gnutls_datum_t *out, unsigned flags) { size_t ucs4_size = 0, nrm_size = 0; size_t final_size = 0; uint8_t *final = NULL; uint32_t *ucs4 = NULL; uint32_t *nrm = NULL; uint8_t *nrmu8 = NULL; int ret; if (plen == 0) { out->data = (uint8_t*)gnutls_strdup(""); out->size = 0; if (out->data == NULL) return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); return 0; } /* check for invalid UTF-8 */ if (u8_check((uint8_t*)password, plen) != NULL) { gnutls_assert(); if (flags & GNUTLS_UTF8_IGNORE_ERRS) { raw_copy: out->data = gnutls_malloc(plen+1); if (out->data == NULL) return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); out->size = plen; memcpy(out->data, password, plen); out->data[plen] = 0; return 0; } else { return GNUTLS_E_INVALID_UTF8_STRING; } } /* convert to UTF-32 */ ucs4 = u8_to_u32((uint8_t*)password, plen, NULL, &ucs4_size); if (ucs4 == NULL) { gnutls_assert(); ret = GNUTLS_E_PARSING_ERROR; goto fail; } ret = check_for_valid_freeformclass(ucs4, ucs4_size); if (ret < 0) { gnutls_assert(); if (flags & GNUTLS_UTF8_IGNORE_ERRS) { free(ucs4); goto raw_copy; } if (ret == GNUTLS_E_INVALID_UTF8_STRING) ret = GNUTLS_E_INVALID_PASSWORD_STRING; goto fail; } /* normalize to NFC */ nrm = u32_normalize(UNINORM_NFC, ucs4, ucs4_size, NULL, &nrm_size); if (nrm == NULL) { gnutls_assert(); ret = GNUTLS_E_INVALID_PASSWORD_STRING; goto fail; } /* convert back to UTF-8 */ final_size = 0; nrmu8 = u32_to_u8(nrm, nrm_size, NULL, &final_size); if (nrmu8 == NULL) { gnutls_assert(); ret = GNUTLS_E_INVALID_PASSWORD_STRING; goto fail; } /* copy to output with null terminator */ final = gnutls_malloc(final_size+1);