Line data Source code
1 : /* 2 : * Copyright (C) 2004-2024 Savoir-faire Linux Inc. 3 : * 4 : * This program is free software: you can redistribute it and/or modify 5 : * it under the terms of the GNU General Public License as published by 6 : * the Free Software Foundation, either version 3 of the License, or 7 : * (at your option) any later version. 8 : * 9 : * This program 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 12 : * GNU General Public License for more details. 13 : * 14 : * You should have received a copy of the GNU General Public License 15 : * along with this program. If not, see <https://www.gnu.org/licenses/>. 16 : */ 17 : 18 : #include "sdes_negotiator.h" 19 : 20 : #include <iostream> 21 : #include <sstream> 22 : #include <algorithm> 23 : #include <stdexcept> 24 : #include <regex> 25 : 26 : #include <cstdio> 27 : 28 : namespace jami { 29 : 30 : std::vector<CryptoAttribute> 31 782 : SdesNegotiator::parse(const std::vector<std::string>& attributes) 32 : { 33 : // The patterns below try to follow 34 : // the ABNF grammar rules described in 35 : // RFC4568 section 9.2 with the general 36 : // syntax : 37 : // a=crypto:tag 1*WSP crypto-suite 1*WSP key-params *(1*WSP session-param) 38 : 39 : // used to match white space (which are used as separator) 40 : 41 782 : static const std::regex generalSyntaxPattern {"[\x20\x09]+"}; 42 : 43 782 : static const std::regex tagPattern {"^([0-9]{1,9})"}; 44 : 45 : static const std::regex cryptoSuitePattern {"(AES_CM_128_HMAC_SHA1_80|" 46 : "AES_CM_128_HMAC_SHA1_32|" 47 : "F8_128_HMAC_SHA1_80|" 48 782 : "[A-Za-z0-9_]+)"}; // srtp-crypto-suite-ext 49 : 50 : static const std::regex keyParamsPattern {"(inline|[A-Za-z0-9_]+)\\:" 51 : "([A-Za-z0-9\x2B\x2F\x3D]+)" 52 : "((\\|2\\^)([0-9]+)\\|" 53 : "([0-9]+)\\:" 54 782 : "([0-9]{1,3})\\;?)?"}; 55 : 56 : // Take each line from the vector 57 : // and parse its content 58 : 59 782 : std::vector<CryptoAttribute> cryptoAttributeVector; 60 : 61 1564 : for (const auto& item : attributes) { 62 : // Split the line into its component that we will analyze further down. 63 : // Additional white space is added to better split the content 64 : // Result is stored in the sdsLine 65 : 66 782 : std::vector<std::string> sdesLine; 67 782 : std::smatch sm_generalSyntaxPattern; 68 : 69 782 : std::sregex_token_iterator iter(item.begin(), item.end(), generalSyntaxPattern, -1), end; 70 3128 : for (; iter != end; ++iter) 71 2346 : sdesLine.push_back(*iter); 72 : 73 782 : if (sdesLine.size() < 3) { 74 0 : throw ParseError("Missing components in SDES line"); 75 : } 76 : 77 : // Check if the attribute starts with a=crypto 78 : // and get the tag for this line 79 : 80 782 : std::string tag; 81 782 : std::smatch sm_tagPattern; 82 : 83 782 : if (std::regex_search(sdesLine.at(0), sm_tagPattern, tagPattern)) { 84 782 : tag = sm_tagPattern[1]; 85 : 86 : } else { 87 0 : throw ParseError("No Matching Found in Tag Attribute"); 88 : } 89 : 90 : // Check if the crypto suite is valid and retrieve 91 : // its value. 92 : 93 782 : std::string cryptoSuite; 94 782 : std::smatch sm_cryptoSuitePattern; 95 : 96 782 : if (std::regex_search(sdesLine.at(1), sm_cryptoSuitePattern, cryptoSuitePattern)) { 97 782 : cryptoSuite = sm_cryptoSuitePattern[1]; 98 : 99 : } else { 100 0 : throw ParseError("No Matching Found in CryptoSuite Attribute"); 101 : } 102 : 103 : // Parse one or more key-params field. 104 : // Group number is used to locate different paras 105 : 106 782 : std::string srtpKeyInfo; 107 782 : std::string srtpKeyMethod; 108 782 : std::string lifetime; 109 782 : std::string mkiLength; 110 782 : std::string mkiValue; 111 782 : std::smatch sm_keyParamsPattern; 112 : 113 782 : if (std::regex_search(sdesLine.at(2), sm_keyParamsPattern, keyParamsPattern)) { 114 782 : srtpKeyMethod = sm_keyParamsPattern[1]; 115 782 : srtpKeyInfo = sm_keyParamsPattern[2]; 116 782 : lifetime = sm_keyParamsPattern[5]; 117 782 : mkiValue = sm_keyParamsPattern[6]; 118 782 : mkiLength = sm_keyParamsPattern[7]; 119 : 120 : } else { 121 0 : throw ParseError("No Matching Found in Key-params Attribute"); 122 : } 123 : 124 : // Add the new CryptoAttribute to the vector 125 782 : cryptoAttributeVector.emplace_back(std::move(tag), 126 782 : std::move(cryptoSuite), 127 782 : std::move(srtpKeyMethod), 128 782 : std::move(srtpKeyInfo), 129 782 : std::move(lifetime), 130 782 : std::move(mkiValue), 131 782 : std::move(mkiLength)); 132 782 : } 133 782 : return cryptoAttributeVector; 134 0 : } 135 : 136 : CryptoAttribute 137 782 : SdesNegotiator::negotiate(const std::vector<std::string>& attributes) 138 : { 139 : try { 140 782 : auto cryptoAttributeVector(parse(attributes)); 141 782 : for (const auto& iter_offer : cryptoAttributeVector) { 142 782 : for (const auto& iter_local : CryptoSuites) { 143 782 : if (iter_offer.getCryptoSuite() == iter_local.name) 144 782 : return iter_offer; 145 : } 146 : } 147 782 : } catch (const ParseError& exception) { 148 0 : } 149 0 : return {}; 150 : } 151 : 152 : } // namespace jami