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