Skip to content
Snippets Groups Projects
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
RADIUS_EncDec.cc 9.48 KiB
/******************************************************************************
* Copyright (c) 2008, 2015  Ericsson AB
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Timea Moder
* Endre Kulcsar
* Gabor Szalai
* Janos Kovesdi
* Kulcsr Endre
* Zoltan Medve
* Tamas Korosi
******************************************************************************/

//
//  File:               RADIUS_EncDec.cc
//  Description:        Encoder/Decoder and external functions for RPMG
//  Rev:                R12A
//  Prodnr:             CNL 113 600
//  Reference:          RFC 2865(RADIUS), 2866(RADIUS Accounting),        
//                       
//                      
//

#include "RADIUS_Types.hh"

#include <openssl/md5.h>

namespace RADIUS__Types{


// calculates 16 bit MD5 message digest
OCTETSTRING f__calc__MD5(const OCTETSTRING& input)
   {  
      unsigned char output[16];     
      MD5(input,(size_t) input.lengthof(),output);  //error check!   
      OCTETSTRING MD5_Value(16,output);  
            
      return MD5_Value;   
   }
   
// copied from Radius Test Port  ( secret CHARSTRING -> OCTETSTRING
OCTETSTRING f__crypt__password (const OCTETSTRING& P, const OCTETSTRING& req__auth, const OCTETSTRING& salt,const BOOLEAN& decrypt, const CHARSTRING& secret) {

  const unsigned char* P_p = (const unsigned char*)P;
  int P_num = P.lengthof() / 16;
  
  if (P.lengthof() % 16 != 0)
    TTCN_warning("Length of P should be multiple of 16");
  
  unsigned char b[16];

  const OCTETSTRING& SRA = char2oct(secret) + req__auth + salt;

  TTCN_Logger::begin_event(TTCN_DEBUG);
  TTCN_Logger::log_event("SRA: ");
  SRA.log();
  TTCN_Logger::end_event();
  
  MD5((const unsigned char*)SRA, SRA.lengthof(), b);
  
  unsigned char* C = new unsigned char [P_num * 16]; // output buffer
  
  for (int j = 0; j < 16; j++) {
    C[j] = P_p[j] ^ b[j];
  }
  
  unsigned int S_len = secret.lengthof();
  
  unsigned char* Sc = new unsigned char[S_len + 16];
  memcpy(Sc, (const unsigned char*)(const char*)secret, S_len);
  
  for (int i = 1; i < P_num; i++) {
    if (decrypt)
      memcpy(Sc + S_len, P_p + (i-1)*16, 16);
    else
      memcpy(Sc + S_len, C + (i-1)*16, 16);

    MD5(Sc, S_len + 16, b);
    for (int j = 0; j < 16; j++) {
      C[(i*16 + j)] = P_p[(i*16 +j)] ^ b[j];
    }
  }

  OCTETSTRING result = OCTETSTRING(P_num*16, (const unsigned char*)C);
  delete [] C;
  delete [] Sc; 

  TTCN_Logger::begin_event(TTCN_DEBUG);
  TTCN_Logger::log_event("Result of hashing: ");
  result.log();
  TTCN_Logger::end_event();

  return result;
  
} // crypt_password 

OCTETSTRING f__crypt__s__key(const OCTETSTRING& pl_s_key, const OCTETSTRING& pl_req_auth, const CHARSTRING& secret,const BOOLEAN& decrypt)
{
  if (decrypt)
  {
    const OCTETSTRING& salt = substr(pl_s_key, 0, 2);
    const OCTETSTRING& decrypted = f__crypt__password(substr(pl_s_key, 2, pl_s_key.lengthof() - 2),pl_req_auth, salt, true, secret);
    int key_len = *((const unsigned char*)decrypted); // first byte
    if (key_len > decrypted.lengthof() - 1) {
      TTCN_warning("Invalid key length in \'S\' key.");
      key_len = decrypted.lengthof() - 1;
    }
    const OCTETSTRING& key_length_and_key = substr(decrypted, 0, key_len + 1);
    OCTETSTRING result = salt + key_length_and_key;
    return result;
  } // decrypt_s_key 
  else
  { 
    if (pl_s_key.lengthof() < 3)
      TTCN_warning("string_val in \'S\' key must be at least 3 octets long.");
    const OCTETSTRING& salt = OCTETSTRING(2, (const unsigned char*)pl_s_key);
    const int key_len = *((const unsigned char*)pl_s_key + 2);
    const OCTETSTRING& key = OCTETSTRING(pl_s_key.lengthof() - 3, (const unsigned char*)pl_s_key + 3);
    int calc_key_len;
    if (key_len == 0)
      calc_key_len = key.lengthof();
    else {
      if (key_len != key.lengthof())
        TTCN_warning("Invalid key length in \'S\' key.");
      calc_key_len = key_len;
    }
    int padding_len = (16 - ((key.lengthof() + 1) % 16)) % 16; // +1 for the key length
    const OCTETSTRING& P = int2oct(calc_key_len, 1) + key + OCTETSTRING(padding_len, (const unsigned char*)"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0");
    OCTETSTRING result = salt + f__crypt__password(P, pl_req_auth, salt, false, secret);
    return result;
  }
}  // crypt_s_key

OCTETSTRING f__crypt__tunnel__password(const OCTETSTRING& pl_password, const OCTETSTRING& req_auth, const OCTETSTRING& salt, const CHARSTRING& secret,const BOOLEAN& decrypt) {
  if (decrypt)
  {
    const OCTETSTRING& plain = f__crypt__password(pl_password, req_auth, salt, true, secret);
    OCTETSTRING password;
    password = OCTETSTRING(plain.lengthof(), (const unsigned char*)plain + 1);
    return  password;
  }
  else
  {
    int data_len=pl_password.lengthof();
    // the following line pads P to be multiple of 16 octets
    const OCTETSTRING& P = int2oct(data_len, 1) + pl_password + OCTETSTRING(
      (16-((pl_password.lengthof() + 1) % 16)) % 16, (const unsigned char*)"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0");
    return f__crypt__password(P, req_auth, salt, false, secret);
  }
} // encrypt_tunnel_password

   
OCTETSTRING f__RADIUS__Enc(const PDU__RADIUS& pdu)
{
  PDU__RADIUS* par=NULL;
    
  TTCN_Buffer buf;
  TTCN_EncDec::error_type_t err;
  buf.clear();
  TTCN_EncDec::clear_error();
  TTCN_EncDec::set_error_behavior(TTCN_EncDec::ET_ALL, TTCN_EncDec::EB_WARNING);
  if(par)
    par->encode(PDU__RADIUS_descr_, buf, TTCN_EncDec::CT_RAW);
  else 
    pdu.encode(PDU__RADIUS_descr_, buf, TTCN_EncDec::CT_RAW);
  err = TTCN_EncDec::get_last_error_type();
  if(err != TTCN_EncDec::ET_NONE)
    TTCN_warning("Encoding error: %s\n", TTCN_EncDec::get_error_str());
  delete par;
  return OCTETSTRING(buf.get_len(), buf.get_data());
}

PDU__RADIUS f__RADIUS__Dec(const OCTETSTRING& stream) 
{
  PDU__RADIUS pdu;
  TTCN_Buffer buf;
  TTCN_EncDec::error_type_t err;
  TTCN_EncDec::clear_error();
  buf.clear();
  buf.put_os(stream);
  TTCN_EncDec::set_error_behavior(TTCN_EncDec::ET_ALL, TTCN_EncDec::EB_WARNING);
  pdu.decode(PDU__RADIUS_descr_, buf, TTCN_EncDec::CT_RAW);
  err = TTCN_EncDec::get_last_error_type();
  if(err != TTCN_EncDec::ET_NONE)
    TTCN_warning("Decoding error: %s\n", TTCN_EncDec::get_error_str());
  return pdu;
}

BOOLEAN f__salt__value(vendor__specific__value&  pdu, const OCTETSTRING&  req_auth, const CHARSTRING&  secret, const BOOLEAN& decrypt){
  OCTETSTRING salt;
  OCTETSTRING key;
  switch(pdu.get_selection()){
    case vendor__specific__value::ALT_unsalted__integer:
    {
      salt = pdu.unsalted__integer().salt();
      key = int2oct(pdu.unsalted__integer().unsalted__value(),4); 
      break;
    }
    case vendor__specific__value::ALT_unsalted__text:
    {
      salt = pdu.unsalted__text().salt();
      key = char2oct(pdu.unsalted__text().unsalted__value()); 
      break;
    }
    case vendor__specific__value::ALT_unsalted__string:
    {
      salt = pdu.unsalted__string().salt();
      key = pdu.unsalted__string().unsalted__value(); 
      break;
    }
    case vendor__specific__value::ALT_string__val:
    {
      salt =OCTETSTRING(0, (const unsigned char*)"\0");
      key = pdu.string__val(); 
      break;
    }
    default:
      return false;
  }
  OCTETSTRING string_val;
  if(decrypt){
	string_val = f__crypt__password (key,req_auth , salt, decrypt, secret);
    int key_len = *((const unsigned char*)string_val); // first byte

    if (key_len > string_val.lengthof() - 1) {
      TTCN_warning("Invalid key length");
      key_len = string_val.lengthof() - 1;
    }
    string_val = substr(string_val, 1, key_len);
  } else {
	  const int key_len = key.lengthof();
	  int padding_len = (16 - ((key_len + 1) % 16)) % 16; // +1 for the key length
	  const OCTETSTRING& P = int2oct(key.lengthof(), 1) + key + OCTETSTRING(padding_len, (const unsigned char*)"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0");
	  string_val = f__crypt__password (P,req_auth , salt, decrypt, secret);

  }
      
  if(salt.lengthof()!=0){
    pdu.unsalted__string().salt()=salt;
    pdu.unsalted__string().unsalted__value()=string_val;
  } else {
    pdu.string__val()=string_val;
  }
  return true;
}

bool f_convert_string_to_text(const OCTETSTRING& in, CHARSTRING& out){
  const unsigned char * key_ptr = (const unsigned char *)in;
  for (int i = 0; i<(in.lengthof());i++){
    if (key_ptr[i] & 0x80) {
      return false;
    }
  }
  out = oct2char(in);
  return true;
  
}

BOOLEAN f__convert__string__to__text(vendor__specific__value& pdu){
  CHARSTRING chr;
  switch(pdu.get_selection()){
    case vendor__specific__value::ALT_string__val:
    {
      if(f_convert_string_to_text( pdu.string__val(),chr)){
         pdu.text__val()=chr;
      } else { return false; }
      break;
    }
    case vendor__specific__value::ALT_unsalted__string:
    {
      if(f_convert_string_to_text( pdu.unsalted__string().unsalted__value(),chr)){
         OCTETSTRING salt=pdu.unsalted__string().salt();
         pdu.unsalted__text().unsalted__value()=chr;
         pdu.unsalted__text().salt()=salt;
      } else { return false; }
      break;
    }
    case vendor__specific__value::ALT_tagged__string:
    {
      if(f_convert_string_to_text( pdu.tagged__string().untagged__value(),chr)){
         OCTETSTRING tag=pdu.tagged__string().tag();
         pdu.tagged__text().untagged__value()=chr;
         pdu.tagged__text().tag()=tag;
      } else { return false; }
      break;
    }
    default:
      return false;
  }
  return true;
}


}
TTCN_Module RADIUS_EncDec("RADIUS_EncDec", __DATE__, __TIME__);