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