[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

technical clarification request to RFC-1828.



The last thing I feel like doing is stepping into the war over 1828, however I
do have a technical concern with regard to the specification of a compliant
implementation.  I apologise for missing the last call on this document, at
the time I was not involved in this area of standards.  This is purely a
technical clarification, I make no judgements about the appropriateness of the
protocol.

My issue is entirely based upon what I consider to be an inadequate
specification of the padding of the initial key.  To my "trained" implementors
eye, it's not at all clear from the RFC, and in fact, I had to speak to
someone who implemented it correctly at the bake-off who received additional
information from Bill.  (read: I looked at the NRL code)

The RFC does not mis-specify the method of padding, merely it specifies it
inadequately.  Given that with the additional information, the compliant
implementation became obvious, I would request the that the working group
insure the clarity of future revisions of 1828.

I think some simple pseudo-code would be enough, as long as the "revision" to
the RFC1323 MD5Final routine was *strongly* noted.

Given that the group seems to be in a war about 1828 and other recently
released drafts,  now is as good a time as any to nail down some technical
details.

Sigh,

Paul

-----------------------------------------------------------------------------

Details:

My inadequate interpretation of 1828 caused the following code to be written
(the MD5 routines are the canonical ones from 1323):

/*
 * md5_rfc1828
 *
 * User-friendly way to compute keyed MD5 authentication information for
 * a chunk of data.
 *
 * Call it like this:
 *
 * char hash[MD5_LEN];
 * md5_rfc1828(message, msglength, key, strlen(key), hash, sizeof(hash));
 */

#define	FILL_LEN 64	/* 512 bit boundaries for fill */
#define	MOD_LEN	 56	/* pad key to bit 448 modulo 512 */

boolean
md5_rfc1828 (void *data,    int datalen,
	     char *key,     int keylen,
	     uchar *digest, int digestlen)
{
	static uchar padding[FILL_LEN] = { 0x80, 0 };

	MD5_CTX context;
	int padlen;

	if (digestlen < MD5_LEN)
	    return FALSE;

	padlen = keylen % FILL_LEN;
	padlen = padlen < MOD_LEN ? MOD_LEN - padlen
				  : (FILL_LEN+MOD_LEN) - padlen;

	MD5Init(&context);
	MD5Update(&context, key, keylen);
	MD5Update(&context, padding, padlen);
	MD5Update(&context, data, datalen);
	MD5Update(&context, key, keylen);
	MD5Final(digest, &context);

	return TRUE;
}

whereas the correct implementation would be:

boolean
md5_rfc1828 (void *data,    int datalen,
	     char *key,     int keylen,
	     uchar *digest, int digestlen)
{
	MD5_CTX context;

	if (digestlen < MD5_LEN)
	    return FALSE;

	MD5Init(&context);
	MD5Update(&context, key, keylen);
	RFC_1828_MD5Final(NULL, &context); /* do padding according to 1828 */

	MD5Update(&context, data, datalen);
	MD5Update(&context, key, keylen);
	MD5Final(digest, &context);

	return TRUE;
}

void
RFC_1828_MD5Final (unsigned char *digest,	/* message digest */
		   MD5_CTX *context)		/* context */
{
    unsigned char   bits[8];
    unsigned int    index, padLen;

    /* Save number of bits */
    Encode(bits, context->count, 8);

    /*
     * Pad out to 56 mod 64.
     */
    index = (unsigned int) ((context->count[0] >> 3) & 0x3f);
    padLen = (index < 56) ? (56 - index) : (120 - index);
    MD5Update(context, PADDING, padLen);

    /* Append length (before padding) */
    MD5Update(context, bits, 8);

|   if (digest) {		/* change to allow RFC1828 padding */
|	/* Store state in digest */
|	Encode(digest, context->state, 16);
|
|	/*
|	 * Zeroize sensitive information.
|	 */
|	MD5_memset((POINTER) context, 0, sizeof(*context));
|   }

}


Follow-Ups: