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