blob: 2cef37e5516e83c8c5d2164d39f6cc71966b367e [file] [log] [blame]
///////////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2000-2018 Ericsson Telecom AB
//
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v2.0
// which accompanies this distribution, and is available at
// https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.html
///////////////////////////////////////////////////////////////////////////////
//
// File: STUN_EncDec.cc
// Description: Encoder/Decoder functions for STUN Protocol module
// Rev: R2B
// Prodnr: CNL 113 778
// Updated: 2013-07-30
// Contact: http://ttcn.ericsson.se
// Reference:
//
//
#include "STUN_Types.hh"
#include <openssl/hmac.h>
#include <openssl/rand.h>
#include <zlib.h>
const int MESSAGE_INTEGRITY_LENGTH = 24;
const int MESSAGE_INTEGRITY_VALUE_LENGTH = 20;
const int FINGERPRINT_LENGTH = 8;
const int FINGERPRINT_VALUE_LENGTH = 4;
const int STUN_MESSAGE_HEADER_LENGTH = 20;
using namespace General__Types;
namespace STUN__Types{
static const unsigned char EMPTY_OCTETS_16[] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
static const unsigned char EMPTY_OCTETS_20[] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
static const OCTETSTRING OCT16_EMPTY(16, EMPTY_OCTETS_16);
static const OCTETSTRING OCT20_EMPTY(20, EMPTY_OCTETS_20);
OCTETSTRING get_HMAC(const unsigned char* pl_stream, const int& pl_stream_length,
const unsigned char* pl_key, const int& pl_key_length) {
if (pl_stream_length > 0) {
unsigned char md[EVP_MAX_MD_SIZE];
unsigned int md_len;
HMAC(EVP_sha1(), pl_key, pl_key_length, pl_stream, pl_stream_length, md, &md_len);
OCTETSTRING result(md_len, md);
return result;
}
TTCN_warning("Message length too short");
return OCT20_EMPTY;
} //get_HMAC
void insert_Fingerprint(TTCN_Buffer& pl_buffer) {
const unsigned char xor_value[] = {'\x53', '\x54', '\x55', '\x4E'};
unsigned char crc_stream[4];
unsigned char attribute_head[4];
//calculate the fingerprint attribute
uLong crc = crc32(0L, pl_buffer.get_data(), pl_buffer.get_len());
//insert the attribute type and length
attribute_head[0] = (STUN__Attribute__Type::FINGERPRINT >> 8) & 0xFF;
attribute_head[1] = (STUN__Attribute__Type::FINGERPRINT & 0xFF);
attribute_head[2] = (4 >> 8) & 0xFF;
attribute_head[3] = (4 & 0xFF);
pl_buffer.put_s(4, attribute_head);
//insert the attribute value
crc_stream[0] = ((crc >> 24) & 0xFF) xor xor_value[0];
crc_stream[1] = ((crc >> 16) & 0xFF) xor xor_value[1];
crc_stream[2] = ((crc >> 8) & 0xFF) xor xor_value[2];
crc_stream[3] = (crc & 0xFF) xor xor_value[3];
pl_buffer.put_s(4, crc_stream);
} //insert_Fingerprint
void insert_HMAC(TTCN_Buffer& pl_buffer, const unsigned char* pl_key, const int& pl_key_length){
unsigned char attribute_head[4];
attribute_head[0] = (STUN__Attribute__Type::MESSAGE__INTEGRITY >> 8) & 0xFF;
attribute_head[1] = (STUN__Attribute__Type::MESSAGE__INTEGRITY & 0xFF);
attribute_head[2] = (20 >> 8) & 0xFF;
attribute_head[3] = (20 & 0xFF);
//pl_stream does not contain the message integrity attribute at all(24 bytes)
OCTETSTRING hmac = get_HMAC(pl_buffer.get_data(), pl_buffer.get_len(), pl_key, pl_key_length);
pl_buffer.put_s(4, attribute_head);
pl_buffer.put_os(hmac);
} //insert_HMAC
void change_Message_Length(TTCN_Buffer& pl_buffer, const unsigned int& new_message_length) {
unsigned char* buffer_data = NULL;
size_t ss = 1;
pl_buffer.get_end(buffer_data, ss);
*(buffer_data-pl_buffer.get_len()+2) = (new_message_length >> 8) & 0xFF;
*(buffer_data-pl_buffer.get_len()+3) = (new_message_length & 0xFF);
}// change_Message_Length
BOOLEAN ef__STUN__Check__Message__Integrity(const OCTETSTRING& pl__stream,
const OCT16& pl__key,
const STUN__Attribute__Type& pl__last__attribute__type) {
int offset = -1;
switch(pl__last__attribute__type) {
case STUN__Attribute__Type::MESSAGE__INTEGRITY:
offset = pl__stream.lengthof() - MESSAGE_INTEGRITY_LENGTH;
break;
case STUN__Attribute__Type::FINGERPRINT:
offset = pl__stream.lengthof() - FINGERPRINT_LENGTH - MESSAGE_INTEGRITY_LENGTH;
break;
default:
TTCN_warning("Last attribute is not Message Integrity");
return true;
};
if(offset < 0){
TTCN_error("Negative offset.");
}
//offset+1 == attribute type
if(((const unsigned char*)pl__stream)[offset+1] == STUN__Attribute__Type::MESSAGE__INTEGRITY) {
OCTETSTRING stream_MAC = substr(pl__stream, offset+4, MESSAGE_INTEGRITY_VALUE_LENGTH);
TTCN_Buffer buffer(substr(pl__stream, 0, offset));
change_Message_Length(buffer, (offset+MESSAGE_INTEGRITY_LENGTH-STUN_MESSAGE_HEADER_LENGTH));
OCTETSTRING calculated_MAC = get_HMAC(buffer.get_data(),
buffer.get_len(),
(const unsigned char*)pl__key,
pl__key.lengthof());
return stream_MAC == calculated_MAC;
}
TTCN_warning("No message integrity specified.");
return false;
} //ef__STUN__Check__Message__Integrity
OCT12 ef__STUN__Generate__Transaction__Id() {
const int transaction_id_length = 12;
unsigned char id[transaction_id_length];
if(RAND_pseudo_bytes(id, transaction_id_length) == -1){
TTCN_error("Not supported by the current RAND method.");
}
return OCTETSTRING(transaction_id_length, id);
} //ef__STUN__Generate_Trasaction_Id
INTEGER ef__STUN__get__Message__Length(const OCTETSTRING& pl__stream) {
int pdu_length = -1;
if (pl__stream.lengthof() >= 4){
const unsigned char* stream_pointer = (const unsigned char*)pl__stream;
pdu_length= (stream_pointer[3] << 8) + stream_pointer[2];
}
return pdu_length;
} //ef__STUN__get_Message_Length
void ef__STUN__enc(const PDU__STUN& pl__pdu,
const OCT16& pl__key,
OCTETSTRING& pl__result,
const BOOLEAN& pl__calculate__HMAC__CRC) {
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);
pl__pdu.encode(PDU__STUN_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());
}
if(pl__calculate__HMAC__CRC) {
const STUN__Attributes attributes = pl__pdu.STUN__attributes();
int buffer_position = 0;
int offset = 0;
bool insert_hmac = false;
bool insert_fingerprint = false;
bool message_length_changed = false;
if(attributes.lengthof() == 0){
pl__result = OCTETSTRING(buf.get_len(), buf.get_data());
return;
}
switch(attributes[attributes.lengthof()-1].attribute__type()){
case STUN__Attribute__Type::MESSAGE__INTEGRITY:
insert_hmac = true;
buffer_position = buf.get_len()-MESSAGE_INTEGRITY_LENGTH;
break;
case STUN__Attribute__Type::FINGERPRINT:
insert_fingerprint = true;
offset = buf.get_len() - FINGERPRINT_LENGTH;
if(attributes.lengthof() >= 2 && attributes[attributes.lengthof()-2].attribute__type() == STUN__Attribute__Type::MESSAGE__INTEGRITY){
insert_hmac = true;
change_Message_Length(buf, (offset-STUN_MESSAGE_HEADER_LENGTH));
offset -= MESSAGE_INTEGRITY_LENGTH;
message_length_changed = true;
}
buffer_position = offset;
break;
default:
TTCN_warning("When present, the FINGERPRINT attribute MUST be the last attribute in the message, and thus will appear after MESSAGE-INTEGRITY.");
};
buf.set_pos(buffer_position);
buf.cut_end();
if(insert_hmac){
insert_HMAC(buf, (const unsigned char*)pl__key, pl__key.lengthof());
}
if(insert_fingerprint){
if(message_length_changed) {
change_Message_Length(buf, buf.get_len()-STUN_MESSAGE_HEADER_LENGTH+FINGERPRINT_LENGTH);
}
insert_Fingerprint(buf);
}
}//if calculate hmac and crc
pl__result = OCTETSTRING(buf.get_len(), buf.get_data());
}//ef__STUN__enc
}//namespace STUN__Types
TTCN_Module STUN_EncDec("STUN_EncDec", __DATE__, __TIME__);