Alexandre Lision | 7fd5d3d | 2013-12-04 13:06:40 -0500 | [diff] [blame] | 1 | /* |
| 2 | Copyright (C) 2012 Werner Dittmann |
| 3 | |
| 4 | This library is free software; you can redistribute it and/or |
| 5 | modify it under the terms of the GNU Lesser General Public |
| 6 | License as published by the Free Software Foundation; either |
| 7 | version 2.1 of the License, or (at your option) any later version. |
| 8 | |
| 9 | This library is distributed in the hope that it will be useful, |
| 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| 12 | Lesser General Public License for more details. |
| 13 | |
| 14 | You should have received a copy of the GNU Lesser General Public |
| 15 | License along with this library; if not, write to the Free Software |
| 16 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
| 17 | */ |
| 18 | |
| 19 | /* |
| 20 | * @author Werner Dittmann <Werner.Dittmann@t-online.de> |
| 21 | */ |
| 22 | |
| 23 | #include <stdio.h> |
| 24 | #include <stdlib.h> |
| 25 | #include <string.h> |
| 26 | #include <stdint.h> |
| 27 | |
| 28 | #include <common/osSpecifics.h> |
| 29 | |
| 30 | #include <SrtpHandler.h> |
| 31 | #include <CryptoContext.h> |
| 32 | #include <CryptoContextCtrl.h> |
| 33 | |
| 34 | #define RTP_HEADER_LENGTH 12 |
| 35 | |
| 36 | bool SrtpHandler::decodeRtp(uint8_t* buffer, int32_t length, uint32_t *ssrc, uint16_t *seq, uint8_t** payload, int32_t *payloadlen) |
| 37 | { |
| 38 | int offset; |
| 39 | uint16_t *pus; |
| 40 | uint32_t *pui; |
| 41 | |
| 42 | /* Assume RTP header at the start of buffer. */ |
| 43 | |
| 44 | if ((*buffer & 0xC0) != 0x80) { // check version bits |
| 45 | return false; |
| 46 | } |
| 47 | if (length < RTP_HEADER_LENGTH) |
| 48 | return false; |
| 49 | |
| 50 | /* Get some handy pointers */ |
| 51 | pus = (uint16_t*)buffer; |
| 52 | pui = (uint32_t*)buffer; |
| 53 | |
| 54 | uint16_t tmp16 = pus[1]; // get seq number |
| 55 | *seq = zrtpNtohs(tmp16); // and return in host oder |
| 56 | |
| 57 | uint32_t tmp32 = pui[2]; // get SSRC |
| 58 | *ssrc = zrtpNtohl(tmp32); // and return in host order |
| 59 | |
| 60 | /* Payload is located right after header plus CSRC */ |
| 61 | int32_t numCC = buffer[0] & 0x0f; // lower 4 bits in first byte is num of contrib SSRC |
| 62 | offset = RTP_HEADER_LENGTH + (numCC * sizeof(uint32_t)); |
| 63 | |
| 64 | // Sanity check |
| 65 | if (offset > length) |
| 66 | return false; |
| 67 | |
| 68 | /* Adjust payload offset if RTP extension is used. */ |
| 69 | if ((*buffer & 0x10) == 0x10) { // packet contains RTP extension |
| 70 | pus = (uint16_t*)(buffer + offset); // pus points to extension as 16bit pointer |
| 71 | tmp16 = pus[1]; // the second 16 bit word is the length |
| 72 | tmp16 = zrtpNtohs(tmp16); // to host order |
| 73 | offset += (tmp16 + 1) * sizeof(uint32_t); |
| 74 | } |
| 75 | /* Sanity check */ |
| 76 | if (offset > length) |
| 77 | return false; |
| 78 | |
| 79 | /* Set payload and payload length. */ |
| 80 | *payload = buffer + offset; |
| 81 | *payloadlen = length - offset; |
| 82 | |
| 83 | return true; |
| 84 | } |
| 85 | |
| 86 | bool SrtpHandler::protect(CryptoContext* pcc, uint8_t* buffer, size_t length, size_t* newLength) |
| 87 | { |
| 88 | uint8_t* payload = NULL; |
| 89 | int32_t payloadlen = 0; |
| 90 | uint16_t seqnum; |
| 91 | uint32_t ssrc; |
| 92 | |
| 93 | |
| 94 | if (pcc == NULL) { |
| 95 | return false; |
| 96 | } |
| 97 | if (!decodeRtp(buffer, length, &ssrc, &seqnum, &payload, &payloadlen)) |
| 98 | return false; |
| 99 | |
| 100 | /* Encrypt the packet */ |
| 101 | uint64_t index = ((uint64_t)pcc->getRoc() << 16) | (uint64_t)seqnum; |
| 102 | |
| 103 | pcc->srtpEncrypt(buffer, payload, payloadlen, index, ssrc); |
| 104 | |
| 105 | // NO MKI support yet - here we assume MKI is zero. To build in MKI |
| 106 | // take MKI length into account when storing the authentication tag. |
| 107 | |
| 108 | /* Compute MAC and store at end of RTP packet data */ |
| 109 | if (pcc->getTagLength() > 0) { |
| 110 | pcc->srtpAuthenticate(buffer, length, pcc->getRoc(), buffer+length); |
| 111 | } |
| 112 | *newLength = length + pcc->getTagLength(); |
| 113 | |
| 114 | /* Update the ROC if necessary */ |
| 115 | if (seqnum == 0xFFFF ) { |
| 116 | pcc->setRoc(pcc->getRoc() + 1); |
| 117 | } |
| 118 | return true; |
| 119 | } |
| 120 | |
| 121 | int32_t SrtpHandler::unprotect(CryptoContext* pcc, uint8_t* buffer, size_t length, size_t* newLength) |
| 122 | { |
| 123 | uint8_t* payload = NULL; |
| 124 | int32_t payloadlen = 0; |
| 125 | uint16_t seqnum; |
| 126 | uint32_t ssrc; |
| 127 | |
| 128 | if (pcc == NULL) { |
| 129 | return 0; |
| 130 | } |
| 131 | |
| 132 | if (!decodeRtp(buffer, length, &ssrc, &seqnum, &payload, &payloadlen)) |
| 133 | return 0; |
| 134 | /* |
| 135 | * This is the setting of the packet data when we come to this point: |
| 136 | * |
| 137 | * length: complete length of received data |
| 138 | * buffer: points to data as received from network |
| 139 | * payloadlen: length of data excluding hdrSize and padding |
| 140 | * |
| 141 | * Because this is an SRTP packet we need to adjust some values here. |
| 142 | * The SRTP MKI and authentication data is always at the end of a |
| 143 | * packet. Thus compute the position of this data. |
| 144 | */ |
| 145 | uint32_t srtpDataIndex = length - (pcc->getTagLength() + pcc->getMkiLength()); |
| 146 | |
| 147 | // Compute new length |
| 148 | length -= pcc->getTagLength() + pcc->getMkiLength(); |
| 149 | *newLength = length; |
| 150 | |
| 151 | // recompute payloadlen by subtracting SRTP data |
| 152 | payloadlen -= pcc->getTagLength() + pcc->getMkiLength(); |
| 153 | |
| 154 | // MKI is unused, so just skip it |
| 155 | // const uint8* mki = buffer + srtpDataIndex; |
| 156 | uint8_t* tag = buffer + srtpDataIndex + pcc->getMkiLength(); |
| 157 | |
| 158 | /* Replay control */ |
| 159 | if (!pcc->checkReplay(seqnum)) { |
| 160 | return -2; |
| 161 | } |
| 162 | /* Guess the index */ |
| 163 | uint64_t guessedIndex = pcc->guessIndex(seqnum); |
| 164 | |
| 165 | if (pcc->getTagLength() > 0) { |
| 166 | uint32_t guessedRoc = guessedIndex >> 16; |
| 167 | uint8_t mac[20]; |
| 168 | |
| 169 | pcc->srtpAuthenticate(buffer, (uint32_t)length, guessedRoc, mac); |
| 170 | if (memcmp(tag, mac, pcc->getTagLength()) != 0) { |
| 171 | return -1; |
| 172 | } |
| 173 | } |
| 174 | /* Decrypt the content */ |
| 175 | pcc->srtpEncrypt(buffer, payload, payloadlen, guessedIndex, ssrc); |
| 176 | |
| 177 | /* Update the Crypto-context */ |
| 178 | pcc->update(seqnum); |
| 179 | |
| 180 | return 1; |
| 181 | } |
| 182 | |
| 183 | |
| 184 | bool SrtpHandler::protectCtrl(CryptoContextCtrl* pcc, uint8_t* buffer, size_t length, size_t* newLength) |
| 185 | { |
| 186 | |
| 187 | if (pcc == NULL) { |
| 188 | return false; |
| 189 | } |
| 190 | /* Encrypt the packet */ |
| 191 | uint32_t ssrc = *(reinterpret_cast<uint32_t*>(buffer + 4)); // always SSRC of sender |
| 192 | ssrc = zrtpNtohl(ssrc); |
| 193 | |
| 194 | uint32_t encIndex = pcc->getSrtcpIndex(); |
| 195 | pcc->srtcpEncrypt(buffer + 8, length - 8, encIndex, ssrc); |
| 196 | |
| 197 | encIndex |= 0x80000000; // set the E flag |
| 198 | |
| 199 | // Fill SRTCP index as last word |
| 200 | uint32_t* ip = reinterpret_cast<uint32_t*>(buffer+length); |
| 201 | *ip = zrtpHtonl(encIndex); |
| 202 | |
| 203 | // NO MKI support yet - here we assume MKI is zero. To build in MKI |
| 204 | // take MKI length into account when storing the authentication tag. |
| 205 | |
| 206 | // Compute MAC and store in packet after the SRTCP index field |
| 207 | pcc->srtcpAuthenticate(buffer, length, encIndex, buffer + length + sizeof(uint32_t)); |
| 208 | |
| 209 | encIndex++; |
| 210 | encIndex &= ~0x80000000; // clear the E-flag and modulo 2^31 |
| 211 | pcc->setSrtcpIndex(encIndex); |
| 212 | *newLength = length + pcc->getTagLength() + sizeof(uint32_t); |
| 213 | |
| 214 | return true; |
| 215 | } |
| 216 | |
| 217 | int32_t SrtpHandler::unprotectCtrl(CryptoContextCtrl* pcc, uint8_t* buffer, size_t length, size_t* newLength) |
| 218 | { |
| 219 | |
| 220 | if (pcc == NULL) { |
| 221 | return 0; |
| 222 | } |
| 223 | |
| 224 | // Compute the total length of the payload |
| 225 | int32_t payloadLen = length - (pcc->getTagLength() + pcc->getMkiLength() + 4); |
| 226 | *newLength = payloadLen; |
| 227 | |
| 228 | // point to the SRTCP index field just after the real payload |
| 229 | const uint32_t* index = reinterpret_cast<uint32_t*>(buffer + payloadLen); |
| 230 | |
| 231 | uint32_t encIndex = zrtpNtohl(*index); |
| 232 | uint32_t remoteIndex = encIndex & ~0x80000000; // get index without Encryption flag |
| 233 | |
| 234 | if (!pcc->checkReplay(remoteIndex)) { |
| 235 | return -2; |
| 236 | } |
| 237 | |
| 238 | uint8_t mac[20]; |
| 239 | |
| 240 | // Now get a pointer to the authentication tag field |
| 241 | const uint8_t* tag = buffer + (length - pcc->getTagLength()); |
| 242 | |
| 243 | // Authenticate includes the index, but not MKI and not (obviously) the tag itself |
| 244 | pcc->srtcpAuthenticate(buffer, payloadLen, encIndex, mac); |
| 245 | if (memcmp(tag, mac, pcc->getTagLength()) != 0) { |
| 246 | return -1; |
| 247 | } |
| 248 | |
| 249 | uint32_t ssrc = *(reinterpret_cast<uint32_t*>(buffer + 4)); // always SSRC of sender |
| 250 | ssrc = zrtpNtohl(ssrc); |
| 251 | |
| 252 | // Decrypt the content, exclude the very first SRTCP header (fixed, 8 bytes) |
| 253 | if (encIndex & 0x80000000) |
| 254 | pcc->srtcpEncrypt(buffer + 8, payloadLen - 8, remoteIndex, ssrc); |
| 255 | |
| 256 | // Update the Crypto-context |
| 257 | pcc->update(remoteIndex); |
| 258 | |
| 259 | return 1; |
| 260 | } |
| 261 | |