[PATCH] For MS-CHAP, convert the password from UTF-8 to UCS-2.

Evan Broder evan
Wed Oct 19 14:30:46 PDT 2011


Hi -
  I was wondering if anybody had an opportunity to review this patch.

Thanks,
 - Evan

On Fri, Sep 16, 2011 at 7:57 PM, Evan Broder <evan at ebroder.net> wrote:
> The MS-CHAPv1 and MS-CHAPv2 RFCs specify that the password is a string
> of "Unicode characters", which for Windows means UCS-2; thus the
> password could be any even-length string of up to 512 bytes.
>
> Instead of making the incompatible change of requiring the incoming
> password to be UCS-2 encoded, assume the password is UTF-8 encoded and
> convert it before using it in NtPasswordHash and
> EncryptPwBlockWithPasswordHash
>
> Signed-off-by: Evan Broder <evan at ebroder.net>
> ---
> ?src/crypto/ms_funcs.c | ?107 ++++++++++++++++++++++++++++++++++++-------------
> ?1 files changed, 79 insertions(+), 28 deletions(-)
>
> diff --git a/src/crypto/ms_funcs.c b/src/crypto/ms_funcs.c
> index dae15ab..29b0c99 100644
> --- a/src/crypto/ms_funcs.c
> +++ b/src/crypto/ms_funcs.c
> @@ -19,6 +19,52 @@
> ?#include "ms_funcs.h"
> ?#include "crypto.h"
>
> +/**
> + * utf8_to_ucs2
> + * @utf8_string: UTF-8 string (IN)
> + * @utf8_string_len: Length of utf8_string (IN)
> + * @ucs2_buffer: UCS-2 buffer (OUT)
> + * @ucs2_buffer_size: Length of UCS-2 buffer (in 2-byte words) (IN)
> + * @ucs2_string_size: Number of 2-byte words in the resulting UCS-2 string
> + * Returns: 0 on success, -1 on failure
> + */
> +static int utf8_to_ucs2(const u8 *utf8_string, size_t utf8_string_len,
> + ? ? ? ? ? ? ? ? ? ? ? ?u16 *ucs2_buffer, size_t ucs2_buffer_size,
> + ? ? ? ? ? ? ? ? ? ? ? ?size_t *ucs2_string_size)
> +{
> + ? ? ? size_t i, j;
> +
> + ? ? ? for (i = 0, j = 0; i < utf8_string_len; i++) {
> + ? ? ? ? ? ? ? u8 c = utf8_string[i];
> + ? ? ? ? ? ? ? if (j == ucs2_buffer_size) {
> + ? ? ? ? ? ? ? ? ? ? ? /* input too long */
> + ? ? ? ? ? ? ? ? ? ? ? return -1;
> + ? ? ? ? ? ? ? }
> + ? ? ? ? ? ? ? if (c <= 0x7F) {
> + ? ? ? ? ? ? ? ? ? ? ? ucs2_buffer[j++] = host_to_le16(c);
> + ? ? ? ? ? ? ? } else if (i == utf8_string_len - 1 || j == ucs2_buffer_size - 1) {
> + ? ? ? ? ? ? ? ? ? ? ? /* incomplete surrogate */
> + ? ? ? ? ? ? ? ? ? ? ? return -1;
> + ? ? ? ? ? ? ? } else {
> + ? ? ? ? ? ? ? ? ? ? ? u8 c2 = utf8_string[++i];
> + ? ? ? ? ? ? ? ? ? ? ? if ((c & 0xE0) == 0xC0) {
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? /* two-byte encoding */
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ucs2_buffer[j++] = host_to_le16(((c & 0x1F) << 6) | (c2 & 0x3F));
> + ? ? ? ? ? ? ? ? ? ? ? } else if (i == utf8_string_len || j == ucs2_buffer_size - 1) {
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? /* incomplete surrogate */
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? return -1;
> + ? ? ? ? ? ? ? ? ? ? ? } else {
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? /* three-byte encoding */
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? u8 c3 = utf8_string[++i];
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ucs2_buffer[j++] = host_to_le16(((c & 0xF) << 12) | ((c2 & 0x3F) << 6) | (c3 & 0x3F));
> + ? ? ? ? ? ? ? ? ? ? ? }
> + ? ? ? ? ? ? ? }
> + ? ? ? }
> +
> + ? ? ? if (ucs2_string_size)
> + ? ? ? ? ? ? ? *ucs2_string_size = j;
> + ? ? ? return 0;
> +}
>
> ?/**
> ?* challenge_hash - ChallengeHash() - RFC 2759, Sect. 8.2
> @@ -53,7 +99,7 @@ static int challenge_hash(const u8 *peer_challenge, const u8 *auth_challenge,
>
> ?/**
> ?* nt_password_hash - NtPasswordHash() - RFC 2759, Sect. 8.3
> - * @password: 0-to-256-unicode-char Password (IN; ASCII)
> + * @password: 0-to-256-unicode-char Password (IN; UTF-8)
> ?* @password_len: Length of password
> ?* @password_hash: 16-octet PasswordHash (OUT)
> ?* Returns: 0 on success, -1 on failure
> @@ -61,20 +107,18 @@ static int challenge_hash(const u8 *peer_challenge, const u8 *auth_challenge,
> ?int nt_password_hash(const u8 *password, size_t password_len,
> ? ? ? ? ? ? ? ? ? ? ?u8 *password_hash)
> ?{
> - ? ? ? u8 buf[512], *pos;
> - ? ? ? size_t i, len;
> -
> - ? ? ? if (password_len > 256)
> - ? ? ? ? ? ? ? password_len = 256;
> + ? ? ? u16 buf[256];
> + ? ? ? u8 *pos;
> + ? ? ? size_t len, max_len;
>
> - ? ? ? /* Convert password into unicode */
> - ? ? ? for (i = 0; i < password_len; i++) {
> - ? ? ? ? ? ? ? buf[2 * i] = password[i];
> - ? ? ? ? ? ? ? buf[2 * i + 1] = 0;
> - ? ? ? }
> + ? ? ? max_len = sizeof(buf) / sizeof(buf[0]);
> + ? ? ? if (-1 == utf8_to_ucs2(password, password_len,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?buf, max_len,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?&len))
> + ? ? ? ? ? ? ? return -1;
>
> - ? ? ? len = password_len * 2;
> - ? ? ? pos = buf;
> + ? ? ? len *= 2;
> + ? ? ? pos = (u8 *)buf;
> ? ? ? ?return md4_vector(1, (const u8 **) &pos, &len, password_hash);
> ?}
>
> @@ -117,7 +161,7 @@ void challenge_response(const u8 *challenge, const u8 *password_hash,
> ?* @peer_challenge: 16-octet PeerChallenge (IN)
> ?* @username: 0-to-256-char UserName (IN)
> ?* @username_len: Length of username
> - * @password: 0-to-256-unicode-char Password (IN; ASCII)
> + * @password: 0-to-256-unicode-char Password (IN; UTF-8)
> ?* @password_len: Length of password
> ?* @response: 24-octet Response (OUT)
> ?* Returns: 0 on success, -1 on failure
> @@ -225,7 +269,7 @@ int generate_authenticator_response_pwhash(
>
> ?/**
> ?* generate_authenticator_response - GenerateAuthenticatorResponse() - RFC 2759, Sect. 8.7
> - * @password: 0-to-256-unicode-char Password (IN; ASCII)
> + * @password: 0-to-256-unicode-char Password (IN; UTF-8)
> ?* @password_len: Length of password
> ?* @nt_response: 24-octet NT-Response (IN)
> ?* @peer_challenge: 16-octet PeerChallenge (IN)
> @@ -254,7 +298,7 @@ int generate_authenticator_response(const u8 *password, size_t password_len,
> ?/**
> ?* nt_challenge_response - NtChallengeResponse() - RFC 2433, Sect. A.5
> ?* @challenge: 8-octet Challenge (IN)
> - * @password: 0-to-256-unicode-char Password (IN; ASCII)
> + * @password: 0-to-256-unicode-char Password (IN; UTF-8)
> ?* @password_len: Length of password
> ?* @response: 24-octet Response (OUT)
> ?* Returns: 0 on success, -1 on failure
> @@ -375,7 +419,7 @@ int get_asymetric_start_key(const u8 *master_key, u8 *session_key,
>
> ?/**
> ?* encrypt_pw_block_with_password_hash - EncryptPwBlockWithPasswordHash() - RFC 2759, Sect. 8.10
> - * @password: 0-to-256-unicode-char Password (IN; ASCII)
> + * @password: 0-to-256-unicode-char Password (IN; UTF-8)
> ?* @password_len: Length of password
> ?* @password_hash: 16-octet PasswordHash (IN)
> ?* @pw_block: 516-byte PwBlock (OUT)
> @@ -385,18 +429,25 @@ int encrypt_pw_block_with_password_hash(
> ? ? ? ?const u8 *password, size_t password_len,
> ? ? ? ?const u8 *password_hash, u8 *pw_block)
> ?{
> - ? ? ? size_t i, offset;
> + ? ? ? size_t ucs2_len, offset;
> ? ? ? ?u8 *pos;
>
> - ? ? ? if (password_len > 256)
> + ? ? ? os_memset(pw_block, 0, PWBLOCK_LEN);
> +
> + ? ? ? if (-1 == utf8_to_ucs2(password, password_len,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?(u16 *)pw_block, 256,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?&ucs2_len))
> ? ? ? ? ? ? ? ?return -1;
>
> - ? ? ? os_memset(pw_block, 0, PWBLOCK_LEN);
> - ? ? ? offset = (256 - password_len) * 2;
> - ? ? ? if (os_get_random(pw_block, offset) < 0)
> + ? ? ? if (ucs2_len > 256)
> ? ? ? ? ? ? ? ?return -1;
> - ? ? ? for (i = 0; i < password_len; i++)
> - ? ? ? ? ? ? ? pw_block[offset + i * 2] = password[i];
> +
> + ? ? ? offset = (256 - ucs2_len) * 2;
> + ? ? ? if (offset != 0) {
> + ? ? ? ? ? ? ? os_memmove(pw_block + offset, pw_block, ucs2_len * 2);
> + ? ? ? ? ? ? ? if (os_get_random(pw_block, offset) < 0)
> + ? ? ? ? ? ? ? ? ? ? ? return -1;
> + ? ? ? }
> ? ? ? ?/*
> ? ? ? ? * PasswordLength is 4 octets, but since the maximum password length is
> ? ? ? ? * 256, only first two (in little endian byte order) can be non-zero.
> @@ -410,9 +461,9 @@ int encrypt_pw_block_with_password_hash(
>
> ?/**
> ?* new_password_encrypted_with_old_nt_password_hash - NewPasswordEncryptedWithOldNtPasswordHash() - RFC 2759, Sect. 8.9
> - * @new_password: 0-to-256-unicode-char NewPassword (IN; ASCII)
> + * @new_password: 0-to-256-unicode-char NewPassword (IN; UTF-8)
> ?* @new_password_len: Length of new_password
> - * @old_password: 0-to-256-unicode-char OldPassword (IN; ASCII)
> + * @old_password: 0-to-256-unicode-char OldPassword (IN; UTF-8)
> ?* @old_password_len: Length of old_password
> ?* @encrypted_pw_block: 516-octet EncryptedPwBlock (OUT)
> ?* Returns: 0 on success, -1 on failure
> @@ -450,9 +501,9 @@ void nt_password_hash_encrypted_with_block(const u8 *password_hash,
>
> ?/**
> ?* old_nt_password_hash_encrypted_with_new_nt_password_hash - OldNtPasswordHashEncryptedWithNewNtPasswordHash() - RFC 2759, Sect. 8.12
> - * @new_password: 0-to-256-unicode-char NewPassword (IN; ASCII)
> + * @new_password: 0-to-256-unicode-char NewPassword (IN; UTF-8)
> ?* @new_password_len: Length of new_password
> - * @old_password: 0-to-256-unicode-char OldPassword (IN; ASCII)
> + * @old_password: 0-to-256-unicode-char OldPassword (IN; UTF-8)
> ?* @old_password_len: Length of old_password
> ?* @encrypted_password_hash: 16-octet EncryptedPasswordHash (OUT)
> ?* Returns: 0 on success, -1 on failure
> --
> 1.7.4.1
>
>



More information about the Hostap mailing list