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 410 : 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 410 : static const std::regex generalSyntaxPattern {"[\x20\x09]+"};
39 :
40 410 : 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 410 : "[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 410 : "([0-9]{1,3})\\;?)?"};
52 :
53 : // Take each line from the vector
54 : // and parse its content
55 :
56 410 : std::vector<CryptoAttribute> cryptoAttributeVector;
57 :
58 820 : 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 410 : std::vector<std::string> sdesLine;
64 410 : std::smatch sm_generalSyntaxPattern;
65 :
66 410 : std::sregex_token_iterator iter(item.begin(), item.end(), generalSyntaxPattern, -1), end;
67 1640 : for (; iter != end; ++iter)
68 1230 : sdesLine.push_back(*iter);
69 :
70 410 : 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 410 : std::string tag;
78 410 : std::smatch sm_tagPattern;
79 :
80 410 : if (std::regex_search(sdesLine.at(0), sm_tagPattern, tagPattern)) {
81 410 : 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 410 : std::string cryptoSuite;
91 410 : std::smatch sm_cryptoSuitePattern;
92 :
93 410 : if (std::regex_search(sdesLine.at(1), sm_cryptoSuitePattern, cryptoSuitePattern)) {
94 410 : 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 410 : std::string srtpKeyInfo;
104 410 : std::string srtpKeyMethod;
105 410 : std::string lifetime;
106 410 : std::string mkiLength;
107 410 : std::string mkiValue;
108 410 : std::smatch sm_keyParamsPattern;
109 :
110 410 : if (std::regex_search(sdesLine.at(2), sm_keyParamsPattern, keyParamsPattern)) {
111 410 : srtpKeyMethod = sm_keyParamsPattern[1];
112 410 : srtpKeyInfo = sm_keyParamsPattern[2];
113 410 : lifetime = sm_keyParamsPattern[5];
114 410 : mkiValue = sm_keyParamsPattern[6];
115 410 : 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 410 : cryptoAttributeVector.emplace_back(std::move(tag),
123 410 : std::move(cryptoSuite),
124 410 : std::move(srtpKeyMethod),
125 410 : std::move(srtpKeyInfo),
126 410 : std::move(lifetime),
127 410 : std::move(mkiValue),
128 410 : std::move(mkiLength));
129 410 : }
130 410 : return cryptoAttributeVector;
131 0 : }
132 :
133 : CryptoAttribute
134 410 : SdesNegotiator::negotiate(const std::vector<std::string>& attributes)
135 : {
136 : try {
137 410 : auto cryptoAttributeVector(parse(attributes));
138 410 : for (const auto& iter_offer : cryptoAttributeVector) {
139 410 : for (const auto& iter_local : CryptoSuites) {
140 410 : if (iter_offer.getCryptoSuite() == iter_local.name)
141 410 : return iter_offer;
142 : }
143 : }
144 410 : } catch (const ParseError& exception) {
145 0 : JAMI_WARNING("");
146 0 : }
147 0 : return {};
148 : }
149 :
150 : } // namespace jami
|