]> git.ipfire.org Git - thirdparty/strongswan.git/commitdiff
libradius: refactor generic RADIUS en-/decryption function to a message method
authorMartin Willi <martin@revosec.ch>
Mon, 22 Jul 2013 11:45:31 +0000 (13:45 +0200)
committerMartin Willi <martin@revosec.ch>
Mon, 29 Jul 2013 07:00:48 +0000 (09:00 +0200)
src/libradius/radius_message.c
src/libradius/radius_message.h
src/libradius/radius_socket.c

index e7717ff7a93f8812e4fd07e9886188d8b92e6004..dd3993704be3a913ef310d3614165715b52599d7 100644 (file)
@@ -366,6 +366,67 @@ METHOD(radius_message_t, add, void,
        this->msg->length = htons(ntohs(this->msg->length) + attribute->length);
 }
 
+METHOD(radius_message_t, crypt, bool,
+       private_radius_message_t *this, chunk_t salt, chunk_t in, chunk_t out,
+       chunk_t secret, hasher_t *hasher)
+{
+       char b[HASH_SIZE_MD5];
+
+       /**
+        * From RFC2548 (encryption):
+        * b(1) = MD5(S + R + A)    c(1) = p(1) xor b(1)   C = c(1)
+        * b(2) = MD5(S + c(1))     c(2) = p(2) xor b(2)   C = C + c(2)
+        *      . . .
+        * b(i) = MD5(S + c(i-1))   c(i) = p(i) xor b(i)   C = C + c(i)
+        *
+        * P/C = Plain/Crypted => in/out
+        * S = secret
+        * R = authenticator
+        * A = salt
+        */
+       if (in.len != out.len)
+       {
+               return FALSE;
+       }
+       if (in.len % HASH_SIZE_MD5 || in.len < HASH_SIZE_MD5)
+       {
+               return FALSE;
+       }
+       if (out.ptr != in.ptr)
+       {
+               memcpy(out.ptr, in.ptr, in.len);
+       }
+       /* Preparse seed for first round:
+        * b(1) = MD5(S + R + A) */
+       if (!hasher->get_hash(hasher, secret, NULL) ||
+               !hasher->get_hash(hasher,
+                                                 chunk_from_thing(this->msg->authenticator), NULL) ||
+               !hasher->get_hash(hasher, salt, b))
+       {
+               return FALSE;
+       }
+       while (in.len)
+       {
+               /* p(i) = b(i) xor c(1) */
+               memxor(out.ptr, b, HASH_SIZE_MD5);
+
+               out = chunk_skip(out, HASH_SIZE_MD5);
+               if (out.len)
+               {
+                       /* Prepare seed for next round::
+                        * b(i) = MD5(S + c(i-1)) */
+                       if (!hasher->get_hash(hasher, secret, NULL) ||
+                               !hasher->get_hash(hasher,
+                                                                 chunk_create(in.ptr, HASH_SIZE_MD5), b))
+                       {
+                               return FALSE;
+                       }
+               }
+               in = chunk_skip(in, HASH_SIZE_MD5);
+       }
+       return TRUE;
+}
+
 METHOD(radius_message_t, sign, bool,
        private_radius_message_t *this, u_int8_t *req_auth, chunk_t secret,
        hasher_t *hasher, signer_t *signer, rng_t *rng, bool msg_auth)
@@ -563,6 +624,7 @@ static private_radius_message_t *radius_message_create_empty()
                        .get_encoding = _get_encoding,
                        .sign = _sign,
                        .verify = _verify,
+                       .crypt = _crypt,
                        .destroy = _destroy,
                },
        );
index c4932349052152312fb39e0cbdd8ec953dd56eb3..4ce03a44eb3359856c24d77f603f6a9ae4c27b64 100644 (file)
@@ -284,6 +284,22 @@ struct radius_message_t {
        bool (*verify)(radius_message_t *this, u_int8_t *req_auth, chunk_t secret,
                                   hasher_t *hasher, signer_t *signer);
 
+       /**
+        * Perform RADIUS attribute en-/decryption.
+        *
+        * Performs en-/decryption by XOring the hash-extended secret into data,
+        * as specified in RFC 2865 5.2 and used by RFC 2548.
+        *
+        * @param salt                  salt to append to message authenticator, if any
+        * @param in                    data to en-/decrypt, multiple of HASH_SIZE_MD5
+        * @param out                   en-/decrypted data, length equal to in
+        * @param secret                RADIUS secret
+        * @param hasher                MD5 hasher
+        * @return                              TRUE if en-/decryption successful
+        */
+       bool (*crypt)(radius_message_t *this, chunk_t salt, chunk_t in, chunk_t out,
+                                 chunk_t secret, hasher_t *hasher);
+
        /**
         * Destroy the message.
         */
index 7dab968d8d09eef0e655c60ee62f7fe627c590df..f432151c0075f6565652cae1e7efcef5c35a1e5f 100644 (file)
@@ -233,54 +233,17 @@ METHOD(radius_socket_t, request, radius_message_t*,
 static chunk_t decrypt_mppe_key(private_radius_socket_t *this, u_int16_t salt,
                                                                chunk_t C, radius_message_t *request)
 {
-       chunk_t A, R, P, seed;
-       u_char *c, *p;
+       chunk_t decrypted;
 
-       /**
-        * From RFC2548 (encryption):
-        * b(1) = MD5(S + R + A)    c(1) = p(1) xor b(1)   C = c(1)
-        * b(2) = MD5(S + c(1))     c(2) = p(2) xor b(2)   C = C + c(2)
-        *      . . .
-        * b(i) = MD5(S + c(i-1))   c(i) = p(i) xor b(i)   C = C + c(i)
-        */
-
-       if (C.len % HASH_SIZE_MD5 || C.len < HASH_SIZE_MD5)
-       {
-               return chunk_empty;
-       }
-
-       A = chunk_create((u_char*)&salt, sizeof(salt));
-       R = chunk_create(request->get_authenticator(request), HASH_SIZE_MD5);
-       P = chunk_alloca(C.len);
-       p = P.ptr;
-       c = C.ptr;
-
-       seed = chunk_cata("cc", R, A);
-
-       while (c < C.ptr + C.len)
-       {
-               /* b(i) = MD5(S + c(i-1)) */
-               if (!this->hasher->get_hash(this->hasher, this->secret, NULL) ||
-                       !this->hasher->get_hash(this->hasher, seed, p))
-               {
-                       return chunk_empty;
-               }
-
-               /* p(i) = b(i) xor c(1) */
-               memxor(p, c, HASH_SIZE_MD5);
-
-               /* prepare next round */
-               seed = chunk_create(c, HASH_SIZE_MD5);
-               c += HASH_SIZE_MD5;
-               p += HASH_SIZE_MD5;
-       }
-
-       /* remove truncation, first byte is key length */
-       if (*P.ptr >= P.len)
+       decrypted = chunk_alloca(C.len);
+       if (!request->crypt(request, chunk_from_thing(salt), C, decrypted,
+                                               this->secret, this->hasher) ||
+               decrypted.ptr[0] >= decrypted.len)
        {       /* decryption failed? */
                return chunk_empty;
        }
-       return chunk_clone(chunk_create(P.ptr + 1, *P.ptr));
+       /* remove truncation, first byte is key length */
+       return chunk_clone(chunk_create(decrypted.ptr + 1, decrypted.ptr[0]));
 }
 
 METHOD(radius_socket_t, decrypt_msk, chunk_t,