├── .gitignore ├── LICENSE.txt ├── README.md ├── build-final.sh ├── build-tools └── Dockerfile ├── cli.sh └── patches ├── .gitdir ├── disable-dtmf-and-comfort-noise.patch ├── disable-unused-audio-codecs.patch ├── dont-leak-video-orientation.patch ├── dtls-cipher-suites.patch ├── enable-cbr-by-default.patch ├── expose-certificate-fingerprint.patch ├── expose-crypto-option-aes-128-sha1-80.patch ├── expose-video-capturer-state.patch ├── group-call-frame-crypto.patch ├── only-resolve-uuid-mdns-hostnames.patch └── srtp-cipher-suites.patch /.gitignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | .vscode/ 3 | out/ 4 | out-*/ 5 | webrtc/ 6 | /.vscode/ 7 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | Copyright (c) 2016-2025 Threema GmbH 3 | 4 | Permission is hereby granted, free of charge, to any person 5 | obtaining a copy of this software and associated documentation files 6 | (the "Software"), to deal in the Software without restriction, 7 | including without limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of the Software, 9 | and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be 13 | included in all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 19 | BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 20 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 21 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # libwebrtc Build Script 2 | 3 | This is a Dockerfile to build libwebrtc for Android using the new GN based 4 | build system. 5 | 6 | **NOTE: We do not provide any support related to building special versions, or 7 | related to issues with your Docker installation, or with regard to bugs in the 8 | WebRTC codebase itself. We also do not provide any support on how to integrate 9 | the resulting build into your application.** 10 | 11 | ## TL;DR 12 | 13 | For an initial local build: 14 | 15 | ./cli.sh build-tools 16 | ./cli.sh fetch 17 | ./cli.sh patch 18 | ./cli.sh build-all 19 | 20 | For subsequent builds after an update: 21 | 22 | ./cli.sh update 23 | ./cli.sh patch 24 | ./cli.sh build-all 25 | 26 | For a (somewhat) reproducible build, created from within a temporary Docker container: 27 | 28 | ./cli.sh build-tools 29 | ./build-final.sh 30 | 31 | ## Usage: cli.sh 32 | 33 | First, build the tools image: 34 | 35 | ./cli.sh build-tools 36 | 37 | This will download and install necessary tools to work with the libwebrtc code 38 | base. 39 | 40 | Then, fetch the libwebrtc code into the `webrtc` directory. This will download 41 | ~24 GiB and may take a while. 42 | 43 | ./cli.sh fetch 44 | 45 | Optionally switch to a specific (release) branch: 46 | 47 | cd webrtc/src 48 | git checkout branch-heads/ 49 | cd - 50 | 51 | You can find the corresponding branch head revisions for libwebrtc releases at 52 | https://chromiumdash.appspot.com/branches 53 | 54 | If it has been a while since you fetched the code, you may update the code as 55 | such: 56 | 57 | ./cli.sh update 58 | 59 | This will work on any branch but obviously may not switch to the most recent 60 | code revision (e.g. if on a release branch). When in detached head state, this 61 | will automatically check out the HEAD of the main branch. 62 | 63 | If you just want to sync libwebrtc source against the current commit/branch 64 | you've checked out, run: 65 | 66 | ./cli.sh sync 67 | 68 | This is particularly useful when in detached head state. 69 | 70 | As an optional step, apply our patches: 71 | 72 | ./cli.sh patch 73 | 74 | To create a build for all targets, run: 75 | 76 | ./cli.sh build-all 77 | 78 | This will take probably around half an hour on a modern computer. Once the 79 | script finished, you'll get the following output in the `out/` directory: 80 | 81 | - `libwebrtc.jar` 82 | - `arm/libjingle_peerconnection_so.so` 83 | - `x86/libjingle_peerconnection_so.so` 84 | - `arm64/libjingle_peerconnection_so.so` 85 | - `x64/libjingle_peerconnection_so.so` 86 | - `revision.txt` 87 | - `patches.txt` (may not exist if no patch has been applied) 88 | - `build_args.txt` 89 | 90 | It is also possible to just jump into the build image shell which allows to 91 | customise the build steps entirely: 92 | 93 | ./cli.sh run 94 | 95 | If you haven't updated for a longer period, it might happen that the build 96 | tools need updating or that the code needs to be fetched again. To clean and 97 | start from scratch, run: 98 | 99 | ./cli.sh clean 100 | 101 | ## Usage: build-final.sh 102 | 103 | To generate a (somewhat) reproducible build, without any caching and with the 104 | whole process being done from within a temporary, deterministic Docker 105 | container: 106 | 107 | ./cli.sh build-tools 108 | ./build-final.sh 109 | 110 | This guarantees the absence of a cache (because it always fetches fresh code), 111 | consistent permissions and filesystem paths (so that your username and workdir 112 | isn't included in the binary's debug info) and will ensure that you don't 113 | forget to apply patches (because it always applies all patches at 114 | `patches/*.patch`). 115 | 116 | 117 | ## Patches 118 | 119 | Patches should be created using `git diff` inside the webrtc/src directory and 120 | stored in the /patches directory to be applied automatically when running 121 | `./cli.sh patch`. 122 | 123 | $ git diff > ../../patches/my-changes.patch 124 | -------------------------------------------------------------------------------- /build-final.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -euo pipefail 3 | 4 | IMAGE=threema/webrtc-build-tools:latest 5 | TARGETS="${WEBRTC_TARGETS:-arm arm64 x86 x64}" 6 | BUILD_ARGS="${WEBRTC_BUILD_ARGS:-symbol_level=1 debuggable_apks=false enable_libaom=false rtc_enable_protobuf=false rtc_include_dav1d_in_internal_decoder_factory=false}" 7 | 8 | if [ $# -ne 1 ]; then 9 | echo "Usage: $0 " 10 | echo "Example: $0 branch-heads/4430" 11 | exit 1 12 | fi 13 | revision=$1 14 | 15 | rm -r ./out && mkdir -p ./out 16 | docker run --rm -ti -v "$(pwd)/out:/out" -v "$(pwd)/patches:/patches" \ 17 | $IMAGE /bin/bash -c " 18 | set -euo pipefail 19 | shopt -s nullglob 20 | 21 | export WEBRTC_COMPILE_ARGS='$BUILD_ARGS' 22 | export OUT='/out' 23 | 24 | echo '==> Fetching sources' 25 | fetch webrtc_android 26 | cd src 27 | 28 | echo '==> Checking out revision $revision' 29 | git checkout $revision 30 | 31 | echo '==> Run gclient sync' 32 | gclient sync 33 | 34 | echo '==> Log revision and build args' 35 | git log --pretty=fuller HEAD...HEAD^ > \$OUT/revision.txt 36 | echo \"WEBRTC_COMPILE_ARGS: \$WEBRTC_COMPILE_ARGS\" >> \$OUT/build_args.txt 37 | 38 | echo '==> Apply patches' 39 | for p in /patches/*.patch; do echo \"Applying \$p...\"; git apply \$p; done 40 | ls -noa --time-style=long-iso /patches/*.patch > \$OUT/patches.txt 41 | 42 | echo '==> Build' 43 | for target in $TARGETS; do 44 | echo \"--> Building \$target\" 45 | 46 | gn gen out/\$target --args=\"is_debug=false target_os=\\\"android\\\" target_cpu=\\\"\$target\\\" \$WEBRTC_COMPILE_ARGS\" 47 | bash -c \"source build/android/envsetup.sh && autoninja -C out/\$target webrtc\" 48 | 49 | mkdir -p \$OUT/\$target/ 50 | cp out/\$target/libjingle_peerconnection_so.so \$OUT/\$target/ 51 | done 52 | cp out/arm64/lib.java/sdk/android/libwebrtc.jar \$OUT/ 53 | 54 | echo 'Done!' 55 | " 56 | -------------------------------------------------------------------------------- /build-tools/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM debian:12 2 | 3 | # Update apt cache 4 | RUN apt-get update 5 | 6 | # Install WebRTC base dependencies 7 | RUN DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \ 8 | apt-utils \ 9 | ca-certificates \ 10 | ccache \ 11 | curl \ 12 | git \ 13 | python3 \ 14 | python3-setuptools \ 15 | sudo \ 16 | wget \ 17 | xz-utils 18 | 19 | # Install WebRTC for Linux compile dependencies 20 | RUN DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \ 21 | libxml2 \ 22 | pkg-config 23 | 24 | # Add user 25 | ARG USERNAME=docker-webrtc 26 | ARG UID=1000 27 | ARG GID=1000 28 | RUN groupadd -g $GID -o $USERNAME \ 29 | && useradd -l -m -u $UID -g $GID -o -s /bin/bash $USERNAME \ 30 | && usermod -a -G sudo $USERNAME \ 31 | && echo '%sudo ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers 32 | 33 | # Get Chromium depot tools 34 | RUN git clone --depth 1 https://chromium.googlesource.com/chromium/tools/depot_tools.git /opt/depot_tools \ 35 | && chown -R $UID:$GID /opt/depot_tools 36 | ENV PATH /opt/depot_tools:$PATH 37 | 38 | # Create workdir 39 | RUN mkdir /webrtc && chown $UID:$GID /webrtc 40 | 41 | # Setup CCache 42 | ENV CCACHE_DIR /webrtc/.ccache 43 | ENV CCACHE_BASEDIR /webrtc/src 44 | ENV CCACHE_SLOPPINESS include_file_mtime 45 | 46 | # Run as user 47 | USER $USERNAME 48 | 49 | # Run fetch and gclient initially since this pulls a lot of data when run 50 | # for the first time 51 | RUN fetch --help > /dev/null \ 52 | && gclient --help > /dev/null 53 | 54 | # Set working directory 55 | WORKDIR /webrtc 56 | -------------------------------------------------------------------------------- /cli.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -euo pipefail 3 | TARGETS="${WEBRTC_TARGETS:-arm arm64 x86 x64}" 4 | BUILD_ARGS="${WEBRTC_BUILD_ARGS:-symbol_level=1 debuggable_apks=false enable_libaom=false rtc_enable_protobuf=false rtc_include_dav1d_in_internal_decoder_factory=false}" 5 | 6 | function print_usage { 7 | echo "Usage: $0 []" 8 | echo "" 9 | echo " clean" 10 | echo " build-tools" 11 | echo "" 12 | echo " fetch []" 13 | echo " update" 14 | echo " sync" 15 | echo " patch" 16 | echo " build <${TARGETS}>" 17 | echo " build-all" 18 | echo " run" 19 | exit 1 20 | } 21 | 22 | function require_tools_image { 23 | docker image inspect threema/webrtc-build-tools &>/dev/null || ( 24 | echo "Build tools image must be built first: '$0 build-tools'" 25 | exit 2 26 | ) 27 | } 28 | 29 | function build_target { 30 | target=$1 31 | build_args=${@:2} 32 | 33 | docker run -it -v ${PWD}/webrtc:/webrtc threema/webrtc-build-tools:latest bash -c " 34 | set -euo pipefail 35 | cd src 36 | gn gen out/android-${target} --args='cc_wrapper=\"ccache\" target_os=\"android\" target_cpu=\"${target}\" ${build_args}' 37 | source build/android/envsetup.sh 38 | autoninja -C out/android-${target} webrtc 39 | " 40 | } 41 | 42 | function after_build_target { 43 | target=$1 44 | 45 | # Copy shared library 46 | mkdir -p out/${target}/ 47 | cp webrtc/src/out/android-${target}/libjingle_peerconnection_so.so out/${target}/ 48 | } 49 | 50 | function after_build_common { 51 | target=$1 52 | build_args=${@:2} 53 | 54 | # Copy Java library 55 | mkdir -p out/ 56 | cp webrtc/src/out/android-${target}/lib.java/sdk/android/libwebrtc.jar out/ 57 | 58 | # Log revision and build args 59 | mkdir -p out/ 60 | (cd webrtc/src && git log --pretty=fuller HEAD...HEAD^ > ../../out/revision.txt) 61 | echo "${build_args}" > out/build_args.txt 62 | } 63 | 64 | case ${1-} in 65 | clean) 66 | echo "Removing built files" 67 | rm -rf out 68 | echo "Removing tools image" 69 | docker rmi threema/webrtc-build-tools:latest || true 70 | echo "Removing source files" 71 | rm -rf webrtc 72 | ;; 73 | 74 | build-tools) 75 | echo "Building tools image" 76 | docker build --build-arg UID=$(id -u) --build-arg GID=$(id -g) \ 77 | --pull --no-cache -t \ 78 | threema/webrtc-build-tools:latest build-tools/ 79 | ;; 80 | 81 | fetch) 82 | require_tools_image 83 | if [[ -d "webrtc" ]]; then 84 | echo "Cannot fetch, source directory 'webrtc' already exists" 85 | echo "Run '$0 clean' to start from scratch" 86 | exit 3; 87 | fi 88 | 89 | # Fetch sources 90 | mkdir webrtc 91 | revision=${2:-main} 92 | docker run -it -v ${PWD}/webrtc:/webrtc threema/webrtc-build-tools:latest bash -c " 93 | set -euo pipefail 94 | echo 'Fetching source files' 95 | fetch webrtc_android 96 | echo 'Checking out revision $revision' 97 | cd src && git checkout $revision && cd - 98 | echo 'Updating third party repos and running pre-compile hooks' 99 | gclient sync -D 100 | " 101 | ;; 102 | 103 | update) 104 | require_tools_image 105 | if [[ ! -d "webrtc" ]]; then 106 | echo "Cannot update, source directory 'webrtc' does not exist" 107 | echo "Did you forget to run an initial '$0 fetch'?" 108 | exit 4; 109 | fi 110 | 111 | # Stash existing patches/uncommitted changes 112 | (cd webrtc/src && git stash push -u) 113 | 114 | # Update sources 115 | docker run -it -v ${PWD}/webrtc:/webrtc threema/webrtc-build-tools:latest bash -c " 116 | set -euo pipefail 117 | echo 'Updating source files and tracking branches' 118 | echo 'Note: This will leave all untracked branches untouched!' 119 | (cd src && git rebase-update) 120 | echo 'Updating third party repos and running pre-compile hooks' 121 | gclient sync -D 122 | echo 'Done. Any patches and uncommited changes to libwebrtc need to be reapplied.' 123 | " 124 | ;; 125 | 126 | sync) 127 | require_tools_image 128 | if [[ ! -d "webrtc" ]]; then 129 | echo "Cannot update, source directory 'webrtc' does not exist" 130 | echo "Did you forget to run an initial '$0 fetch'?" 131 | exit 4; 132 | fi 133 | 134 | # Sync sources 135 | docker run -it -v ${PWD}/webrtc:/webrtc threema/webrtc-build-tools:latest bash -c " 136 | set -euo pipefail 137 | echo 'Syncing third party repos and running pre-compile hooks' 138 | gclient sync -D 139 | " 140 | ;; 141 | 142 | patch) 143 | if [[ ! -d "webrtc" ]]; then 144 | echo "Cannot patch, source directory 'webrtc' does not exist" 145 | echo "Did you forget to run an initial '$0 fetch'?" 146 | exit 4; 147 | fi 148 | 149 | # Stash existing patches/uncommitted changes 150 | (cd webrtc/src && git stash push -u) 151 | 152 | # Apply patches 153 | pattern=${2-*.patch} 154 | cd webrtc/src 155 | shopt -s nullglob 156 | patch_count=0 157 | for patch in ../../patches/${pattern}; do 158 | echo "Applying ${patch}..." 159 | git apply ${patch} 160 | patch_count=$((patch_count+1)) 161 | done 162 | echo "Applied ${patch_count} patches" 163 | cd ../../ 164 | 165 | # Log patches 166 | mkdir -p out/ 167 | ls -noa --time-style=long-iso patches/${pattern} > out/patches.txt 168 | ;; 169 | 170 | build) 171 | require_tools_image 172 | if [[ ! -d "webrtc" ]]; then 173 | echo "Cannot build, source directory 'webrtc' does not exist" 174 | echo "Did you forget to run an initial '$0 fetch'?" 175 | exit 4; 176 | fi 177 | 178 | # Build target and copy files into out/ 179 | if [ -z "${2-}" ]; then 180 | print_usage 181 | fi 182 | target=$2 183 | echo "Building ${target}" 184 | build_target ${target} ${BUILD_ARGS} 185 | after_build_target ${target} 186 | after_build_common ${target} ${BUILD_ARGS} 187 | echo "Built ${target} into out/${target}" 188 | ;; 189 | 190 | build-all) 191 | require_tools_image 192 | if [[ ! -d "webrtc" ]]; then 193 | echo "Cannot build, source directory 'webrtc' does not exist" 194 | echo "Did you forget to run an initial '$0 fetch'?" 195 | exit 4; 196 | fi 197 | 198 | # Build all targets and copy files into out/ 199 | for target in $TARGETS; do 200 | echo "Building ${target}" 201 | build_target ${target} ${BUILD_ARGS} 202 | after_build_target ${target} 203 | echo "Built ${target} into out/${target}" 204 | done 205 | after_build_common ${target} ${BUILD_ARGS} 206 | ;; 207 | 208 | run) 209 | require_tools_image 210 | 211 | # Run an interactive shell 212 | docker run -it -v ${PWD}/webrtc:/webrtc threema/webrtc-build-tools:latest 213 | ;; 214 | 215 | *) 216 | print_usage $0 217 | esac 218 | -------------------------------------------------------------------------------- /patches/.gitdir: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/threema-ch/webrtc-build-docker/c1bdc77da1513aa816a6e5d29150b3da8a04c144/patches/.gitdir -------------------------------------------------------------------------------- /patches/disable-dtmf-and-comfort-noise.patch: -------------------------------------------------------------------------------- 1 | diff --git a/media/engine/webrtc_voice_engine.cc b/media/engine/webrtc_voice_engine.cc 2 | index a7568696db..670db725ff 100644 3 | --- a/media/engine/webrtc_voice_engine.cc 4 | +++ b/media/engine/webrtc_voice_engine.cc 5 | @@ -368,13 +368,6 @@ std::vector LegacyCollectCodecs( 6 | webrtc::PayloadTypePicker pt_mapper; 7 | std::vector out; 8 | 9 | - // Only generate CN payload types for these clockrates: 10 | - std::map> generate_cn = { 11 | - {8000, false}, {16000, false}, {32000, false}}; 12 | - // Only generate telephone-event payload types for these clockrates: 13 | - std::map> generate_dtmf = { 14 | - {8000, false}, {16000, false}, {32000, false}, {48000, false}}; 15 | - 16 | for (const auto& spec : specs) { 17 | cricket::Codec codec = CreateAudioCodec(spec.format); 18 | if (allocate_pt) { 19 | @@ -390,21 +383,6 @@ std::vector LegacyCollectCodecs( 20 | FeedbackParam(kRtcpFbParamTransportCc, kParamValueEmpty)); 21 | } 22 | 23 | - if (spec.info.allow_comfort_noise) { 24 | - // Generate a CN entry if the decoder allows it and we support the 25 | - // clockrate. 26 | - auto cn = generate_cn.find(spec.format.clockrate_hz); 27 | - if (cn != generate_cn.end()) { 28 | - cn->second = true; 29 | - } 30 | - } 31 | - 32 | - // Generate a telephone-event entry if we support the clockrate. 33 | - auto dtmf = generate_dtmf.find(spec.format.clockrate_hz); 34 | - if (dtmf != generate_dtmf.end()) { 35 | - dtmf->second = true; 36 | - } 37 | - 38 | out.push_back(codec); 39 | 40 | // TODO(hta): Don't assign RED codecs until we know that the PT for Opus 41 | @@ -426,28 +404,6 @@ std::vector LegacyCollectCodecs( 42 | } 43 | } 44 | 45 | - // Add CN codecs after "proper" audio codecs. 46 | - for (const auto& cn : generate_cn) { 47 | - if (cn.second) { 48 | - cricket::Codec cn_codec = CreateAudioCodec({kCnCodecName, cn.first, 1}); 49 | - if (allocate_pt) { 50 | - cn_codec.id = pt_mapper.SuggestMapping(cn_codec, nullptr).value(); 51 | - } 52 | - out.push_back(cn_codec); 53 | - } 54 | - } 55 | - 56 | - // Add telephone-event codecs last. 57 | - for (const auto& dtmf : generate_dtmf) { 58 | - if (dtmf.second) { 59 | - cricket::Codec dtmf_codec = 60 | - CreateAudioCodec({kDtmfCodecName, dtmf.first, 1}); 61 | - if (allocate_pt) { 62 | - dtmf_codec.id = pt_mapper.SuggestMapping(dtmf_codec, nullptr).value(); 63 | - } 64 | - out.push_back(dtmf_codec); 65 | - } 66 | - } 67 | return out; 68 | } 69 | 70 | @@ -1418,20 +1374,6 @@ bool WebRtcVoiceSendChannel::SetSendCodecs( 71 | } 72 | } 73 | 74 | - // Find PT of telephone-event codec with lowest clockrate, as a fallback, in 75 | - // case we don't have a DTMF codec with a rate matching the send codec's, or 76 | - // if this function returns early. 77 | - std::vector dtmf_codecs; 78 | - for (const Codec& codec : codecs) { 79 | - if (IsCodec(codec, kDtmfCodecName)) { 80 | - dtmf_codecs.push_back(codec); 81 | - if (!dtmf_payload_type_ || codec.clockrate < dtmf_payload_freq_) { 82 | - dtmf_payload_type_ = codec.id; 83 | - dtmf_payload_freq_ = codec.clockrate; 84 | - } 85 | - } 86 | - } 87 | - 88 | // Scan through the list to figure out the codec to use for sending. 89 | std::optional send_codec_spec; 90 | webrtc::BitrateConstraints bitrate_config; 91 | @@ -1471,36 +1413,6 @@ bool WebRtcVoiceSendChannel::SetSendCodecs( 92 | } 93 | 94 | RTC_DCHECK(voice_codec_info); 95 | - if (voice_codec_info->allow_comfort_noise) { 96 | - // Loop through the codecs list again to find the CN codec. 97 | - // TODO(solenberg): Break out into a separate function? 98 | - for (const Codec& cn_codec : codecs) { 99 | - if (IsCodec(cn_codec, kCnCodecName) && 100 | - cn_codec.clockrate == send_codec_spec->format.clockrate_hz && 101 | - cn_codec.channels == voice_codec_info->num_channels) { 102 | - if (cn_codec.channels != 1) { 103 | - RTC_LOG(LS_WARNING) 104 | - << "CN #channels " << cn_codec.channels << " not supported."; 105 | - } else if (cn_codec.clockrate != 8000 && cn_codec.clockrate != 16000 && 106 | - cn_codec.clockrate != 32000) { 107 | - RTC_LOG(LS_WARNING) 108 | - << "CN frequency " << cn_codec.clockrate << " not supported."; 109 | - } else { 110 | - send_codec_spec->cng_payload_type = cn_codec.id; 111 | - } 112 | - break; 113 | - } 114 | - } 115 | - 116 | - // Find the telephone-event PT exactly matching the preferred send codec. 117 | - for (const Codec& dtmf_codec : dtmf_codecs) { 118 | - if (dtmf_codec.clockrate == send_codec_spec->format.clockrate_hz) { 119 | - dtmf_payload_type_ = dtmf_codec.id; 120 | - dtmf_payload_freq_ = dtmf_codec.clockrate; 121 | - break; 122 | - } 123 | - } 124 | - } 125 | 126 | // Loop through the codecs to find the RED codec that matches opus 127 | // with respect to clockrate and number of channels. 128 | diff --git a/sdk/android/api/org/webrtc/RtpSender.java b/sdk/android/api/org/webrtc/RtpSender.java 129 | index de0781e2e7..adeefbe20c 100644 130 | --- a/sdk/android/api/org/webrtc/RtpSender.java 131 | +++ b/sdk/android/api/org/webrtc/RtpSender.java 132 | @@ -28,12 +28,7 @@ public class RtpSender { 133 | long nativeTrack = nativeGetTrack(nativeRtpSender); 134 | cachedTrack = MediaStreamTrack.createMediaStreamTrack(nativeTrack); 135 | 136 | - if (nativeGetMediaType(nativeRtpSender).equalsIgnoreCase(MediaStreamTrack.AUDIO_TRACK_KIND)) { 137 | - long nativeDtmfSender = nativeGetDtmfSender(nativeRtpSender); 138 | - dtmfSender = (nativeDtmfSender != 0) ? new DtmfSender(nativeDtmfSender) : null; 139 | - } else { 140 | - dtmfSender = null; 141 | - } 142 | + dtmfSender = null; 143 | } 144 | 145 | /** 146 | diff --git a/sdk/objc/api/peerconnection/RTCRtpSender.mm b/sdk/objc/api/peerconnection/RTCRtpSender.mm 147 | index 08ab9ed404..acece122f6 100644 148 | --- a/sdk/objc/api/peerconnection/RTCRtpSender.mm 149 | +++ b/sdk/objc/api/peerconnection/RTCRtpSender.mm 150 | @@ -128,14 +128,6 @@ 151 | if (self) { 152 | _factory = factory; 153 | _nativeRtpSender = nativeRtpSender; 154 | - if (_nativeRtpSender->media_type() == cricket::MEDIA_TYPE_AUDIO) { 155 | - rtc::scoped_refptr nativeDtmfSender( 156 | - _nativeRtpSender->GetDtmfSender()); 157 | - if (nativeDtmfSender) { 158 | - _dtmfSender = [[RTC_OBJC_TYPE(RTCDtmfSender) alloc] 159 | - initWithNativeDtmfSender:nativeDtmfSender]; 160 | - } 161 | - } 162 | RTCLogInfo(@"RTC_OBJC_TYPE(RTCRtpSender)(%p): created sender: %@", 163 | self, 164 | self.description); 165 | -------------------------------------------------------------------------------- /patches/disable-unused-audio-codecs.patch: -------------------------------------------------------------------------------- 1 | diff --git a/api/audio_codecs/L16/audio_decoder_L16.cc b/api/audio_codecs/L16/audio_decoder_L16.cc 2 | index 162a9b91d2..ce59eade59 100644 3 | --- a/api/audio_codecs/L16/audio_decoder_L16.cc 4 | +++ b/api/audio_codecs/L16/audio_decoder_L16.cc 5 | @@ -38,7 +38,7 @@ std::optional AudioDecoderL16::SdpToConfig( 6 | 7 | void AudioDecoderL16::AppendSupportedDecoders( 8 | std::vector* specs) { 9 | - Pcm16BAppendSupportedCodecSpecs(specs); 10 | + // disabled 11 | } 12 | 13 | std::unique_ptr AudioDecoderL16::MakeAudioDecoder( 14 | @@ -48,8 +48,7 @@ std::unique_ptr AudioDecoderL16::MakeAudioDecoder( 15 | if (!config.IsOk()) { 16 | return nullptr; 17 | } 18 | - return std::make_unique(config.sample_rate_hz, 19 | - config.num_channels); 20 | + return nullptr; // disabled 21 | } 22 | 23 | } // namespace webrtc 24 | diff --git a/api/audio_codecs/L16/audio_encoder_L16.cc b/api/audio_codecs/L16/audio_encoder_L16.cc 25 | index 3bbaa79ba9..d0d500f128 100644 26 | --- a/api/audio_codecs/L16/audio_encoder_L16.cc 27 | +++ b/api/audio_codecs/L16/audio_encoder_L16.cc 28 | @@ -56,7 +56,7 @@ std::optional AudioEncoderL16::SdpToConfig( 29 | 30 | void AudioEncoderL16::AppendSupportedEncoders( 31 | std::vector* specs) { 32 | - Pcm16BAppendSupportedCodecSpecs(specs); 33 | + // disabled 34 | } 35 | 36 | AudioCodecInfo AudioEncoderL16::QueryAudioEncoder( 37 | @@ -72,16 +72,7 @@ std::unique_ptr AudioEncoderL16::MakeAudioEncoder( 38 | int payload_type, 39 | std::optional /*codec_pair_id*/, 40 | const FieldTrialsView* /* field_trials */) { 41 | - AudioEncoderPcm16B::Config c; 42 | - c.sample_rate_hz = config.sample_rate_hz; 43 | - c.num_channels = config.num_channels; 44 | - c.frame_size_ms = config.frame_size_ms; 45 | - c.payload_type = payload_type; 46 | - if (!config.IsOk()) { 47 | - RTC_DCHECK_NOTREACHED(); 48 | - return nullptr; 49 | - } 50 | - return std::make_unique(c); 51 | + return nullptr; // disabled 52 | } 53 | 54 | } // namespace webrtc 55 | diff --git a/api/audio_codecs/builtin_audio_decoder_factory.cc b/api/audio_codecs/builtin_audio_decoder_factory.cc 56 | index 36fc39d5ba..c22661a54f 100644 57 | --- a/api/audio_codecs/builtin_audio_decoder_factory.cc 58 | +++ b/api/audio_codecs/builtin_audio_decoder_factory.cc 59 | @@ -14,19 +14,14 @@ 60 | #include 61 | #include 62 | 63 | -#include "api/audio_codecs/L16/audio_decoder_L16.h" 64 | #include "api/audio_codecs/audio_codec_pair_id.h" 65 | #include "api/audio_codecs/audio_decoder.h" 66 | #include "api/audio_codecs/audio_decoder_factory.h" 67 | #include "api/audio_codecs/audio_decoder_factory_template.h" 68 | #include "api/audio_codecs/audio_format.h" 69 | -#include "api/audio_codecs/g711/audio_decoder_g711.h" 70 | -#include "api/audio_codecs/g722/audio_decoder_g722.h" 71 | -#include "api/scoped_refptr.h" 72 | -#if WEBRTC_USE_BUILTIN_OPUS 73 | #include "api/audio_codecs/opus/audio_decoder_multi_channel_opus.h" 74 | -#include "api/audio_codecs/opus/audio_decoder_opus.h" // nogncheck 75 | -#endif 76 | +#include "api/audio_codecs/opus/audio_decoder_opus.h" 77 | +#include "api/scoped_refptr.h" 78 | 79 | namespace webrtc { 80 | 81 | @@ -54,12 +49,7 @@ struct NotAdvertised { 82 | 83 | rtc::scoped_refptr CreateBuiltinAudioDecoderFactory() { 84 | return CreateAudioDecoderFactory< 85 | - 86 | -#if WEBRTC_USE_BUILTIN_OPUS 87 | - AudioDecoderOpus, NotAdvertised, 88 | -#endif 89 | - 90 | - AudioDecoderG722, AudioDecoderG711, NotAdvertised>(); 91 | + AudioDecoderOpus, NotAdvertised>(); 92 | } 93 | 94 | } // namespace webrtc 95 | diff --git a/api/audio_codecs/builtin_audio_encoder_factory.cc b/api/audio_codecs/builtin_audio_encoder_factory.cc 96 | index aceb64de7f..7e30906027 100644 97 | --- a/api/audio_codecs/builtin_audio_encoder_factory.cc 98 | +++ b/api/audio_codecs/builtin_audio_encoder_factory.cc 99 | @@ -14,20 +14,15 @@ 100 | #include 101 | #include 102 | 103 | -#include "api/audio_codecs/L16/audio_encoder_L16.h" 104 | #include "api/audio_codecs/audio_codec_pair_id.h" 105 | #include "api/audio_codecs/audio_encoder.h" 106 | #include "api/audio_codecs/audio_encoder_factory.h" 107 | #include "api/audio_codecs/audio_encoder_factory_template.h" 108 | #include "api/audio_codecs/audio_format.h" 109 | -#include "api/audio_codecs/g711/audio_encoder_g711.h" 110 | -#include "api/audio_codecs/g722/audio_encoder_g722.h" 111 | +#include "api/audio_codecs/opus/audio_encoder_multi_channel_opus.h" 112 | +#include "api/audio_codecs/opus/audio_encoder_opus.h" 113 | #include "api/field_trials_view.h" 114 | #include "api/scoped_refptr.h" 115 | -#if WEBRTC_USE_BUILTIN_OPUS 116 | -#include "api/audio_codecs/opus/audio_encoder_multi_channel_opus.h" 117 | -#include "api/audio_codecs/opus/audio_encoder_opus.h" // nogncheck 118 | -#endif 119 | 120 | namespace webrtc { 121 | 122 | @@ -61,12 +56,7 @@ struct NotAdvertised { 123 | 124 | rtc::scoped_refptr CreateBuiltinAudioEncoderFactory() { 125 | return CreateAudioEncoderFactory< 126 | - 127 | -#if WEBRTC_USE_BUILTIN_OPUS 128 | - AudioEncoderOpus, NotAdvertised, 129 | -#endif 130 | - 131 | - AudioEncoderG722, AudioEncoderG711, NotAdvertised>(); 132 | + AudioEncoderOpus, NotAdvertised>(); 133 | } 134 | 135 | } // namespace webrtc 136 | diff --git a/api/audio_codecs/g711/audio_decoder_g711.cc b/api/audio_codecs/g711/audio_decoder_g711.cc 137 | index e59dca66db..fb1494cfe0 100644 138 | --- a/api/audio_codecs/g711/audio_decoder_g711.cc 139 | +++ b/api/audio_codecs/g711/audio_decoder_g711.cc 140 | @@ -47,9 +47,7 @@ std::optional AudioDecoderG711::SdpToConfig( 141 | 142 | void AudioDecoderG711::AppendSupportedDecoders( 143 | std::vector* specs) { 144 | - for (const char* type : {"PCMU", "PCMA"}) { 145 | - specs->push_back({{type, 8000, 1}, {8000, 1, 64000}}); 146 | - } 147 | + // disabled 148 | } 149 | 150 | std::unique_ptr AudioDecoderG711::MakeAudioDecoder( 151 | @@ -60,15 +58,7 @@ std::unique_ptr AudioDecoderG711::MakeAudioDecoder( 152 | RTC_DCHECK_NOTREACHED(); 153 | return nullptr; 154 | } 155 | - switch (config.type) { 156 | - case Config::Type::kPcmU: 157 | - return std::make_unique(config.num_channels); 158 | - case Config::Type::kPcmA: 159 | - return std::make_unique(config.num_channels); 160 | - default: 161 | - RTC_DCHECK_NOTREACHED(); 162 | - return nullptr; 163 | - } 164 | + return nullptr; // disabled 165 | } 166 | 167 | } // namespace webrtc 168 | diff --git a/api/audio_codecs/g711/audio_encoder_g711.cc b/api/audio_codecs/g711/audio_encoder_g711.cc 169 | index aa676a22c8..4fd6dde0a4 100644 170 | --- a/api/audio_codecs/g711/audio_encoder_g711.cc 171 | +++ b/api/audio_codecs/g711/audio_encoder_g711.cc 172 | @@ -61,9 +61,7 @@ std::optional AudioEncoderG711::SdpToConfig( 173 | 174 | void AudioEncoderG711::AppendSupportedEncoders( 175 | std::vector* specs) { 176 | - for (const char* type : {"PCMU", "PCMA"}) { 177 | - specs->push_back({{type, 8000, 1}, {8000, 1, 64000}}); 178 | - } 179 | + // disabled 180 | } 181 | 182 | AudioCodecInfo AudioEncoderG711::QueryAudioEncoder(const Config& config) { 183 | @@ -81,26 +79,7 @@ std::unique_ptr AudioEncoderG711::MakeAudioEncoder( 184 | RTC_DCHECK_NOTREACHED(); 185 | return nullptr; 186 | } 187 | - switch (config.type) { 188 | - case Config::Type::kPcmU: { 189 | - AudioEncoderPcmU::Config impl_config; 190 | - impl_config.num_channels = config.num_channels; 191 | - impl_config.frame_size_ms = config.frame_size_ms; 192 | - impl_config.payload_type = payload_type; 193 | - return std::make_unique(impl_config); 194 | - } 195 | - case Config::Type::kPcmA: { 196 | - AudioEncoderPcmA::Config impl_config; 197 | - impl_config.num_channels = config.num_channels; 198 | - impl_config.frame_size_ms = config.frame_size_ms; 199 | - impl_config.payload_type = payload_type; 200 | - return std::make_unique(impl_config); 201 | - } 202 | - default: { 203 | - RTC_DCHECK_NOTREACHED(); 204 | - return nullptr; 205 | - } 206 | - } 207 | + return nullptr; // disabled 208 | } 209 | 210 | } // namespace webrtc 211 | diff --git a/api/audio_codecs/g722/audio_decoder_g722.cc b/api/audio_codecs/g722/audio_decoder_g722.cc 212 | index 88581bbc2e..df499c3489 100644 213 | --- a/api/audio_codecs/g722/audio_decoder_g722.cc 214 | +++ b/api/audio_codecs/g722/audio_decoder_g722.cc 215 | @@ -37,7 +37,7 @@ std::optional AudioDecoderG722::SdpToConfig( 216 | 217 | void AudioDecoderG722::AppendSupportedDecoders( 218 | std::vector* specs) { 219 | - specs->push_back({{"G722", 8000, 1}, {16000, 1, 64000}}); 220 | + // disabled 221 | } 222 | 223 | std::unique_ptr AudioDecoderG722::MakeAudioDecoder( 224 | @@ -48,15 +48,7 @@ std::unique_ptr AudioDecoderG722::MakeAudioDecoder( 225 | RTC_DCHECK_NOTREACHED(); 226 | return nullptr; 227 | } 228 | - switch (config.num_channels) { 229 | - case 1: 230 | - return std::make_unique(); 231 | - case 2: 232 | - return std::make_unique(); 233 | - default: 234 | - RTC_DCHECK_NOTREACHED(); 235 | - return nullptr; 236 | - } 237 | + return nullptr; // disabled 238 | } 239 | 240 | } // namespace webrtc 241 | diff --git a/api/audio_codecs/g722/audio_encoder_g722.cc b/api/audio_codecs/g722/audio_encoder_g722.cc 242 | index 6c932ef039..7b1683e3c5 100644 243 | --- a/api/audio_codecs/g722/audio_encoder_g722.cc 244 | +++ b/api/audio_codecs/g722/audio_encoder_g722.cc 245 | @@ -58,9 +58,7 @@ std::optional AudioEncoderG722::SdpToConfig( 246 | 247 | void AudioEncoderG722::AppendSupportedEncoders( 248 | std::vector* specs) { 249 | - const SdpAudioFormat fmt = {"G722", 8000, 1}; 250 | - const AudioCodecInfo info = QueryAudioEncoder(*SdpToConfig(fmt)); 251 | - specs->push_back({fmt, info}); 252 | + // disabled 253 | } 254 | 255 | AudioCodecInfo AudioEncoderG722::QueryAudioEncoder( 256 | @@ -79,7 +77,7 @@ std::unique_ptr AudioEncoderG722::MakeAudioEncoder( 257 | RTC_DCHECK_NOTREACHED(); 258 | return nullptr; 259 | } 260 | - return std::make_unique(config, payload_type); 261 | + return nullptr; // disabled 262 | } 263 | 264 | } // namespace webrtc 265 | -------------------------------------------------------------------------------- /patches/dont-leak-video-orientation.patch: -------------------------------------------------------------------------------- 1 | diff --git a/modules/rtp_rtcp/source/rtp_sender_video.cc b/modules/rtp_rtcp/source/rtp_sender_video.cc 2 | index 9ba860836a..e9bb90bd98 100644 3 | --- a/modules/rtp_rtcp/source/rtp_sender_video.cc 4 | +++ b/modules/rtp_rtcp/source/rtp_sender_video.cc 5 | @@ -365,10 +365,7 @@ void RTPSenderVideo::AddRtpHeaderExtensions(const RTPVideoHeader& video_header, 6 | // value sent. 7 | // Set rotation when key frame or when changed (to follow standard). 8 | // Or when different from 0 (to follow current receiver implementation). 9 | - bool set_video_rotation = 10 | - video_header.frame_type == VideoFrameType::kVideoFrameKey || 11 | - video_header.rotation != last_rotation_ || 12 | - video_header.rotation != kVideoRotation_0; 13 | + bool set_video_rotation = true; 14 | if (last_packet && set_video_rotation) 15 | packet->SetExtension(video_header.rotation); 16 | 17 | -------------------------------------------------------------------------------- /patches/dtls-cipher-suites.patch: -------------------------------------------------------------------------------- 1 | diff --git a/rtc_base/openssl_stream_adapter.cc b/rtc_base/openssl_stream_adapter.cc 2 | index e1e367b1ba..58bcf9c150 100644 3 | --- a/rtc_base/openssl_stream_adapter.cc 4 | +++ b/rtc_base/openssl_stream_adapter.cc 5 | @@ -1064,9 +1064,10 @@ SSL_CTX* OpenSSLStreamAdapter::SetupSSLContext() { 6 | // remove HMAC-SHA256 and HMAC-SHA384 cipher suites, not GCM cipher suites 7 | // with SHA256 or SHA384 as the handshake hash. 8 | // This matches the list of SSLClientSocketImpl in Chromium. 9 | - SSL_CTX_set_cipher_list( 10 | - ctx, 11 | - "DEFAULT:!NULL:!aNULL:!SHA256:!SHA384:!aECDH:!AESGCM+AES256:!aPSK:!3DES"); 12 | + SSL_CTX_set_cipher_list(ctx, 13 | + "ECDHE-ECDSA-CHACHA20-POLY1305:" 14 | + "ECDHE-ECDSA-AES256-GCM-SHA384:" 15 | + "ECDHE-ECDSA-AES128-GCM-SHA256:"); 16 | 17 | if (!srtp_ciphers_.empty()) { 18 | if (SSL_CTX_set_tlsext_use_srtp(ctx, srtp_ciphers_.c_str())) { 19 | -------------------------------------------------------------------------------- /patches/enable-cbr-by-default.patch: -------------------------------------------------------------------------------- 1 | diff --git a/modules/audio_coding/codecs/opus/audio_encoder_opus.cc b/modules/audio_coding/codecs/opus/audio_encoder_opus.cc 2 | index 83f356091c..6db1a44910 100644 3 | --- a/modules/audio_coding/codecs/opus/audio_encoder_opus.cc 4 | +++ b/modules/audio_coding/codecs/opus/audio_encoder_opus.cc 5 | @@ -208,10 +208,11 @@ int GetMultipliedBitrate(int bitrate, const std::vector& multipliers) { 6 | 7 | void AudioEncoderOpusImpl::AppendSupportedEncoders( 8 | std::vector* specs) { 9 | - const SdpAudioFormat fmt = {"opus", 10 | - kRtpTimestampRateHz, 11 | - 2, 12 | - {{"minptime", "10"}, {"useinbandfec", "1"}}}; 13 | + const SdpAudioFormat fmt = { 14 | + "opus", 15 | + kRtpTimestampRateHz, 16 | + 2, 17 | + {{"minptime", "10"}, {"useinbandfec", "1"}, {"cbr", "1"}}}; 18 | const AudioCodecInfo info = QueryAudioEncoder(*SdpToConfig(fmt)); 19 | specs->push_back({fmt, info}); 20 | } 21 | -------------------------------------------------------------------------------- /patches/expose-certificate-fingerprint.patch: -------------------------------------------------------------------------------- 1 | diff --git a/rtc_base/rtc_certificate.cc b/rtc_base/rtc_certificate.cc 2 | index 93e5f15148..3ad049c86e 100644 3 | --- a/rtc_base/rtc_certificate.cc 4 | +++ b/rtc_base/rtc_certificate.cc 5 | @@ -14,6 +14,7 @@ 6 | 7 | #include "rtc_base/checks.h" 8 | #include "rtc_base/ssl_certificate.h" 9 | +#include "rtc_base/ssl_fingerprint.h" 10 | #include "rtc_base/ssl_identity.h" 11 | #include "rtc_base/time_utils.h" 12 | 13 | @@ -53,8 +54,10 @@ const SSLCertChain& RTCCertificate::GetSSLCertificateChain() const { 14 | } 15 | 16 | RTCCertificatePEM RTCCertificate::ToPEM() const { 17 | + std::string fingerprint = 18 | + SSLFingerprint::CreateFromCertificate(*this).get()->ToString(); 19 | return RTCCertificatePEM(identity_->PrivateKeyToPEMString(), 20 | - GetSSLCertificate().ToPEMString()); 21 | + GetSSLCertificate().ToPEMString(), fingerprint); 22 | } 23 | 24 | scoped_refptr RTCCertificate::FromPEM( 25 | diff --git a/rtc_base/rtc_certificate.h b/rtc_base/rtc_certificate.h 26 | index 67c5c29a89..3914535792 100644 27 | --- a/rtc_base/rtc_certificate.h 28 | +++ b/rtc_base/rtc_certificate.h 29 | @@ -37,15 +37,20 @@ class SSLIdentity; 30 | class RTCCertificatePEM { 31 | public: 32 | RTCCertificatePEM(absl::string_view private_key, 33 | - absl::string_view certificate) 34 | - : private_key_(private_key), certificate_(certificate) {} 35 | + absl::string_view certificate, 36 | + absl::string_view fingerprint) 37 | + : private_key_(private_key), 38 | + certificate_(certificate), 39 | + fingerprint_(fingerprint) {} 40 | 41 | const std::string& private_key() const { return private_key_; } 42 | const std::string& certificate() const { return certificate_; } 43 | + const std::string& fingerprint() const { return fingerprint_; } 44 | 45 | private: 46 | std::string private_key_; 47 | std::string certificate_; 48 | + std::string fingerprint_; 49 | }; 50 | 51 | // A thin abstraction layer between "lower level crypto stuff" like 52 | diff --git a/sdk/android/api/org/webrtc/RtcCertificatePem.java b/sdk/android/api/org/webrtc/RtcCertificatePem.java 53 | index 6070135b3e..ab7a07a516 100644 54 | --- a/sdk/android/api/org/webrtc/RtcCertificatePem.java 55 | +++ b/sdk/android/api/org/webrtc/RtcCertificatePem.java 56 | @@ -20,14 +20,17 @@ public class RtcCertificatePem { 57 | public final String privateKey; 58 | /** PEM string representation of the certificate. */ 59 | public final String certificate; 60 | + /** Fingerprint of the DER representation of the certificate. */ 61 | + public final String fingerprint; 62 | /** Default expiration time of 30 days. */ 63 | private static final long DEFAULT_EXPIRY = 60 * 60 * 24 * 30; 64 | 65 | /** Instantiate an RtcCertificatePem object from stored strings. */ 66 | @CalledByNative 67 | - public RtcCertificatePem(String privateKey, String certificate) { 68 | + public RtcCertificatePem(String privateKey, String certificate, String fingerprint) { 69 | this.privateKey = privateKey; 70 | this.certificate = certificate; 71 | + this.fingerprint = fingerprint; 72 | } 73 | 74 | @CalledByNative 75 | @@ -40,6 +43,11 @@ public class RtcCertificatePem { 76 | return certificate; 77 | } 78 | 79 | + @CalledByNative 80 | + String getFingerprint() { 81 | + return fingerprint; 82 | + } 83 | + 84 | /** 85 | * Generate a new RtcCertificatePem with the default settings of KeyType = ECDSA and 86 | * expires = 30 days. 87 | diff --git a/sdk/android/src/jni/pc/rtc_certificate.cc b/sdk/android/src/jni/pc/rtc_certificate.cc 88 | index 484b8d5eac..570c62f207 100644 89 | --- a/sdk/android/src/jni/pc/rtc_certificate.cc 90 | +++ b/sdk/android/src/jni/pc/rtc_certificate.cc 91 | @@ -29,8 +29,11 @@ rtc::RTCCertificatePEM JavaToNativeRTCCertificatePEM( 92 | Java_RtcCertificatePem_getPrivateKey(jni, j_rtc_certificate); 93 | jni_zero::ScopedJavaLocalRef certificate_field = 94 | Java_RtcCertificatePem_getCertificate(jni, j_rtc_certificate); 95 | + jni_zero::ScopedJavaLocalRef fingerprint_field = 96 | + Java_RtcCertificatePem_getFingerprint(jni, j_rtc_certificate); 97 | return rtc::RTCCertificatePEM(JavaToNativeString(jni, privatekey_field), 98 | - JavaToNativeString(jni, certificate_field)); 99 | + JavaToNativeString(jni, certificate_field), 100 | + JavaToNativeString(jni, fingerprint_field)); 101 | } 102 | 103 | ScopedJavaLocalRef NativeToJavaRTCCertificatePEM( 104 | @@ -38,7 +41,8 @@ ScopedJavaLocalRef NativeToJavaRTCCertificatePEM( 105 | const rtc::RTCCertificatePEM& certificate) { 106 | return Java_RtcCertificatePem_Constructor( 107 | jni, NativeToJavaString(jni, certificate.private_key()), 108 | - NativeToJavaString(jni, certificate.certificate())); 109 | + NativeToJavaString(jni, certificate.certificate()), 110 | + NativeToJavaString(jni, certificate.fingerprint())); 111 | } 112 | 113 | static jni_zero::ScopedJavaLocalRef 114 | @@ -54,7 +58,8 @@ JNI_RtcCertificatePem_GenerateCertificate( 115 | rtc::RTCCertificatePEM pem = certificate->ToPEM(); 116 | return Java_RtcCertificatePem_Constructor( 117 | jni, NativeToJavaString(jni, pem.private_key()), 118 | - NativeToJavaString(jni, pem.certificate())); 119 | + NativeToJavaString(jni, pem.certificate()), 120 | + NativeToJavaString(jni, pem.fingerprint())); 121 | } 122 | 123 | } // namespace jni 124 | diff --git a/sdk/objc/api/peerconnection/RTCCertificate.h b/sdk/objc/api/peerconnection/RTCCertificate.h 125 | index 343eb24d0d..8e25810e1b 100644 126 | --- a/sdk/objc/api/peerconnection/RTCCertificate.h 127 | +++ b/sdk/objc/api/peerconnection/RTCCertificate.h 128 | @@ -23,12 +23,16 @@ RTC_OBJC_EXPORT 129 | /** Public key in an x509 cert encoded in PEM. */ 130 | @property(nonatomic, readonly, copy) NSString *certificate; 131 | 132 | +/** Fingerprint of the DER-encoded x509 cert. */ 133 | +@property(nonatomic, readonly, copy) NSString *fingerprint; 134 | + 135 | /** 136 | * Initialize an RTCCertificate with PEM strings for private_key and 137 | * certificate. 138 | */ 139 | - (instancetype)initWithPrivateKey:(NSString *)private_key 140 | certificate:(NSString *)certificate 141 | + fingerprint:(NSString *)fingerprint 142 | NS_DESIGNATED_INITIALIZER; 143 | 144 | - (instancetype)init NS_UNAVAILABLE; 145 | diff --git a/sdk/objc/api/peerconnection/RTCCertificate.mm b/sdk/objc/api/peerconnection/RTCCertificate.mm 146 | index ce4dff9f92..863577f641 100644 147 | --- a/sdk/objc/api/peerconnection/RTCCertificate.mm 148 | +++ b/sdk/objc/api/peerconnection/RTCCertificate.mm 149 | @@ -20,20 +20,24 @@ 150 | 151 | @synthesize private_key = _private_key; 152 | @synthesize certificate = _certificate; 153 | +@synthesize fingerprint = _fingerprint; 154 | 155 | - (id)copyWithZone:(NSZone *)zone { 156 | id copy = [[[self class] alloc] 157 | initWithPrivateKey:[self.private_key copyWithZone:zone] 158 | - certificate:[self.certificate copyWithZone:zone]]; 159 | + certificate:[self.certificate copyWithZone:zone] 160 | + fingerprint:[self.fingerprint copyWithZone:zone]]; 161 | return copy; 162 | } 163 | 164 | - (instancetype)initWithPrivateKey:(NSString *)private_key 165 | - certificate:(NSString *)certificate { 166 | + certificate:(NSString *)certificate 167 | + fingerprint:(NSString *)fingerprint { 168 | self = [super init]; 169 | if (self) { 170 | _private_key = [private_key copy]; 171 | _certificate = [certificate copy]; 172 | + _fingerprint = [fingerprint copy]; 173 | } 174 | return self; 175 | } 176 | @@ -64,12 +68,14 @@ 177 | rtc::RTCCertificatePEM pem = cc_certificate->ToPEM(); 178 | std::string pem_private_key = pem.private_key(); 179 | std::string pem_certificate = pem.certificate(); 180 | + std::string pem_fingerprint = pem.fingerprint(); 181 | RTC_LOG(LS_INFO) << "CERT PEM "; 182 | RTC_LOG(LS_INFO) << pem_certificate; 183 | 184 | RTC_OBJC_TYPE(RTCCertificate) *cert = [[RTC_OBJC_TYPE(RTCCertificate) alloc] 185 | initWithPrivateKey:@(pem_private_key.c_str()) 186 | - certificate:@(pem_certificate.c_str())]; 187 | + certificate:@(pem_certificate.c_str()) 188 | + fingerprint:@(pem_fingerprint.c_str())]; 189 | return cert; 190 | } 191 | 192 | diff --git a/sdk/objc/api/peerconnection/RTCConfiguration.mm b/sdk/objc/api/peerconnection/RTCConfiguration.mm 193 | index 91151ef59c..188f413398 100644 194 | --- a/sdk/objc/api/peerconnection/RTCConfiguration.mm 195 | +++ b/sdk/objc/api/peerconnection/RTCConfiguration.mm 196 | @@ -91,7 +91,8 @@ 197 | rtc::RTCCertificatePEM native_pem = native_cert->ToPEM(); 198 | _certificate = [[RTC_OBJC_TYPE(RTCCertificate) alloc] 199 | initWithPrivateKey:@(native_pem.private_key().c_str()) 200 | - certificate:@(native_pem.certificate().c_str())]; 201 | + certificate:@(native_pem.certificate().c_str()) 202 | + fingerprint:@(native_pem.fingerprint().c_str())]; 203 | } 204 | _iceTransportPolicy = 205 | [[self class] transportPolicyForTransportsType:config.type]; 206 | @@ -247,8 +248,9 @@ 207 | RTC_LOG(LS_INFO) << "Have configured cert - using it."; 208 | std::string pem_private_key = [[_certificate private_key] UTF8String]; 209 | std::string pem_certificate = [[_certificate certificate] UTF8String]; 210 | - rtc::RTCCertificatePEM pem = 211 | - rtc::RTCCertificatePEM(pem_private_key, pem_certificate); 212 | + std::string pem_fingerprint = [[_certificate fingerprint] UTF8String]; 213 | + rtc::RTCCertificatePEM pem = rtc::RTCCertificatePEM( 214 | + pem_private_key, pem_certificate, pem_fingerprint); 215 | rtc::scoped_refptr certificate = 216 | rtc::RTCCertificate::FromPEM(pem); 217 | RTC_LOG(LS_INFO) << "Created cert from PEM strings."; 218 | -------------------------------------------------------------------------------- /patches/expose-crypto-option-aes-128-sha1-80.patch: -------------------------------------------------------------------------------- 1 | diff --git a/sdk/android/api/org/webrtc/CryptoOptions.java b/sdk/android/api/org/webrtc/CryptoOptions.java 2 | index 23f1e70f5a..d92073d387 100644 3 | --- a/sdk/android/api/org/webrtc/CryptoOptions.java 4 | +++ b/sdk/android/api/org/webrtc/CryptoOptions.java 5 | @@ -35,6 +35,11 @@ public final class CryptoOptions { 6 | * other ciphers get preferred. 7 | */ 8 | private final boolean enableAes128Sha1_32CryptoCipher; 9 | + /** 10 | + * If set to true, the crypto cipher SRTP_AES128_CM_SHA1_80 will be 11 | + * included in the list of supported ciphers during negotiation. 12 | + */ 13 | + private final boolean enableAes128Sha1_80CryptoCipher; 14 | /** 15 | * If set to true, encrypted RTP header extensions as defined in RFC 6904 16 | * will be negotiated. They will only be used if both peers support them. 17 | @@ -42,9 +47,10 @@ public final class CryptoOptions { 18 | private final boolean enableEncryptedRtpHeaderExtensions; 19 | 20 | private Srtp(boolean enableGcmCryptoSuites, boolean enableAes128Sha1_32CryptoCipher, 21 | - boolean enableEncryptedRtpHeaderExtensions) { 22 | + boolean enableAes128Sha1_80CryptoCipher, boolean enableEncryptedRtpHeaderExtensions) { 23 | this.enableGcmCryptoSuites = enableGcmCryptoSuites; 24 | this.enableAes128Sha1_32CryptoCipher = enableAes128Sha1_32CryptoCipher; 25 | + this.enableAes128Sha1_80CryptoCipher = enableAes128Sha1_80CryptoCipher; 26 | this.enableEncryptedRtpHeaderExtensions = enableEncryptedRtpHeaderExtensions; 27 | } 28 | 29 | @@ -58,6 +64,11 @@ public final class CryptoOptions { 30 | return enableAes128Sha1_32CryptoCipher; 31 | } 32 | 33 | + @CalledByNative("Srtp") 34 | + public boolean getEnableAes128Sha1_80CryptoCipher() { 35 | + return enableAes128Sha1_80CryptoCipher; 36 | + } 37 | + 38 | @CalledByNative("Srtp") 39 | public boolean getEnableEncryptedRtpHeaderExtensions() { 40 | return enableEncryptedRtpHeaderExtensions; 41 | @@ -90,9 +101,10 @@ public final class CryptoOptions { 42 | private final SFrame sframe; 43 | 44 | private CryptoOptions(boolean enableGcmCryptoSuites, boolean enableAes128Sha1_32CryptoCipher, 45 | - boolean enableEncryptedRtpHeaderExtensions, boolean requireFrameEncryption) { 46 | - this.srtp = new Srtp( 47 | - enableGcmCryptoSuites, enableAes128Sha1_32CryptoCipher, enableEncryptedRtpHeaderExtensions); 48 | + boolean enableAes128Sha1_80CryptoCipher, boolean enableEncryptedRtpHeaderExtensions, 49 | + boolean requireFrameEncryption) { 50 | + this.srtp = new Srtp(enableGcmCryptoSuites, enableAes128Sha1_32CryptoCipher, 51 | + enableAes128Sha1_80CryptoCipher, enableEncryptedRtpHeaderExtensions); 52 | this.sframe = new SFrame(requireFrameEncryption); 53 | } 54 | 55 | @@ -113,6 +125,7 @@ public final class CryptoOptions { 56 | public static class Builder { 57 | private boolean enableGcmCryptoSuites; 58 | private boolean enableAes128Sha1_32CryptoCipher; 59 | + private boolean enableAes128Sha1_80CryptoCipher; 60 | private boolean enableEncryptedRtpHeaderExtensions; 61 | private boolean requireFrameEncryption; 62 | 63 | @@ -128,6 +141,11 @@ public final class CryptoOptions { 64 | return this; 65 | } 66 | 67 | + public Builder setEnableAes128Sha1_80CryptoCipher(boolean enableAes128Sha1_80CryptoCipher) { 68 | + this.enableAes128Sha1_80CryptoCipher = enableAes128Sha1_80CryptoCipher; 69 | + return this; 70 | + } 71 | + 72 | public Builder setEnableEncryptedRtpHeaderExtensions( 73 | boolean enableEncryptedRtpHeaderExtensions) { 74 | this.enableEncryptedRtpHeaderExtensions = enableEncryptedRtpHeaderExtensions; 75 | @@ -141,7 +159,8 @@ public final class CryptoOptions { 76 | 77 | public CryptoOptions createCryptoOptions() { 78 | return new CryptoOptions(enableGcmCryptoSuites, enableAes128Sha1_32CryptoCipher, 79 | - enableEncryptedRtpHeaderExtensions, requireFrameEncryption); 80 | + enableAes128Sha1_80CryptoCipher, enableEncryptedRtpHeaderExtensions, 81 | + requireFrameEncryption); 82 | } 83 | } 84 | } 85 | diff --git a/sdk/android/src/jni/pc/crypto_options.cc b/sdk/android/src/jni/pc/crypto_options.cc 86 | index f37ebc1dcc..8d86cf1cca 100644 87 | --- a/sdk/android/src/jni/pc/crypto_options.cc 88 | +++ b/sdk/android/src/jni/pc/crypto_options.cc 89 | @@ -32,6 +32,8 @@ std::optional JavaToNativeOptionalCryptoOptions( 90 | Java_Srtp_getEnableGcmCryptoSuites(jni, j_srtp); 91 | native_crypto_options.srtp.enable_aes128_sha1_32_crypto_cipher = 92 | Java_Srtp_getEnableAes128Sha1_32CryptoCipher(jni, j_srtp); 93 | + native_crypto_options.srtp.enable_aes128_sha1_80_crypto_cipher = 94 | + Java_Srtp_getEnableAes128Sha1_80CryptoCipher(jni, j_srtp); 95 | native_crypto_options.srtp.enable_encrypted_rtp_header_extensions = 96 | Java_Srtp_getEnableEncryptedRtpHeaderExtensions(jni, j_srtp); 97 | native_crypto_options.sframe.require_frame_encryption = 98 | diff --git a/sdk/objc/api/peerconnection/RTCConfiguration.mm b/sdk/objc/api/peerconnection/RTCConfiguration.mm 99 | index 91151ef59c..4ecb39c1e9 100644 100 | --- a/sdk/objc/api/peerconnection/RTCConfiguration.mm 101 | +++ b/sdk/objc/api/peerconnection/RTCConfiguration.mm 102 | @@ -137,6 +137,8 @@ 103 | .enable_gcm_crypto_suites 104 | srtpEnableAes128Sha1_32CryptoCipher: 105 | config.crypto_options->srtp.enable_aes128_sha1_32_crypto_cipher 106 | + srtpEnableAes128Sha1_80CryptoCipher: 107 | + config.crypto_options->srtp.enable_aes128_sha1_80_crypto_cipher 108 | srtpEnableEncryptedRtpHeaderExtensions: 109 | config.crypto_options->srtp.enable_encrypted_rtp_header_extensions 110 | sframeRequireFrameEncryption:config.crypto_options->sframe 111 | diff --git a/sdk/objc/api/peerconnection/RTCCryptoOptions.h b/sdk/objc/api/peerconnection/RTCCryptoOptions.h 112 | index cb4ddd9c5d..9d5a21bf2c 100644 113 | --- a/sdk/objc/api/peerconnection/RTCCryptoOptions.h 114 | +++ b/sdk/objc/api/peerconnection/RTCCryptoOptions.h 115 | @@ -33,6 +33,11 @@ RTC_OBJC_EXPORT 116 | * other ciphers get preferred. 117 | */ 118 | @property(nonatomic, assign) BOOL srtpEnableAes128Sha1_32CryptoCipher; 119 | +/** 120 | + * If set to true, the crypto cipher SRTP_AES128_CM_SHA1_80 will be included in 121 | + * the list of supported ciphers during negotiation. 122 | + */ 123 | +@property(nonatomic, assign) BOOL srtpEnableAes128Sha1_80CryptoCipher; 124 | /** 125 | * If set to true, encrypted RTP header extensions as defined in RFC 6904 126 | * will be negotiated. They will only be used if both peers support them. 127 | @@ -54,6 +59,8 @@ RTC_OBJC_EXPORT 128 | initWithSrtpEnableGcmCryptoSuites:(BOOL)srtpEnableGcmCryptoSuites 129 | srtpEnableAes128Sha1_32CryptoCipher: 130 | (BOOL)srtpEnableAes128Sha1_32CryptoCipher 131 | + srtpEnableAes128Sha1_80CryptoCipher: 132 | + (BOOL)srtpEnableAes128Sha1_80CryptoCipher 133 | srtpEnableEncryptedRtpHeaderExtensions: 134 | (BOOL)srtpEnableEncryptedRtpHeaderExtensions 135 | sframeRequireFrameEncryption:(BOOL)sframeRequireFrameEncryption 136 | diff --git a/sdk/objc/api/peerconnection/RTCCryptoOptions.mm b/sdk/objc/api/peerconnection/RTCCryptoOptions.mm 137 | index 0b777b59e9..52acb63f12 100644 138 | --- a/sdk/objc/api/peerconnection/RTCCryptoOptions.mm 139 | +++ b/sdk/objc/api/peerconnection/RTCCryptoOptions.mm 140 | @@ -15,6 +15,8 @@ 141 | @synthesize srtpEnableGcmCryptoSuites = _srtpEnableGcmCryptoSuites; 142 | @synthesize srtpEnableAes128Sha1_32CryptoCipher = 143 | _srtpEnableAes128Sha1_32CryptoCipher; 144 | +@synthesize srtpEnableAes128Sha1_80CryptoCipher = 145 | + _srtpEnableAes128Sha1_80CryptoCipher; 146 | @synthesize srtpEnableEncryptedRtpHeaderExtensions = 147 | _srtpEnableEncryptedRtpHeaderExtensions; 148 | @synthesize sframeRequireFrameEncryption = _sframeRequireFrameEncryption; 149 | @@ -23,6 +25,8 @@ 150 | initWithSrtpEnableGcmCryptoSuites:(BOOL)srtpEnableGcmCryptoSuites 151 | srtpEnableAes128Sha1_32CryptoCipher: 152 | (BOOL)srtpEnableAes128Sha1_32CryptoCipher 153 | + srtpEnableAes128Sha1_80CryptoCipher: 154 | + (BOOL)srtpEnableAes128Sha1_80CryptoCipher 155 | srtpEnableEncryptedRtpHeaderExtensions: 156 | (BOOL)srtpEnableEncryptedRtpHeaderExtensions 157 | sframeRequireFrameEncryption:(BOOL)sframeRequireFrameEncryption { 158 | @@ -30,6 +34,7 @@ 159 | if (self) { 160 | _srtpEnableGcmCryptoSuites = srtpEnableGcmCryptoSuites; 161 | _srtpEnableAes128Sha1_32CryptoCipher = srtpEnableAes128Sha1_32CryptoCipher; 162 | + _srtpEnableAes128Sha1_80CryptoCipher = srtpEnableAes128Sha1_80CryptoCipher; 163 | _srtpEnableEncryptedRtpHeaderExtensions = 164 | srtpEnableEncryptedRtpHeaderExtensions; 165 | _sframeRequireFrameEncryption = sframeRequireFrameEncryption; 166 | -------------------------------------------------------------------------------- /patches/expose-video-capturer-state.patch: -------------------------------------------------------------------------------- 1 | diff --git a/sdk/android/api/org/webrtc/FileVideoCapturer.java b/sdk/android/api/org/webrtc/FileVideoCapturer.java 2 | index 408d8b55fc..2cddd30a2a 100644 3 | --- a/sdk/android/api/org/webrtc/FileVideoCapturer.java 4 | +++ b/sdk/android/api/org/webrtc/FileVideoCapturer.java 5 | @@ -141,6 +141,7 @@ public class FileVideoCapturer implements VideoCapturer { 6 | private final VideoReader videoReader; 7 | private CapturerObserver capturerObserver; 8 | private final Timer timer = new Timer(); 9 | + private boolean isCapturing; 10 | 11 | private final TimerTask tickTask = new TimerTask() { 12 | @Override 13 | @@ -173,11 +174,13 @@ public class FileVideoCapturer implements VideoCapturer { 14 | @Override 15 | public void startCapture(int width, int height, int framerate) { 16 | timer.schedule(tickTask, 0, 1000 / framerate); 17 | + isCapturing = true; 18 | } 19 | 20 | @Override 21 | public void stopCapture() throws InterruptedException { 22 | timer.cancel(); 23 | + isCapturing = false; 24 | } 25 | 26 | @Override 27 | @@ -185,6 +188,11 @@ public class FileVideoCapturer implements VideoCapturer { 28 | // Empty on purpose 29 | } 30 | 31 | + @Override 32 | + public boolean isCapturing() { 33 | + return isCapturing; 34 | + } 35 | + 36 | @Override 37 | public void dispose() { 38 | videoReader.close(); 39 | diff --git a/sdk/android/api/org/webrtc/ScreenCapturerAndroid.java b/sdk/android/api/org/webrtc/ScreenCapturerAndroid.java 40 | index 08b03bd684..2855b17251 100644 41 | --- a/sdk/android/api/org/webrtc/ScreenCapturerAndroid.java 42 | +++ b/sdk/android/api/org/webrtc/ScreenCapturerAndroid.java 43 | @@ -217,6 +217,12 @@ public class ScreenCapturerAndroid implements VideoCapturer, VideoSink { 44 | return true; 45 | } 46 | 47 | + @Override 48 | + public boolean isCapturing() { 49 | + // The virtual display is created in startCapture() and set to null in stopCapture() 50 | + return virtualDisplay != null; 51 | + } 52 | + 53 | public long getNumCapturedFrames() { 54 | return numCapturedFrames; 55 | } 56 | diff --git a/sdk/android/api/org/webrtc/VideoCapturer.java b/sdk/android/api/org/webrtc/VideoCapturer.java 57 | index 67eb7ab086..5425657df0 100644 58 | --- a/sdk/android/api/org/webrtc/VideoCapturer.java 59 | +++ b/sdk/android/api/org/webrtc/VideoCapturer.java 60 | @@ -41,6 +41,11 @@ public interface VideoCapturer { 61 | 62 | void changeCaptureFormat(int width, int height, int framerate); 63 | 64 | + /** 65 | + * Return whether capturing is currently active. 66 | + */ 67 | + boolean isCapturing(); 68 | + 69 | /** 70 | * Perform any final cleanup here. No more capturing will be done after this call. 71 | */ 72 | diff --git a/sdk/android/src/java/org/webrtc/CameraCapturer.java b/sdk/android/src/java/org/webrtc/CameraCapturer.java 73 | index 1922a529e2..2375fba0e4 100644 74 | --- a/sdk/android/src/java/org/webrtc/CameraCapturer.java 75 | +++ b/sdk/android/src/java/org/webrtc/CameraCapturer.java 76 | @@ -312,6 +312,13 @@ abstract class CameraCapturer implements CameraVideoCapturer { 77 | Logging.d(TAG, "Stop capture done"); 78 | } 79 | 80 | + @Override 81 | + public boolean isCapturing() { 82 | + synchronized (stateLock) { 83 | + return sessionOpening || currentSession != null; 84 | + } 85 | + } 86 | + 87 | @Override 88 | public void changeCaptureFormat(int width, int height, int framerate) { 89 | Logging.d(TAG, "changeCaptureFormat: " + width + "x" + height + "@" + framerate); 90 | -------------------------------------------------------------------------------- /patches/group-call-frame-crypto.patch: -------------------------------------------------------------------------------- 1 | diff --git a/pc/BUILD.gn b/pc/BUILD.gn 2 | index d46ff16d72..786e601fdc 100644 3 | --- a/pc/BUILD.gn 4 | +++ b/pc/BUILD.gn 5 | @@ -1239,6 +1239,26 @@ rtc_source_set("peer_connection") { 6 | ] 7 | } 8 | 9 | +rtc_source_set("threema_group_call_frame_transformer") { 10 | + visibility = [ "*" ] 11 | + sources = [ 12 | + "threema_group_call_frame_transformer.cc", 13 | + "threema_group_call_frame_transformer.h", 14 | + ] 15 | + deps = [ 16 | + "../api:frame_transformer_interface", 17 | + "../api:make_ref_counted", 18 | + "../api:scoped_refptr", 19 | + "../rtc_base:byte_order", 20 | + "../rtc_base:checks", 21 | + "../rtc_base:logging", 22 | + "../rtc_base/synchronization:mutex", 23 | + "../rtc_base/third_party/threema-blake2b", 24 | + "//third_party/abseil-cpp/absl/types:optional", 25 | + "//third_party/boringssl", 26 | + ] 27 | +} 28 | + 29 | rtc_source_set("simulcast_sdp_serializer") { 30 | visibility = [ ":*" ] 31 | sources = [ 32 | diff --git a/pc/threema_group_call_frame_transformer.cc b/pc/threema_group_call_frame_transformer.cc 33 | new file mode 100644 34 | index 0000000000..923b8f5933 35 | --- /dev/null 36 | +++ b/pc/threema_group_call_frame_transformer.cc 37 | @@ -0,0 +1,820 @@ 38 | +/* 39 | + * Copyright 2022 Threema GmbH. All Rights Reserved. 40 | + * 41 | + * Use of this source code is governed by a BSD-style license 42 | + * that can be found in the LICENSE file in the root of the source 43 | + * tree. 44 | + */ 45 | + 46 | +#include "pc/threema_group_call_frame_transformer.h" 47 | + 48 | +// We're using BoringSSL which gives us a tad nicer API surface, see: 49 | +// https://commondatastorage.googleapis.com/chromium-boringssl-docs/headers.html 50 | +#include 51 | +#include 52 | + 53 | +#include "absl/types/optional.h" 54 | +#include "api/frame_transformer_interface.h" 55 | +#include "api/make_ref_counted.h" 56 | +#include "api/scoped_refptr.h" 57 | +#include "rtc_base/byte_order.h" 58 | +#include "rtc_base/checks.h" 59 | +#include "rtc_base/logging.h" 60 | +#include "rtc_base/synchronization/mutex.h" 61 | +#include "rtc_base/third_party/threema-blake2b/blake2b.h" 62 | + 63 | +namespace threema { 64 | + 65 | +uint8_t const hash_personal[BLAKE2B_PERSONALBYTES] = { 66 | + // '3ma-call', followed by 8 zeroes 67 | + 0x33, 0x6d, 0x61, 0x2d, 0x63, 0x61, 0x6c, 0x6c, 68 | + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 69 | +}; 70 | +uint8_t const hash_next_pcmk_salt[BLAKE2B_SALTBYTES] = { 71 | + // "m'", followed by 14 zeroes 72 | + 0x6d, 0x27, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 73 | + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 74 | +}; 75 | +uint8_t const hash_pcmfk_salt[BLAKE2B_SALTBYTES] = { 76 | + // 'mf', followed by 14 zeroes 77 | + 0x6d, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 78 | + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 79 | +}; 80 | + 81 | +static void derive_next_pcmk(uint8_t pcmk[kKeyLength]) { 82 | + // PCMK' = BLAKE2b(key=PCMK, salt="m'", personal='3ma-call') 83 | + blake2b_state state = {{0}}; 84 | + RTC_CHECK(blake2b_init_with_params(&state, kKeyLength, pcmk, kKeyLength, 85 | + hash_next_pcmk_salt, hash_personal) == 0); 86 | + RTC_CHECK(blake2b_final(&state, pcmk, kKeyLength) == 0); 87 | +} 88 | + 89 | +static void derive_pcmfk(uint8_t const gckh[kKeyLength], 90 | + uint8_t const pcmk[kKeyLength], 91 | + uint8_t pcmfk[kKeyLength]) { 92 | + // PCMFK = BLAKE2b(key=PCMK, salt='mf', personal='3ma-call', input=GCKH) 93 | + blake2b_state state = {{0}}; 94 | + RTC_CHECK(blake2b_init_with_params(&state, kKeyLength, pcmk, kKeyLength, 95 | + hash_pcmfk_salt, hash_personal) == 0); 96 | + RTC_CHECK(blake2b_update(&state, gckh, kKeyLength) == 0); 97 | + RTC_CHECK(blake2b_final(&state, pcmfk, kKeyLength) == 0); 98 | +} 99 | + 100 | +static bool inline setup_aead_ctx( 101 | + GroupCallFrameCryptoAeadCtxState& aead_ctx_state, 102 | + EVP_AEAD const* const cipher, 103 | + GroupCallFrameCryptoKeyState const* const target) { 104 | + if (!aead_ctx_state.initialized || aead_ctx_state.epoch != target->epoch || 105 | + aead_ctx_state.ratchet_counter != target->ratchet_counter) { 106 | + RTC_DCHECK(!aead_ctx_state.initialized || 107 | + ((target->epoch == 0 && aead_ctx_state.epoch == 255) || 108 | + target->epoch > aead_ctx_state.epoch || 109 | + (target->epoch == aead_ctx_state.epoch && 110 | + target->ratchet_counter > aead_ctx_state.ratchet_counter))); 111 | + if (!EVP_AEAD_CTX_init(&aead_ctx_state.aead_ctx, cipher, target->pcmfk, 112 | + kKeyLength, kTagLength, nullptr)) { 113 | + RTC_LOG(LS_ERROR) << "Failed to initialise AEAD context"; 114 | + return false; 115 | + } 116 | + aead_ctx_state.initialized = true; 117 | + aead_ctx_state.epoch = target->epoch; 118 | + aead_ctx_state.ratchet_counter = target->ratchet_counter; 119 | + } 120 | + return true; 121 | +} 122 | + 123 | +/* 124 | + * #region GroupCallFrameEncryptorKeyContext 125 | + */ 126 | +GroupCallFrameEncryptorKeyContext::GroupCallFrameEncryptorKeyContext( 127 | + uint8_t const gckh[kKeyLength]) 128 | + : state({0}) { 129 | + std::memcpy(this->gckh, gckh, kKeyLength); 130 | +} 131 | + 132 | +void GroupCallFrameEncryptorKeyContext::SetPcmk(uint8_t const pcmk[kKeyLength], 133 | + uint8_t const epoch, 134 | + uint8_t const ratchet_counter) { 135 | + webrtc::MutexLock lock(&mutex); 136 | + 137 | + // Note: We intentionally do **not** reset the MFSN since it could 138 | + // result in nonce reuse if we have a bug somewhere in this code! 139 | + 140 | + // Ensure we're not reusing an epoch or ratchet counter within an epoch. 141 | + // This would otherwise lead to nonce reuse. 142 | + if (initialized) { 143 | + RTC_CHECK( 144 | + (epoch == 0 && state.epoch == 255) || epoch > state.epoch || 145 | + (epoch == state.epoch && ratchet_counter > state.ratchet_counter)); 146 | + } 147 | + 148 | + // Update state 149 | + initialized = true; 150 | + std::memcpy(&state.pcmk, pcmk, kKeyLength); 151 | + derive_pcmfk(gckh, pcmk, state.pcmfk); 152 | + state.epoch = epoch; 153 | + state.ratchet_counter = ratchet_counter; 154 | + RTC_LOG(LS_INFO) << "Applied new PCMK (epoch=" << epoch 155 | + << ", ratchet-counter=" << ratchet_counter << ")"; 156 | +} 157 | + 158 | +absl::optional 159 | +GroupCallFrameEncryptorKeyContext::Apply( 160 | + GroupCallFrameCryptoAeadCtxState& aead_ctx_state, 161 | + EVP_AEAD const* const cipher) { 162 | + webrtc::MutexLock lock(&mutex); 163 | + 164 | + // Ensure the context is initialised 165 | + if (!initialized) { 166 | + RTC_LOG(LS_ERROR) << "Encryptor context not initialized"; 167 | + return absl::nullopt; 168 | + } 169 | + 170 | + // Get and increase MFSN. 171 | + // 172 | + // Note: While technically feasible to reach 2^32 - 1, it approximately 173 | + // takes multiple days. Hopefully no one is that insane. 174 | + RTC_CHECK(state.mfsn < UINT32_MAX); 175 | + GroupCallFrameCryptoKeyStateSnapshot snapshot = { 176 | + .mfsn_le = htole32(static_cast(state.mfsn++)), 177 | + .epoch = state.epoch, 178 | + .ratchet_counter = state.ratchet_counter, 179 | + }; 180 | + 181 | + // Setup AEAD context and apply the PCMFK, if necessary 182 | + if (!setup_aead_ctx(aead_ctx_state, cipher, &state)) { 183 | + return absl::nullopt; 184 | + } 185 | + 186 | + // Return the current key state snapshot 187 | + return snapshot; 188 | +} 189 | + 190 | +/* 191 | + * #region GroupCallFrameDecryptorKeysContext 192 | + */ 193 | +GroupCallFrameDecryptorKeysContext::GroupCallFrameDecryptorKeysContext( 194 | + uint8_t const gckh[kKeyLength]) { 195 | + std::memcpy(this->gckh, gckh, kKeyLength); 196 | +} 197 | + 198 | +void GroupCallFrameDecryptorKeysContext::AddPcmk( 199 | + uint8_t const pcmk[kKeyLength], 200 | + uint8_t const epoch, 201 | + uint8_t const ratchet_counter) { 202 | + webrtc::MutexLock lock(&mutex); 203 | + 204 | + // Ensure we're not adding an old/existing epoch (with the exception of 205 | + // wrapping) 206 | + if (!states.empty()) { 207 | + auto newest_epoch = states.back().epoch; 208 | + if (!(epoch == 0 && newest_epoch == 255) && epoch < newest_epoch) { 209 | + RTC_LOG(LS_WARNING) 210 | + << "Discarding PCMK that would downgrade the epoch (newest=" 211 | + << newest_epoch << ", provided=" << epoch << ")"; 212 | + return; 213 | + } 214 | + } 215 | + 216 | + // Add state 217 | + GroupCallFrameCryptoKeyState state = { 218 | + .mfsn = 0, 219 | + .pcmk = {0}, 220 | + .pcmfk = {0}, 221 | + .epoch = epoch, 222 | + .ratchet_counter = ratchet_counter, 223 | + }; 224 | + std::memcpy(&state.pcmk, pcmk, kKeyLength); 225 | + derive_pcmfk(gckh, pcmk, state.pcmfk); 226 | + states.push_back(state); 227 | + RTC_LOG(LS_INFO) << "Added new PCMK (epoch=" << epoch 228 | + << ", ratchet-counter=" << ratchet_counter << ")"; 229 | +} 230 | + 231 | +bool GroupCallFrameDecryptorKeysContext::Apply( 232 | + GroupCallFrameCryptoAeadCtxState& aead_ctx_state, 233 | + EVP_AEAD const* const cipher, 234 | + GroupCallFrameCryptoKeyStateSnapshot const& target) { 235 | + webrtc::MutexLock lock(&mutex); 236 | + 237 | + // Align the decryptor state 238 | + auto const maybe_state = AlignWith(target); 239 | + if (!maybe_state.has_value()) { 240 | + return false; 241 | + } 242 | + auto const* const state = maybe_state.value(); 243 | + 244 | + // Setup AEAD context and apply the PCMFK, if necessary 245 | + return setup_aead_ctx(aead_ctx_state, cipher, state); 246 | +} 247 | + 248 | +absl::optional 249 | +GroupCallFrameDecryptorKeysContext::AlignWith( 250 | + GroupCallFrameCryptoKeyStateSnapshot const& target) { 251 | + // Lookup the decryptor state by epoch and purge all contexts of older 252 | + // epochs 253 | + for (;;) { 254 | + auto const state = states.begin(); 255 | + 256 | + // Check if any states are left 257 | + if (state == states.end()) { 258 | + RTC_LOG(LS_WARNING) << "No decryptor context available"; 259 | + return absl::nullopt; 260 | + } 261 | + 262 | + // Same epoch: Align the ratchet counter. 263 | + if (target.epoch == state->epoch) { 264 | + // Same ratchet counter: No-op. 265 | + if (target.ratchet_counter == state->ratchet_counter) { 266 | + return &*state; 267 | + } 268 | + 269 | + // Old rachet counter: Discard. 270 | + if (target.ratchet_counter < state->ratchet_counter) { 271 | + RTC_LOG(LS_WARNING) << "No decryptor context for old ratchet counter " 272 | + << target.ratchet_counter << " available"; 273 | + return absl::nullopt; 274 | + } 275 | + 276 | + // Newer ratchet counter: Apply the ratchet until the counter matches. 277 | + for (; target.ratchet_counter != state->ratchet_counter; 278 | + ++state->ratchet_counter) { 279 | + RTC_LOG(LS_INFO) << "Discarding decryptor state (ratchet counter " 280 | + << state->ratchet_counter << " < " 281 | + << target.ratchet_counter << ")"; 282 | + derive_next_pcmk(state->pcmk); 283 | + derive_pcmfk(gckh, state->pcmk, state->pcmfk); 284 | + } 285 | + return &*state; 286 | + } 287 | + 288 | + // Older epoch: Discard. 289 | + // 290 | + // Note: Epoch `0` is handled specially because the epoch is allowed to 291 | + // wrap. Since different media frames may race with each other, we 292 | + // apply a heuristic to prevent accidental wrapping back from epoch 293 | + // `1` to `0`. Otherwise, we just seek until we find epoch `0` again. 294 | + if ((target.epoch == 0 && state->epoch < 128) || 295 | + (target.epoch != 0 && target.epoch < state->epoch)) { 296 | + RTC_LOG(LS_WARNING) << "No decryptor context for old epoch " 297 | + << target.epoch << " available"; 298 | + return absl::nullopt; 299 | + } 300 | + 301 | + // Newer epoch: Drop the current state and continue searching. 302 | + RTC_LOG(LS_INFO) << "Discarding decryptor state (epoch " << state->epoch 303 | + << " < " << target.epoch << ")"; 304 | + states.erase(state); 305 | + } 306 | +} 307 | + 308 | +/* 309 | + * #region GroupCallFrameEncryptor 310 | + */ 311 | +GroupCallFrameEncryptor::GroupCallFrameEncryptor( 312 | + std::string const tag, 313 | + Codec const codec, 314 | + webrtc::scoped_refptr const key_ctx) 315 | + : tag(tag), 316 | + codec(codec), 317 | + cipher(EVP_aead_aes_256_gcm()), 318 | + key_ctx(std::move(key_ctx)), 319 | + aead_ctx_state() { 320 | + // Do some sanity checks 321 | + RTC_CHECK(EVP_AEAD_key_length(cipher) == kKeyLength); 322 | + RTC_CHECK(EVP_AEAD_nonce_length(cipher) == kNonceLength); 323 | + RTC_CHECK(EVP_AEAD_max_tag_len(cipher) == kTagLength); 324 | +} 325 | + 326 | +GroupCallFrameEncryptor::~GroupCallFrameEncryptor() { 327 | + EVP_AEAD_CTX_zero(&aead_ctx_state.aead_ctx); 328 | + RTC_DLOG(LS_VERBOSE) << tag << " Encryptor::Destroyed"; 329 | +} 330 | + 331 | +void GroupCallFrameEncryptor::Transform( 332 | + std::unique_ptr frame) { 333 | + // Lookup frame dispatcher by SSRC 334 | + uint32_t const ssrc = frame->GetSsrc(); 335 | + auto const* const frame_dispatcher = GetFrameDispatcher(ssrc); 336 | + if (frame_dispatcher == nullptr) { 337 | + RTC_LOG(LS_ERROR) << tag << " No frame dispatcher registered for SSRC " 338 | + << ssrc; 339 | + return; 340 | + } 341 | + auto const unencrypted = frame->GetData(); 342 | + RTC_DLOG(LS_VERBOSE) << tag 343 | + << " Unencrypted frame (length=" << unencrypted.size() 344 | + << ", ssrc=" << ssrc << ")"; 345 | + 346 | + // Check frame length 347 | + if (unencrypted.size() == 0) { 348 | + if (codec == Codec::kOpus) { 349 | + // Opus DTX generates empty frames for discontinued transmission (i.e. 350 | + // silence) 351 | + frame_dispatcher->get()->OnTransformedFrame(std::move(frame)); 352 | + } else { 353 | + RTC_LOG(LS_ERROR) << tag << " Discarding unencrypted frame of 0 bytes"; 354 | + } 355 | + return; 356 | + } 357 | + if (unencrypted.size() > kMaxUnencryptedFrameLength) { 358 | + RTC_LOG(LS_ERROR) << tag << " Discarding unencrypted frame exceeding " 359 | + << kMaxUnencryptedFrameLength << " bytes"; 360 | + return; 361 | + } 362 | + 363 | + // For VP8, extract the header that should remain unencrypted 364 | + size_t unencrypted_header_length = 0; 365 | + if (codec == Codec::kVp8) { 366 | + if (unencrypted.size() < 3) { 367 | + RTC_LOG(LS_ERROR) << tag 368 | + << " Discarding invalid VP8 payload, byte length < 3"; 369 | + return; 370 | + } 371 | + bool const isKeyFrame = (unencrypted[0] & 0x01) == 0; 372 | + if (isKeyFrame) { 373 | + RTC_DLOG(LS_VERBOSE) << tag << " Got a VP8 keyframe"; 374 | + if (unencrypted.size() < 10) { 375 | + RTC_LOG(LS_ERROR) 376 | + << tag 377 | + << " Discarding invalid VP8 payload key frame, byte length < 10"; 378 | + return; 379 | + } 380 | + unencrypted_header_length = kVp8KeyFrameLength; 381 | + } else { 382 | + unencrypted_header_length = kVp8DeltaFrameLength; 383 | + } 384 | + } 385 | + 386 | + // Apply key state. This sets up the AEAD context with the PCMFK. 387 | + auto snapshot = key_ctx->Apply(aead_ctx_state, cipher); 388 | + if (!snapshot.has_value()) { 389 | + RTC_LOG(LS_WARNING) << tag 390 | + << " Discarding frame because the key state was not " 391 | + "provided (or failed to apply)"; 392 | + return; 393 | + } 394 | + 395 | + // Encode nonce 396 | + uint8_t nonce[kNonceLength] = {0}; 397 | + std::memcpy(nonce, &snapshot->mfsn_le, sizeof(snapshot->mfsn_le)); 398 | + 399 | + // Encode additional data 400 | + size_t const additional_data_length = 401 | + kBaseAdditionalDataLength + unencrypted_header_length; 402 | + uint8_t additional_data[kMaxAdditionalDataLength]; 403 | + additional_data[0] = snapshot->epoch; 404 | + additional_data[1] = snapshot->ratchet_counter; 405 | + std::memcpy(additional_data + 2, &snapshot->mfsn_le, 406 | + sizeof(snapshot->mfsn_le)); 407 | + static_assert(sizeof(snapshot->mfsn_le) + 2 == kBaseAdditionalDataLength, ""); 408 | + std::memcpy(additional_data + kBaseAdditionalDataLength, unencrypted.data(), 409 | + unencrypted_header_length); 410 | + 411 | + // Prepare the encoded output buffer 412 | + size_t const unencrypted_payload_length = 413 | + unencrypted.size() - unencrypted_header_length; 414 | + size_t const encrypted_payload_length = 415 | + unencrypted_payload_length + kTagLength; 416 | + std::vector encoded; 417 | + size_t const encoded_length = unencrypted_header_length + 418 | + encrypted_payload_length + 419 | + kBaseAdditionalDataLength; 420 | + encoded.reserve(encoded_length); 421 | + 422 | + // Write unencrypted header 423 | + std::copy(unencrypted.data(), unencrypted.data() + unencrypted_header_length, 424 | + std::back_inserter(encoded)); 425 | + 426 | + // Encrypt and write encrypted payload, including the 16 byte tag 427 | + { 428 | + uint8_t* const encrypted_buffer = encoded.data() + encoded.size(); 429 | + encoded.resize(encoded.size() + encrypted_payload_length); 430 | + size_t actual_encrypted_payload_length; 431 | + if (!EVP_AEAD_CTX_seal(&aead_ctx_state.aead_ctx, encrypted_buffer, 432 | + &actual_encrypted_payload_length, 433 | + encrypted_payload_length, nonce, kNonceLength, 434 | + unencrypted.data() + unencrypted_header_length, 435 | + unencrypted_payload_length, additional_data, 436 | + additional_data_length)) { 437 | + RTC_LOG(LS_ERROR) << tag << " Unable to encrypt data"; 438 | + return; 439 | + } 440 | + if (actual_encrypted_payload_length != encrypted_payload_length) { 441 | + RTC_LOG(LS_ERROR) << tag 442 | + << " Unexpected amount of bytes encrypted (expected=" 443 | + << encrypted_payload_length 444 | + << ", actual=" << actual_encrypted_payload_length 445 | + << ")"; 446 | + return; 447 | + } 448 | + } 449 | + 450 | + // Write the 6 byte footer 451 | + std::copy(additional_data, additional_data + kBaseAdditionalDataLength, 452 | + std::back_inserter(encoded)); 453 | + 454 | + // Update frame data 455 | + RTC_CHECK(encoded.size() == encoded_length); 456 | + RTC_DLOG(LS_VERBOSE) << tag << " Encrypted frame (mfsn=" 457 | + << le32toh(snapshot->mfsn_le) 458 | + << ", length=" << unencrypted.size() << " -> " 459 | + << encoded.size() << ", ssrc=" << ssrc << ")"; 460 | + frame->SetData(rtc::ArrayView(encoded.data(), encoded.size())); 461 | + frame_dispatcher->get()->OnTransformedFrame(std::move(frame)); 462 | +} 463 | + 464 | +void GroupCallFrameEncryptor::RegisterTransformedFrameCallback( 465 | + webrtc::scoped_refptr const dispatcher) { 466 | + RTC_DCHECK(frame_dispatchers.size() == 0); 467 | + 468 | + // Add non-SSRC based dispatcher (deprecated) 469 | + RTC_LOG(LS_VERBOSE) 470 | + << tag << " Registering legacy frame dispatcher for unknown SSRC"; 471 | + frame_dispatcher = std::move(dispatcher); 472 | +} 473 | + 474 | +void GroupCallFrameEncryptor::RegisterTransformedFrameSinkCallback( 475 | + webrtc::scoped_refptr const dispatcher, 476 | + uint32_t const ssrc) { 477 | + RTC_DCHECK(frame_dispatcher == nullptr); 478 | + 479 | + // Replace any existing dispatcher for this SSRC 480 | + for (auto& existing_frame_dispatcher : frame_dispatchers) { 481 | + if (existing_frame_dispatcher.first == ssrc) { 482 | + RTC_LOG(LS_WARNING) << tag 483 | + << " Replacing existing frame dispatcher for SSRC " 484 | + << ssrc; 485 | + existing_frame_dispatcher.second = std::move(dispatcher); 486 | + return; 487 | + } 488 | + } 489 | + 490 | + // No existing dispatcher found for this SSRC, add a new one 491 | + RTC_LOG(LS_VERBOSE) << tag << " Registering frame dispatcher for SSRC " 492 | + << ssrc; 493 | + frame_dispatchers.push_back(std::make_pair(ssrc, std::move(dispatcher))); 494 | +} 495 | + 496 | +void GroupCallFrameEncryptor::UnregisterTransformedFrameCallback() { 497 | + RTC_LOG(LS_VERBOSE) 498 | + << tag << " Unregistering legacy frame dispatcher for unknown SSRC"; 499 | + frame_dispatcher = nullptr; 500 | +} 501 | + 502 | +void GroupCallFrameEncryptor::UnregisterTransformedFrameSinkCallback( 503 | + uint32_t const ssrc) { 504 | + frame_dispatchers.erase( 505 | + std::remove_if(frame_dispatchers.begin(), frame_dispatchers.end(), 506 | + [=, this](auto& frame_dispatcher) { 507 | + if (frame_dispatcher.first == ssrc) { 508 | + RTC_LOG(LS_VERBOSE) 509 | + << tag 510 | + << " Unregistering frame dispatcher for SSRC " 511 | + << ssrc; 512 | + return true; 513 | + } else { 514 | + return false; 515 | + } 516 | + }), 517 | + frame_dispatchers.end()); 518 | +} 519 | + 520 | +webrtc::scoped_refptr* 521 | +GroupCallFrameEncryptor::GetFrameDispatcher(uint32_t const ssrc) { 522 | + // Deprecated 523 | + if (frame_dispatcher != nullptr) { 524 | + return &frame_dispatcher; 525 | + } 526 | + 527 | + // Lookup frame dispatcher 528 | + for (auto& frame_dispatcher : frame_dispatchers) { 529 | + if (frame_dispatcher.first == ssrc) { 530 | + return &frame_dispatcher.second; 531 | + } 532 | + } 533 | + 534 | + // None found 535 | + return nullptr; 536 | +} 537 | + 538 | +/* 539 | + * #region GroupCallFrameDecryptor 540 | + */ 541 | +GroupCallFrameDecryptor::GroupCallFrameDecryptor( 542 | + std::string const tag, 543 | + Codec const codec, 544 | + webrtc::scoped_refptr const keys_ctx) 545 | + : tag(tag), 546 | + codec(codec), 547 | + cipher(EVP_aead_aes_256_gcm()), 548 | + keys_ctx(std::move(keys_ctx)), 549 | + aead_ctx_state() { 550 | + // Do some sanity checks 551 | + RTC_CHECK(EVP_AEAD_key_length(cipher) == kKeyLength); 552 | + RTC_CHECK(EVP_AEAD_nonce_length(cipher) == kNonceLength); 553 | + RTC_CHECK(EVP_AEAD_max_tag_len(cipher) == kTagLength); 554 | +} 555 | + 556 | +GroupCallFrameDecryptor::~GroupCallFrameDecryptor() { 557 | + EVP_AEAD_CTX_zero(&aead_ctx_state.aead_ctx); 558 | + RTC_DLOG(LS_VERBOSE) << tag << " Decryptor::Destroyed"; 559 | +} 560 | + 561 | +void GroupCallFrameDecryptor::Transform( 562 | + std::unique_ptr frame) { 563 | + // Lookup frame dispatcher by SSRC 564 | + uint32_t const ssrc = frame->GetSsrc(); 565 | + auto const* const frame_dispatcher = GetFrameDispatcher(ssrc); 566 | + if (frame_dispatcher == nullptr) { 567 | + RTC_LOG(LS_ERROR) << tag << " No frame dispatcher registered for SSRC " 568 | + << ssrc; 569 | + return; 570 | + } 571 | + auto const encrypted = frame->GetData(); 572 | + RTC_DLOG(LS_VERBOSE) << tag << " Encrypted frame (length=" << encrypted.size() 573 | + << ", ssrc=" << ssrc << ")"; 574 | + 575 | + // Check full frame length 576 | + if (encrypted.size() == 0) { 577 | + if (codec == Codec::kOpus) { 578 | + // Opus DTX generates empty frames for discontinued transmission (i.e. 579 | + // silence) 580 | + frame_dispatcher->get()->OnTransformedFrame(std::move(frame)); 581 | + } else { 582 | + RTC_LOG(LS_ERROR) << tag << " Discarding encrypted frame of 0 bytes"; 583 | + } 584 | + return; 585 | + } 586 | + if (encrypted.size() > kMaxEncryptedFrameLength) { 587 | + RTC_LOG(LS_WARNING) << tag << " Discarding encrypted frame exceeding " 588 | + << kMaxEncryptedFrameLength << " bytes"; 589 | + return; 590 | + } 591 | + 592 | + // For VP8, extract the header that remained unencrypted 593 | + if (encrypted.size() < 1) { 594 | + RTC_LOG(LS_WARNING) 595 | + << tag << " Discarding encrypted frame containing less than 1 byte"; 596 | + return; 597 | + } 598 | + size_t unencrypted_header_length = 0; 599 | + if (codec == Codec::kVp8) { 600 | + bool const isKeyFrame = (encrypted[0] & 0x01) == 0; 601 | + if (isKeyFrame) { 602 | + RTC_DLOG(LS_VERBOSE) << tag << " Got a VP8 keyframe"; 603 | + unencrypted_header_length = kVp8KeyFrameLength; 604 | + } else { 605 | + unencrypted_header_length = kVp8DeltaFrameLength; 606 | + } 607 | + } 608 | + 609 | + // Check encrypted frame length 610 | + if (encrypted.size() - unencrypted_header_length < 611 | + kTagLength + kBaseAdditionalDataLength) { 612 | + RTC_LOG(LS_WARNING) 613 | + << tag 614 | + << " Discarding invalid frame, does not contain the 6 byte footer"; 615 | + return; 616 | + } 617 | + 618 | + // Extract footer and apply key state. This sets up the AEAD context with the 619 | + // PCMFK. 620 | + // 621 | + // Note: We're not doing sequence number counting when decrypting since 622 | + // reordering may happen and replay is deemed somewhat acceptable here. 623 | + uint8_t const* const footer = 624 | + encrypted.data() + (encrypted.size() - kBaseAdditionalDataLength); 625 | + GroupCallFrameCryptoKeyStateSnapshot snapshot = { 626 | + .mfsn_le = 0, 627 | + .epoch = footer[0], 628 | + .ratchet_counter = footer[1], 629 | + }; 630 | + std::memcpy(&snapshot.mfsn_le, footer + 2, sizeof(snapshot.mfsn_le)); 631 | + if (!keys_ctx->Apply(aead_ctx_state, cipher, snapshot)) { 632 | + RTC_LOG(LS_WARNING) << tag 633 | + << " Discarding frame because the key state was not " 634 | + "provided (or failed to apply)"; 635 | + return; 636 | + } 637 | + 638 | + // Encode nonce 639 | + uint8_t nonce[kNonceLength] = {0}; 640 | + std::memcpy(nonce, &snapshot.mfsn_le, sizeof(snapshot.mfsn_le)); 641 | + 642 | + // Encode additional data 643 | + size_t const additional_data_length = 644 | + kBaseAdditionalDataLength + unencrypted_header_length; 645 | + uint8_t additional_data[kMaxAdditionalDataLength]; 646 | + std::memcpy(additional_data, footer, kBaseAdditionalDataLength); 647 | + std::memcpy(additional_data + kBaseAdditionalDataLength, encrypted.data(), 648 | + unencrypted_header_length); 649 | + 650 | + // Prepare the decoded output buffer 651 | + size_t const encrypted_payload_length = 652 | + encrypted.size() - unencrypted_header_length - kBaseAdditionalDataLength; 653 | + size_t const decrypted_payload_length = encrypted_payload_length - kTagLength; 654 | + std::vector decoded; 655 | + size_t const decoded_length = 656 | + unencrypted_header_length + decrypted_payload_length; 657 | + decoded.reserve(decoded_length); 658 | + 659 | + // Write unencrypted header 660 | + std::copy(encrypted.data(), encrypted.data() + unencrypted_header_length, 661 | + std::back_inserter(decoded)); 662 | + 663 | + // Decrypt the encrypted payload, including the 16 byte tag 664 | + { 665 | + uint8_t* const decrypted_buffer = decoded.data() + decoded.size(); 666 | + decoded.resize(decoded.size() + decrypted_payload_length); 667 | + size_t actual_decrypted_payload_length; 668 | + if (!EVP_AEAD_CTX_open(&aead_ctx_state.aead_ctx, decrypted_buffer, 669 | + &actual_decrypted_payload_length, 670 | + decrypted_payload_length, nonce, kNonceLength, 671 | + encrypted.data() + unencrypted_header_length, 672 | + encrypted_payload_length, additional_data, 673 | + additional_data_length)) { 674 | + RTC_LOG(LS_WARNING) << tag << " Unable to decrypt data"; 675 | + return; 676 | + } 677 | + if (actual_decrypted_payload_length != decrypted_payload_length) { 678 | + RTC_LOG(LS_ERROR) << tag 679 | + << " Unexpected amount of bytes decrypted (expected=" 680 | + << decrypted_payload_length 681 | + << ", actual=" << actual_decrypted_payload_length 682 | + << ")"; 683 | + return; 684 | + } 685 | + } 686 | + 687 | + // Update frame data 688 | + RTC_CHECK(decoded.size() == decoded_length); 689 | + RTC_DLOG(LS_VERBOSE) << tag 690 | + << " Decrypted frame (mfsn=" << le32toh(snapshot.mfsn_le) 691 | + << ", length=" << encrypted.size() << " -> " 692 | + << decoded.size() << ", ssrc=" << ssrc << ")"; 693 | + frame->SetData(rtc::ArrayView(decoded.data(), decoded.size())); 694 | + frame_dispatcher->get()->OnTransformedFrame(std::move(frame)); 695 | +} 696 | + 697 | +void GroupCallFrameDecryptor::RegisterTransformedFrameCallback( 698 | + webrtc::scoped_refptr const dispatcher) { 699 | + RTC_DCHECK(frame_dispatchers.size() == 0); 700 | + 701 | + // Add non-SSRC based dispatcher (deprecated) 702 | + RTC_LOG(LS_VERBOSE) 703 | + << tag << " Registering legacy frame dispatcher for unknown SSRC"; 704 | + frame_dispatcher = std::move(dispatcher); 705 | +} 706 | + 707 | +void GroupCallFrameDecryptor::RegisterTransformedFrameSinkCallback( 708 | + webrtc::scoped_refptr const dispatcher, 709 | + uint32_t const ssrc) { 710 | + RTC_DCHECK(frame_dispatcher == nullptr); 711 | + 712 | + // Replace any existing dispatcher for this SSRC 713 | + for (auto& existing_frame_dispatcher : frame_dispatchers) { 714 | + if (existing_frame_dispatcher.first == ssrc) { 715 | + RTC_LOG(LS_WARNING) << tag 716 | + << " Replacing existing frame dispatcher for SSRC " 717 | + << ssrc; 718 | + existing_frame_dispatcher.second = std::move(dispatcher); 719 | + return; 720 | + } 721 | + } 722 | + 723 | + // No existing dispatcher found for this SSRC, add a new one 724 | + RTC_LOG(LS_VERBOSE) << tag << " Registering frame dispatcher for SSRC " 725 | + << ssrc; 726 | + frame_dispatchers.push_back(std::make_pair(ssrc, std::move(dispatcher))); 727 | +} 728 | + 729 | +void GroupCallFrameDecryptor::UnregisterTransformedFrameCallback() { 730 | + RTC_LOG(LS_VERBOSE) 731 | + << tag << " Unregistering legacy frame dispatcher for unknown SSRC"; 732 | + frame_dispatcher = nullptr; 733 | +} 734 | + 735 | +void GroupCallFrameDecryptor::UnregisterTransformedFrameSinkCallback( 736 | + uint32_t const ssrc) { 737 | + frame_dispatchers.erase( 738 | + std::remove_if(frame_dispatchers.begin(), frame_dispatchers.end(), 739 | + [=, this](auto& frame_dispatcher) { 740 | + if (frame_dispatcher.first == ssrc) { 741 | + RTC_LOG(LS_VERBOSE) 742 | + << tag 743 | + << " Unregistering frame dispatcher for SSRC " 744 | + << ssrc; 745 | + return true; 746 | + } else { 747 | + return false; 748 | + } 749 | + }), 750 | + frame_dispatchers.end()); 751 | +} 752 | + 753 | +webrtc::scoped_refptr* 754 | +GroupCallFrameDecryptor::GetFrameDispatcher(uint32_t const ssrc) { 755 | + // Deprecated 756 | + if (frame_dispatcher != nullptr) { 757 | + return &frame_dispatcher; 758 | + } 759 | + 760 | + // Lookup frame dispatcher 761 | + for (auto& frame_dispatcher : frame_dispatchers) { 762 | + if (frame_dispatcher.first == ssrc) { 763 | + return &frame_dispatcher.second; 764 | + } 765 | + } 766 | + 767 | + // None found 768 | + return nullptr; 769 | +} 770 | + 771 | +/* 772 | + * #region GroupCallFrameEncryptorContext 773 | + */ 774 | +GroupCallFrameEncryptorContext::GroupCallFrameEncryptorContext( 775 | + uint8_t const gckh[kKeyLength]) 776 | + : key_ctx( 777 | + webrtc::make_ref_counted(gckh)) {} 778 | + 779 | +void GroupCallFrameEncryptorContext::SetPcmk(uint8_t const pcmk[kKeyLength], 780 | + uint8_t const epoch, 781 | + uint8_t const ratchet_counter) { 782 | + key_ctx->SetPcmk(pcmk, epoch, ratchet_counter); 783 | +} 784 | + 785 | +webrtc::scoped_refptr 786 | +GroupCallFrameEncryptorContext::CreateEncryptor(std::string const tag, 787 | + Codec const codec) { 788 | + return webrtc::make_ref_counted(tag, codec, key_ctx); 789 | +} 790 | + 791 | +/* 792 | + * #region GroupCallFrameDecryptorContext 793 | + */ 794 | +GroupCallFrameDecryptorContext::GroupCallFrameDecryptorContext( 795 | + uint8_t const gckh[kKeyLength], 796 | + uint16_t const participant_id) 797 | + : participant_id(participant_id), 798 | + keys_ctx( 799 | + webrtc::make_ref_counted(gckh)) {} 800 | + 801 | +void GroupCallFrameDecryptorContext::AddPcmk(uint8_t const pcmk[kKeyLength], 802 | + uint8_t const epoch, 803 | + uint8_t const ratchet_counter) { 804 | + keys_ctx->AddPcmk(pcmk, epoch, ratchet_counter); 805 | +} 806 | + 807 | +webrtc::scoped_refptr 808 | +GroupCallFrameDecryptorContext::CreateDecryptor(std::string const tag, 809 | + Codec const codec) { 810 | + return webrtc::make_ref_counted(tag, codec, 811 | + keys_ctx); 812 | +} 813 | + 814 | +/* 815 | + * #region GroupCallFrameCryptoContext 816 | + */ 817 | +GroupCallFrameCryptoContext::GroupCallFrameCryptoContext( 818 | + uint8_t const gckh[kKeyLength]) 819 | + : encryptor(std::make_unique(gckh)) { 820 | + std::memcpy(this->gckh, gckh, kKeyLength); 821 | +} 822 | + 823 | +GroupCallFrameEncryptorContext& GroupCallFrameCryptoContext::GetEncryptor() { 824 | + return *encryptor; 825 | +} 826 | + 827 | +void GroupCallFrameCryptoContext::AddDecryptor(uint16_t const participant_id) { 828 | + // Ensure no decryptor with this participant id exists yet 829 | + for (auto& decryptor : decryptors) { 830 | + RTC_CHECK(participant_id != decryptor->participant_id); 831 | + } 832 | + 833 | + // Create and add the decryptor 834 | + decryptors.push_back( 835 | + std::make_unique(gckh, participant_id)); 836 | +} 837 | + 838 | +GroupCallFrameDecryptorContext& GroupCallFrameCryptoContext::GetDecryptor( 839 | + uint16_t const participant_id) { 840 | + // Find and return the decryptor. We assert that one must exist. 841 | + for (auto& decryptor : decryptors) { 842 | + if (participant_id == decryptor->participant_id) { 843 | + return *decryptor; 844 | + } 845 | + } 846 | + RTC_CHECK_NOTREACHED(); 847 | +} 848 | + 849 | +void GroupCallFrameCryptoContext::RemoveDecryptor( 850 | + uint16_t const participant_id) { 851 | + decryptors.erase(std::remove_if( 852 | + decryptors.begin(), decryptors.end(), [=](auto& decryptor) { 853 | + return participant_id == decryptor->participant_id; 854 | + })); 855 | +} 856 | + 857 | +} // namespace threema 858 | diff --git a/pc/threema_group_call_frame_transformer.h b/pc/threema_group_call_frame_transformer.h 859 | new file mode 100644 860 | index 0000000000..69db90a08e 861 | --- /dev/null 862 | +++ b/pc/threema_group_call_frame_transformer.h 863 | @@ -0,0 +1,232 @@ 864 | +/* 865 | + * Copyright 2022 Threema GmbH. All Rights Reserved. 866 | + * 867 | + * Use of this source code is governed by a BSD-style license 868 | + * that can be found in the LICENSE file in the root of the source 869 | + * tree. 870 | + */ 871 | + 872 | +#ifndef PC_THREEMA_GROUP_CALL_FRAME_TRANSFORMER_H_ 873 | +#define PC_THREEMA_GROUP_CALL_FRAME_TRANSFORMER_H_ 874 | + 875 | +// We're using BoringSSL which gives us a tad nicer API surface, see: 876 | +// https://commondatastorage.googleapis.com/chromium-boringssl-docs/headers.html 877 | +#include 878 | +#include 879 | + 880 | +#include "absl/types/optional.h" 881 | +#include "api/frame_transformer_interface.h" 882 | +#include "api/scoped_refptr.h" 883 | +#include "rtc_base/synchronization/mutex.h" 884 | +#include "rtc_base/thread_annotations.h" 885 | + 886 | +constexpr size_t kHashLength = 32; 887 | +constexpr size_t kKeyLength = 32; 888 | +constexpr size_t kNonceLength = 12; 889 | +constexpr size_t kBaseAdditionalDataLength = 6; 890 | +constexpr size_t kVp8DeltaFrameLength = 3; 891 | +constexpr size_t kVp8KeyFrameLength = 10; 892 | +constexpr size_t kTagLength = 16; 893 | + 894 | +constexpr size_t kMaxAdditionalDataLength = 895 | + kBaseAdditionalDataLength + 896 | + std::max(kVp8DeltaFrameLength, kVp8KeyFrameLength); 897 | +constexpr size_t kMaxEncryptedFrameLength = 1048576; 898 | +constexpr size_t kMaxUnencryptedFrameLength = 899 | + kMaxEncryptedFrameLength - kTagLength - kBaseAdditionalDataLength; 900 | + 901 | +namespace threema { 902 | + 903 | +enum class Codec { 904 | + kOpus, 905 | + kVp8, 906 | +}; 907 | + 908 | +struct GroupCallFrameCryptoKeyState { 909 | + uint_fast32_t mfsn; 910 | + uint8_t pcmk[kKeyLength]; 911 | + uint8_t pcmfk[kKeyLength]; 912 | + uint_fast8_t epoch; 913 | + uint_fast8_t ratchet_counter; 914 | +}; 915 | + 916 | +struct GroupCallFrameCryptoAeadCtxState { 917 | + explicit GroupCallFrameCryptoAeadCtxState() 918 | + : initialized(false), aead_ctx({}), epoch(0), ratchet_counter(0) {} 919 | + 920 | + bool initialized; 921 | + EVP_AEAD_CTX aead_ctx; 922 | + uint_fast8_t epoch; 923 | + uint_fast8_t ratchet_counter; 924 | +}; 925 | + 926 | +struct GroupCallFrameCryptoKeyStateSnapshot { 927 | + uint32_t mfsn_le; 928 | + uint_fast8_t epoch; 929 | + uint_fast8_t ratchet_counter; 930 | +}; 931 | + 932 | +class GroupCallFrameEncryptorKeyContext : public webrtc::RefCountInterface { 933 | + public: 934 | + explicit GroupCallFrameEncryptorKeyContext(uint8_t const gckh[kKeyLength]); 935 | + 936 | + void SetPcmk(uint8_t const pcmk[kKeyLength], 937 | + uint8_t const epoch, 938 | + uint8_t const ratchet_counter) RTC_NO_THREAD_SAFETY_ANALYSIS; 939 | + absl::optional Apply( 940 | + GroupCallFrameCryptoAeadCtxState& aead_ctx_state, 941 | + EVP_AEAD const* const cipher) RTC_LOCKS_EXCLUDED(mutex); 942 | + 943 | + private: 944 | + webrtc::Mutex mutex; 945 | + uint8_t gckh[kKeyLength] RTC_GUARDED_BY(mutex); 946 | + bool initialized RTC_GUARDED_BY(mutex) = false; 947 | + GroupCallFrameCryptoKeyState state RTC_GUARDED_BY(mutex); 948 | +}; 949 | + 950 | +class GroupCallFrameDecryptorKeysContext : public webrtc::RefCountInterface { 951 | + public: 952 | + explicit GroupCallFrameDecryptorKeysContext(uint8_t const gckh[kKeyLength]); 953 | + 954 | + void AddPcmk(uint8_t const pcmk[kKeyLength], 955 | + uint8_t const epoch, 956 | + uint8_t const ratchet_counter) RTC_LOCKS_EXCLUDED(mutex); 957 | + bool Apply(GroupCallFrameCryptoAeadCtxState& aead_ctx_state, 958 | + EVP_AEAD const* const cipher, 959 | + GroupCallFrameCryptoKeyStateSnapshot const& target) 960 | + RTC_LOCKS_EXCLUDED(mutex); 961 | + 962 | + private: 963 | + absl::optional AlignWith( 964 | + GroupCallFrameCryptoKeyStateSnapshot const& target) 965 | + RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex); 966 | + 967 | + webrtc::Mutex mutex; 968 | + uint8_t gckh[kKeyLength] RTC_GUARDED_BY(mutex); 969 | + /** An array of PCMKs, strictly ordered by epoch, ascending. */ 970 | + std::vector states RTC_GUARDED_BY(mutex); 971 | +}; 972 | + 973 | +class GroupCallFrameEncryptor : public webrtc::FrameTransformerInterface { 974 | + public: 975 | + explicit GroupCallFrameEncryptor( 976 | + std::string const tag, 977 | + Codec const codec, 978 | + webrtc::scoped_refptr const key_ctx); 979 | + ~GroupCallFrameEncryptor() override; 980 | + 981 | + void Transform( 982 | + std::unique_ptr frame) override; 983 | + void RegisterTransformedFrameCallback( 984 | + webrtc::scoped_refptr const dispatcher) 985 | + override; 986 | + void RegisterTransformedFrameSinkCallback( 987 | + webrtc::scoped_refptr const dispatcher, 988 | + uint32_t const ssrc) override; 989 | + void UnregisterTransformedFrameCallback() override; 990 | + void UnregisterTransformedFrameSinkCallback(uint32_t const ssrc) override; 991 | + 992 | + private: 993 | + webrtc::scoped_refptr* GetFrameDispatcher( 994 | + uint32_t const ssrc); 995 | + 996 | + std::string const tag; 997 | + Codec const codec; 998 | + EVP_AEAD const* const cipher; 999 | + webrtc::scoped_refptr const key_ctx; 1000 | + GroupCallFrameCryptoAeadCtxState aead_ctx_state; 1001 | + webrtc::scoped_refptr frame_dispatcher = 1002 | + nullptr; 1003 | + std::vector< 1004 | + std::pair>> 1006 | + frame_dispatchers; 1007 | +}; 1008 | + 1009 | +class GroupCallFrameDecryptor : public webrtc::FrameTransformerInterface { 1010 | + public: 1011 | + explicit GroupCallFrameDecryptor( 1012 | + std::string const tag, 1013 | + Codec const codec, 1014 | + webrtc::scoped_refptr const keys_ctx); 1015 | + ~GroupCallFrameDecryptor() override; 1016 | + 1017 | + void Transform( 1018 | + std::unique_ptr frame) override; 1019 | + void RegisterTransformedFrameCallback( 1020 | + webrtc::scoped_refptr const dispatcher) 1021 | + override; 1022 | + void RegisterTransformedFrameSinkCallback( 1023 | + webrtc::scoped_refptr const dispatcher, 1024 | + uint32_t const ssrc) override; 1025 | + void UnregisterTransformedFrameCallback() override; 1026 | + void UnregisterTransformedFrameSinkCallback(uint32_t const ssrc) override; 1027 | + 1028 | + private: 1029 | + webrtc::scoped_refptr* GetFrameDispatcher( 1030 | + uint32_t const ssrc); 1031 | + 1032 | + std::string const tag; 1033 | + Codec const codec; 1034 | + EVP_AEAD const* const cipher; 1035 | + webrtc::scoped_refptr const keys_ctx; 1036 | + GroupCallFrameCryptoAeadCtxState aead_ctx_state; 1037 | + webrtc::scoped_refptr frame_dispatcher = 1038 | + nullptr; 1039 | + std::vector< 1040 | + std::pair>> 1042 | + frame_dispatchers; 1043 | +}; 1044 | + 1045 | +class GroupCallFrameEncryptorContext { 1046 | + public: 1047 | + explicit GroupCallFrameEncryptorContext(uint8_t const gckh[kKeyLength]); 1048 | + 1049 | + void SetPcmk(uint8_t const pcmk[kKeyLength], 1050 | + uint8_t const epoch, 1051 | + uint8_t const ratchet_counter); 1052 | + webrtc::scoped_refptr CreateEncryptor( 1053 | + std::string const tag, 1054 | + Codec const codec); 1055 | + 1056 | + private: 1057 | + webrtc::scoped_refptr const key_ctx; 1058 | +}; 1059 | + 1060 | +class GroupCallFrameDecryptorContext { 1061 | + public: 1062 | + explicit GroupCallFrameDecryptorContext(uint8_t const gckh[kKeyLength], 1063 | + uint16_t const participant_id); 1064 | + 1065 | + void AddPcmk(uint8_t const pcmk[kKeyLength], 1066 | + uint8_t const epoch, 1067 | + uint8_t const ratchet_counter); 1068 | + webrtc::scoped_refptr CreateDecryptor( 1069 | + std::string const tag, 1070 | + Codec const codec); 1071 | + 1072 | + uint16_t const participant_id; 1073 | + 1074 | + private: 1075 | + webrtc::scoped_refptr const keys_ctx; 1076 | +}; 1077 | + 1078 | +class GroupCallFrameCryptoContext : public webrtc::RefCountInterface { 1079 | + public: 1080 | + explicit GroupCallFrameCryptoContext(uint8_t const gckh[kKeyLength]); 1081 | + 1082 | + GroupCallFrameEncryptorContext& GetEncryptor(); 1083 | + void AddDecryptor(uint16_t const participant_id); 1084 | + GroupCallFrameDecryptorContext& GetDecryptor(uint16_t const participant_id); 1085 | + void RemoveDecryptor(uint16_t const participant_id); 1086 | + 1087 | + private: 1088 | + uint8_t gckh[kKeyLength]; 1089 | + std::unique_ptr const encryptor; 1090 | + std::vector> decryptors; 1091 | +}; 1092 | + 1093 | +} // namespace threema 1094 | + 1095 | +#endif // PC_THREEMA_GROUP_CALL_FRAME_TRANSFORMER_H_ 1096 | diff --git a/rtc_base/third_party/threema-blake2b/.clang-format b/rtc_base/third_party/threema-blake2b/.clang-format 1097 | new file mode 100644 1098 | index 0000000000..e3845288a2 1099 | --- /dev/null 1100 | +++ b/rtc_base/third_party/threema-blake2b/.clang-format 1101 | @@ -0,0 +1 @@ 1102 | +DisableFormat: true 1103 | diff --git a/rtc_base/third_party/threema-blake2b/BUILD.gn b/rtc_base/third_party/threema-blake2b/BUILD.gn 1104 | new file mode 100644 1105 | index 0000000000..4af223855b 1106 | --- /dev/null 1107 | +++ b/rtc_base/third_party/threema-blake2b/BUILD.gn 1108 | @@ -0,0 +1,18 @@ 1109 | +# Copyright 2022 Threema GmbH. All Rights Reserved. 1110 | +# 1111 | +# Use of this source code is governed by a BSD-style license 1112 | +# that can be found in the LICENSE file in the root of the source 1113 | +# tree. 1114 | + 1115 | +import("../../../webrtc.gni") 1116 | + 1117 | +rtc_library("threema-blake2b") { 1118 | + visibility = [ "*" ] 1119 | + sources = [ 1120 | + "blake2b.c", 1121 | + "blake2b.h", 1122 | + "ref/blake2-impl.h", 1123 | + "ref/blake2.h", 1124 | + "ref/blake2b-ref.c", 1125 | + ] 1126 | +} 1127 | diff --git a/rtc_base/third_party/threema-blake2b/blake2b.c b/rtc_base/third_party/threema-blake2b/blake2b.c 1128 | new file mode 100644 1129 | index 0000000000..f820a97910 1130 | --- /dev/null 1131 | +++ b/rtc_base/third_party/threema-blake2b/blake2b.c 1132 | @@ -0,0 +1,58 @@ 1133 | +/* 1134 | + * Copyright 2022 Threema GmbH. All Rights Reserved. 1135 | + * 1136 | + * Use of this source code is governed by a BSD-style license 1137 | + * that can be found in the LICENSE file in the root of the source 1138 | + * tree. 1139 | + */ 1140 | + 1141 | +#include "blake2b.h" 1142 | + 1143 | +#include "ref/blake2-impl.h" 1144 | +#include "ref/blake2.h" 1145 | + 1146 | +// Implementation based on `blake2b_init_key`. 1147 | +int blake2b_init_with_params(blake2b_state* S, 1148 | + size_t outlen, 1149 | + const uint8_t* key, 1150 | + size_t keylen, 1151 | + const uint8_t salt[BLAKE2B_SALTBYTES], 1152 | + const uint8_t personal[BLAKE2B_PERSONALBYTES]) { 1153 | + blake2b_param P[1]; 1154 | + 1155 | + if ((!outlen) || (outlen > BLAKE2B_OUTBYTES)) { 1156 | + return -1; 1157 | + } 1158 | + if (!key) { 1159 | + keylen = 0; 1160 | + } 1161 | + if (key && (!keylen || keylen > BLAKE2B_KEYBYTES)) { 1162 | + return -1; 1163 | + } 1164 | + 1165 | + P->digest_length = (uint8_t)outlen; 1166 | + P->key_length = (uint8_t)keylen; 1167 | + P->fanout = 1; 1168 | + P->depth = 1; 1169 | + store32(&P->leaf_length, 0); 1170 | + store32(&P->node_offset, 0); 1171 | + store32(&P->xof_length, 0); 1172 | + P->node_depth = 0; 1173 | + P->inner_length = 0; 1174 | + memset(P->reserved, 0, sizeof(P->reserved)); 1175 | + memcpy(P->salt, salt, sizeof(P->salt)); 1176 | + memcpy(P->personal, personal, sizeof(P->personal)); 1177 | + 1178 | + if (blake2b_init_param(S, P) < 0) { 1179 | + return -1; 1180 | + } 1181 | + 1182 | + if (key) { 1183 | + uint8_t block[BLAKE2B_BLOCKBYTES]; 1184 | + memset(block, 0, BLAKE2B_BLOCKBYTES); 1185 | + memcpy(block, key, keylen); 1186 | + blake2b_update(S, block, BLAKE2B_BLOCKBYTES); 1187 | + secure_zero_memory(block, BLAKE2B_BLOCKBYTES); /* Burn the key from stack */ 1188 | + } 1189 | + return 0; 1190 | +} 1191 | diff --git a/rtc_base/third_party/threema-blake2b/blake2b.h b/rtc_base/third_party/threema-blake2b/blake2b.h 1192 | new file mode 100644 1193 | index 0000000000..328ae4f24c 1194 | --- /dev/null 1195 | +++ b/rtc_base/third_party/threema-blake2b/blake2b.h 1196 | @@ -0,0 +1,30 @@ 1197 | +/* 1198 | + * Copyright 2022 Threema GmbH. All Rights Reserved. 1199 | + * 1200 | + * Use of this source code is governed by a BSD-style license 1201 | + * that can be found in the LICENSE file in the root of the source 1202 | + * tree. 1203 | + */ 1204 | + 1205 | +#ifndef BLAKE2B_H 1206 | +#define BLAKE2B_H 1207 | + 1208 | +#include 1209 | +#include 1210 | + 1211 | +#include "ref/blake2.h" 1212 | + 1213 | +#if defined(__cplusplus) 1214 | +extern "C" { 1215 | +#endif 1216 | +int blake2b_init_with_params(blake2b_state* S, 1217 | + size_t outlen, 1218 | + const uint8_t* key, 1219 | + size_t keylen, 1220 | + const uint8_t salt[BLAKE2B_SALTBYTES], 1221 | + const uint8_t personal[BLAKE2B_PERSONALBYTES]); 1222 | +#if defined(__cplusplus) 1223 | +} 1224 | +#endif 1225 | + 1226 | +#endif 1227 | diff --git a/rtc_base/third_party/threema-blake2b/ref/blake2-impl.h b/rtc_base/third_party/threema-blake2b/ref/blake2-impl.h 1228 | new file mode 100644 1229 | index 0000000000..c1df82e0c9 1230 | --- /dev/null 1231 | +++ b/rtc_base/third_party/threema-blake2b/ref/blake2-impl.h 1232 | @@ -0,0 +1,160 @@ 1233 | +/* 1234 | + BLAKE2 reference source code package - reference C implementations 1235 | + 1236 | + Copyright 2012, Samuel Neves . You may use this under the 1237 | + terms of the CC0, the OpenSSL Licence, or the Apache Public License 2.0, at 1238 | + your option. The terms of these licenses can be found at: 1239 | + 1240 | + - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0 1241 | + - OpenSSL license : https://www.openssl.org/source/license.html 1242 | + - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0 1243 | + 1244 | + More information about the BLAKE2 hash function can be found at 1245 | + https://blake2.net. 1246 | +*/ 1247 | +#ifndef BLAKE2_IMPL_H 1248 | +#define BLAKE2_IMPL_H 1249 | + 1250 | +#include 1251 | +#include 1252 | + 1253 | +#if !defined(__cplusplus) && (!defined(__STDC_VERSION__) || __STDC_VERSION__ < 199901L) 1254 | + #if defined(_MSC_VER) 1255 | + #define BLAKE2_INLINE __inline 1256 | + #elif defined(__GNUC__) 1257 | + #define BLAKE2_INLINE __inline__ 1258 | + #else 1259 | + #define BLAKE2_INLINE 1260 | + #endif 1261 | +#else 1262 | + #define BLAKE2_INLINE inline 1263 | +#endif 1264 | + 1265 | +static BLAKE2_INLINE uint32_t load32( const void *src ) 1266 | +{ 1267 | +#if defined(NATIVE_LITTLE_ENDIAN) 1268 | + uint32_t w; 1269 | + memcpy(&w, src, sizeof w); 1270 | + return w; 1271 | +#else 1272 | + const uint8_t *p = ( const uint8_t * )src; 1273 | + return (( uint32_t )( p[0] ) << 0) | 1274 | + (( uint32_t )( p[1] ) << 8) | 1275 | + (( uint32_t )( p[2] ) << 16) | 1276 | + (( uint32_t )( p[3] ) << 24) ; 1277 | +#endif 1278 | +} 1279 | + 1280 | +static BLAKE2_INLINE uint64_t load64( const void *src ) 1281 | +{ 1282 | +#if defined(NATIVE_LITTLE_ENDIAN) 1283 | + uint64_t w; 1284 | + memcpy(&w, src, sizeof w); 1285 | + return w; 1286 | +#else 1287 | + const uint8_t *p = ( const uint8_t * )src; 1288 | + return (( uint64_t )( p[0] ) << 0) | 1289 | + (( uint64_t )( p[1] ) << 8) | 1290 | + (( uint64_t )( p[2] ) << 16) | 1291 | + (( uint64_t )( p[3] ) << 24) | 1292 | + (( uint64_t )( p[4] ) << 32) | 1293 | + (( uint64_t )( p[5] ) << 40) | 1294 | + (( uint64_t )( p[6] ) << 48) | 1295 | + (( uint64_t )( p[7] ) << 56) ; 1296 | +#endif 1297 | +} 1298 | + 1299 | +static BLAKE2_INLINE uint16_t load16( const void *src ) 1300 | +{ 1301 | +#if defined(NATIVE_LITTLE_ENDIAN) 1302 | + uint16_t w; 1303 | + memcpy(&w, src, sizeof w); 1304 | + return w; 1305 | +#else 1306 | + const uint8_t *p = ( const uint8_t * )src; 1307 | + return ( uint16_t )((( uint32_t )( p[0] ) << 0) | 1308 | + (( uint32_t )( p[1] ) << 8)); 1309 | +#endif 1310 | +} 1311 | + 1312 | +static BLAKE2_INLINE void store16( void *dst, uint16_t w ) 1313 | +{ 1314 | +#if defined(NATIVE_LITTLE_ENDIAN) 1315 | + memcpy(dst, &w, sizeof w); 1316 | +#else 1317 | + uint8_t *p = ( uint8_t * )dst; 1318 | + *p++ = ( uint8_t )w; w >>= 8; 1319 | + *p++ = ( uint8_t )w; 1320 | +#endif 1321 | +} 1322 | + 1323 | +static BLAKE2_INLINE void store32( void *dst, uint32_t w ) 1324 | +{ 1325 | +#if defined(NATIVE_LITTLE_ENDIAN) 1326 | + memcpy(dst, &w, sizeof w); 1327 | +#else 1328 | + uint8_t *p = ( uint8_t * )dst; 1329 | + p[0] = (uint8_t)(w >> 0); 1330 | + p[1] = (uint8_t)(w >> 8); 1331 | + p[2] = (uint8_t)(w >> 16); 1332 | + p[3] = (uint8_t)(w >> 24); 1333 | +#endif 1334 | +} 1335 | + 1336 | +static BLAKE2_INLINE void store64( void *dst, uint64_t w ) 1337 | +{ 1338 | +#if defined(NATIVE_LITTLE_ENDIAN) 1339 | + memcpy(dst, &w, sizeof w); 1340 | +#else 1341 | + uint8_t *p = ( uint8_t * )dst; 1342 | + p[0] = (uint8_t)(w >> 0); 1343 | + p[1] = (uint8_t)(w >> 8); 1344 | + p[2] = (uint8_t)(w >> 16); 1345 | + p[3] = (uint8_t)(w >> 24); 1346 | + p[4] = (uint8_t)(w >> 32); 1347 | + p[5] = (uint8_t)(w >> 40); 1348 | + p[6] = (uint8_t)(w >> 48); 1349 | + p[7] = (uint8_t)(w >> 56); 1350 | +#endif 1351 | +} 1352 | + 1353 | +static BLAKE2_INLINE uint64_t load48( const void *src ) 1354 | +{ 1355 | + const uint8_t *p = ( const uint8_t * )src; 1356 | + return (( uint64_t )( p[0] ) << 0) | 1357 | + (( uint64_t )( p[1] ) << 8) | 1358 | + (( uint64_t )( p[2] ) << 16) | 1359 | + (( uint64_t )( p[3] ) << 24) | 1360 | + (( uint64_t )( p[4] ) << 32) | 1361 | + (( uint64_t )( p[5] ) << 40) ; 1362 | +} 1363 | + 1364 | +static BLAKE2_INLINE void store48( void *dst, uint64_t w ) 1365 | +{ 1366 | + uint8_t *p = ( uint8_t * )dst; 1367 | + p[0] = (uint8_t)(w >> 0); 1368 | + p[1] = (uint8_t)(w >> 8); 1369 | + p[2] = (uint8_t)(w >> 16); 1370 | + p[3] = (uint8_t)(w >> 24); 1371 | + p[4] = (uint8_t)(w >> 32); 1372 | + p[5] = (uint8_t)(w >> 40); 1373 | +} 1374 | + 1375 | +static BLAKE2_INLINE uint32_t rotr32( const uint32_t w, const unsigned c ) 1376 | +{ 1377 | + return ( w >> c ) | ( w << ( 32 - c ) ); 1378 | +} 1379 | + 1380 | +static BLAKE2_INLINE uint64_t rotr64( const uint64_t w, const unsigned c ) 1381 | +{ 1382 | + return ( w >> c ) | ( w << ( 64 - c ) ); 1383 | +} 1384 | + 1385 | +/* prevents compiler optimizing out memset() */ 1386 | +static BLAKE2_INLINE void secure_zero_memory(void *v, size_t n) 1387 | +{ 1388 | + static void *(*const volatile memset_v)(void *, int, size_t) = &memset; 1389 | + memset_v(v, 0, n); 1390 | +} 1391 | + 1392 | +#endif 1393 | diff --git a/rtc_base/third_party/threema-blake2b/ref/blake2.h b/rtc_base/third_party/threema-blake2b/ref/blake2.h 1394 | new file mode 100644 1395 | index 0000000000..ca390305e6 1396 | --- /dev/null 1397 | +++ b/rtc_base/third_party/threema-blake2b/ref/blake2.h 1398 | @@ -0,0 +1,195 @@ 1399 | +/* 1400 | + BLAKE2 reference source code package - reference C implementations 1401 | + 1402 | + Copyright 2012, Samuel Neves . You may use this under the 1403 | + terms of the CC0, the OpenSSL Licence, or the Apache Public License 2.0, at 1404 | + your option. The terms of these licenses can be found at: 1405 | + 1406 | + - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0 1407 | + - OpenSSL license : https://www.openssl.org/source/license.html 1408 | + - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0 1409 | + 1410 | + More information about the BLAKE2 hash function can be found at 1411 | + https://blake2.net. 1412 | +*/ 1413 | +#ifndef BLAKE2_H 1414 | +#define BLAKE2_H 1415 | + 1416 | +#include 1417 | +#include 1418 | + 1419 | +#if defined(_MSC_VER) 1420 | +#define BLAKE2_PACKED(x) __pragma(pack(push, 1)) x __pragma(pack(pop)) 1421 | +#else 1422 | +#define BLAKE2_PACKED(x) x __attribute__((packed)) 1423 | +#endif 1424 | + 1425 | +#if defined(__cplusplus) 1426 | +extern "C" { 1427 | +#endif 1428 | + 1429 | + enum blake2s_constant 1430 | + { 1431 | + BLAKE2S_BLOCKBYTES = 64, 1432 | + BLAKE2S_OUTBYTES = 32, 1433 | + BLAKE2S_KEYBYTES = 32, 1434 | + BLAKE2S_SALTBYTES = 8, 1435 | + BLAKE2S_PERSONALBYTES = 8 1436 | + }; 1437 | + 1438 | + enum blake2b_constant 1439 | + { 1440 | + BLAKE2B_BLOCKBYTES = 128, 1441 | + BLAKE2B_OUTBYTES = 64, 1442 | + BLAKE2B_KEYBYTES = 64, 1443 | + BLAKE2B_SALTBYTES = 16, 1444 | + BLAKE2B_PERSONALBYTES = 16 1445 | + }; 1446 | + 1447 | + typedef struct blake2s_state__ 1448 | + { 1449 | + uint32_t h[8]; 1450 | + uint32_t t[2]; 1451 | + uint32_t f[2]; 1452 | + uint8_t buf[BLAKE2S_BLOCKBYTES]; 1453 | + size_t buflen; 1454 | + size_t outlen; 1455 | + uint8_t last_node; 1456 | + } blake2s_state; 1457 | + 1458 | + typedef struct blake2b_state__ 1459 | + { 1460 | + uint64_t h[8]; 1461 | + uint64_t t[2]; 1462 | + uint64_t f[2]; 1463 | + uint8_t buf[BLAKE2B_BLOCKBYTES]; 1464 | + size_t buflen; 1465 | + size_t outlen; 1466 | + uint8_t last_node; 1467 | + } blake2b_state; 1468 | + 1469 | + typedef struct blake2sp_state__ 1470 | + { 1471 | + blake2s_state S[8][1]; 1472 | + blake2s_state R[1]; 1473 | + uint8_t buf[8 * BLAKE2S_BLOCKBYTES]; 1474 | + size_t buflen; 1475 | + size_t outlen; 1476 | + } blake2sp_state; 1477 | + 1478 | + typedef struct blake2bp_state__ 1479 | + { 1480 | + blake2b_state S[4][1]; 1481 | + blake2b_state R[1]; 1482 | + uint8_t buf[4 * BLAKE2B_BLOCKBYTES]; 1483 | + size_t buflen; 1484 | + size_t outlen; 1485 | + } blake2bp_state; 1486 | + 1487 | + 1488 | + BLAKE2_PACKED(struct blake2s_param__ 1489 | + { 1490 | + uint8_t digest_length; /* 1 */ 1491 | + uint8_t key_length; /* 2 */ 1492 | + uint8_t fanout; /* 3 */ 1493 | + uint8_t depth; /* 4 */ 1494 | + uint32_t leaf_length; /* 8 */ 1495 | + uint32_t node_offset; /* 12 */ 1496 | + uint16_t xof_length; /* 14 */ 1497 | + uint8_t node_depth; /* 15 */ 1498 | + uint8_t inner_length; /* 16 */ 1499 | + /* uint8_t reserved[0]; */ 1500 | + uint8_t salt[BLAKE2S_SALTBYTES]; /* 24 */ 1501 | + uint8_t personal[BLAKE2S_PERSONALBYTES]; /* 32 */ 1502 | + }); 1503 | + 1504 | + typedef struct blake2s_param__ blake2s_param; 1505 | + 1506 | + BLAKE2_PACKED(struct blake2b_param__ 1507 | + { 1508 | + uint8_t digest_length; /* 1 */ 1509 | + uint8_t key_length; /* 2 */ 1510 | + uint8_t fanout; /* 3 */ 1511 | + uint8_t depth; /* 4 */ 1512 | + uint32_t leaf_length; /* 8 */ 1513 | + uint32_t node_offset; /* 12 */ 1514 | + uint32_t xof_length; /* 16 */ 1515 | + uint8_t node_depth; /* 17 */ 1516 | + uint8_t inner_length; /* 18 */ 1517 | + uint8_t reserved[14]; /* 32 */ 1518 | + uint8_t salt[BLAKE2B_SALTBYTES]; /* 48 */ 1519 | + uint8_t personal[BLAKE2B_PERSONALBYTES]; /* 64 */ 1520 | + }); 1521 | + 1522 | + typedef struct blake2b_param__ blake2b_param; 1523 | + 1524 | + typedef struct blake2xs_state__ 1525 | + { 1526 | + blake2s_state S[1]; 1527 | + blake2s_param P[1]; 1528 | + } blake2xs_state; 1529 | + 1530 | + typedef struct blake2xb_state__ 1531 | + { 1532 | + blake2b_state S[1]; 1533 | + blake2b_param P[1]; 1534 | + } blake2xb_state; 1535 | + 1536 | + /* Padded structs result in a compile-time error */ 1537 | + enum { 1538 | + BLAKE2_DUMMY_1 = 1/(int)(sizeof(blake2s_param) == BLAKE2S_OUTBYTES), 1539 | + BLAKE2_DUMMY_2 = 1/(int)(sizeof(blake2b_param) == BLAKE2B_OUTBYTES) 1540 | + }; 1541 | + 1542 | + /* Streaming API */ 1543 | + int blake2s_init( blake2s_state *S, size_t outlen ); 1544 | + int blake2s_init_key( blake2s_state *S, size_t outlen, const void *key, size_t keylen ); 1545 | + int blake2s_init_param( blake2s_state *S, const blake2s_param *P ); 1546 | + int blake2s_update( blake2s_state *S, const void *in, size_t inlen ); 1547 | + int blake2s_final( blake2s_state *S, void *out, size_t outlen ); 1548 | + 1549 | + int blake2b_init( blake2b_state *S, size_t outlen ); 1550 | + int blake2b_init_key( blake2b_state *S, size_t outlen, const void *key, size_t keylen ); 1551 | + int blake2b_init_param( blake2b_state *S, const blake2b_param *P ); 1552 | + int blake2b_update( blake2b_state *S, const void *in, size_t inlen ); 1553 | + int blake2b_final( blake2b_state *S, void *out, size_t outlen ); 1554 | + 1555 | + int blake2sp_init( blake2sp_state *S, size_t outlen ); 1556 | + int blake2sp_init_key( blake2sp_state *S, size_t outlen, const void *key, size_t keylen ); 1557 | + int blake2sp_update( blake2sp_state *S, const void *in, size_t inlen ); 1558 | + int blake2sp_final( blake2sp_state *S, void *out, size_t outlen ); 1559 | + 1560 | + int blake2bp_init( blake2bp_state *S, size_t outlen ); 1561 | + int blake2bp_init_key( blake2bp_state *S, size_t outlen, const void *key, size_t keylen ); 1562 | + int blake2bp_update( blake2bp_state *S, const void *in, size_t inlen ); 1563 | + int blake2bp_final( blake2bp_state *S, void *out, size_t outlen ); 1564 | + 1565 | + /* Variable output length API */ 1566 | + int blake2xs_init( blake2xs_state *S, const size_t outlen ); 1567 | + int blake2xs_init_key( blake2xs_state *S, const size_t outlen, const void *key, size_t keylen ); 1568 | + int blake2xs_update( blake2xs_state *S, const void *in, size_t inlen ); 1569 | + int blake2xs_final(blake2xs_state *S, void *out, size_t outlen); 1570 | + 1571 | + int blake2xb_init( blake2xb_state *S, const size_t outlen ); 1572 | + int blake2xb_init_key( blake2xb_state *S, const size_t outlen, const void *key, size_t keylen ); 1573 | + int blake2xb_update( blake2xb_state *S, const void *in, size_t inlen ); 1574 | + int blake2xb_final(blake2xb_state *S, void *out, size_t outlen); 1575 | + 1576 | + /* Simple API */ 1577 | + int blake2s( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen ); 1578 | + int blake2b( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen ); 1579 | + 1580 | + int blake2sp( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen ); 1581 | + int blake2bp( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen ); 1582 | + 1583 | + int blake2xs( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen ); 1584 | + int blake2xb( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen ); 1585 | + 1586 | + /* This is simply an alias for blake2b */ 1587 | + int blake2( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen ); 1588 | + 1589 | +#if defined(__cplusplus) 1590 | +} 1591 | +#endif 1592 | + 1593 | +#endif 1594 | diff --git a/rtc_base/third_party/threema-blake2b/ref/blake2b-ref.c b/rtc_base/third_party/threema-blake2b/ref/blake2b-ref.c 1595 | new file mode 100644 1596 | index 0000000000..cd38b1ba00 1597 | --- /dev/null 1598 | +++ b/rtc_base/third_party/threema-blake2b/ref/blake2b-ref.c 1599 | @@ -0,0 +1,379 @@ 1600 | +/* 1601 | + BLAKE2 reference source code package - reference C implementations 1602 | + 1603 | + Copyright 2012, Samuel Neves . You may use this under the 1604 | + terms of the CC0, the OpenSSL Licence, or the Apache Public License 2.0, at 1605 | + your option. The terms of these licenses can be found at: 1606 | + 1607 | + - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0 1608 | + - OpenSSL license : https://www.openssl.org/source/license.html 1609 | + - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0 1610 | + 1611 | + More information about the BLAKE2 hash function can be found at 1612 | + https://blake2.net. 1613 | +*/ 1614 | + 1615 | +#include 1616 | +#include 1617 | +#include 1618 | + 1619 | +#include "blake2.h" 1620 | +#include "blake2-impl.h" 1621 | + 1622 | +static const uint64_t blake2b_IV[8] = 1623 | +{ 1624 | + 0x6a09e667f3bcc908ULL, 0xbb67ae8584caa73bULL, 1625 | + 0x3c6ef372fe94f82bULL, 0xa54ff53a5f1d36f1ULL, 1626 | + 0x510e527fade682d1ULL, 0x9b05688c2b3e6c1fULL, 1627 | + 0x1f83d9abfb41bd6bULL, 0x5be0cd19137e2179ULL 1628 | +}; 1629 | + 1630 | +static const uint8_t blake2b_sigma[12][16] = 1631 | +{ 1632 | + { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 } , 1633 | + { 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 } , 1634 | + { 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4 } , 1635 | + { 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8 } , 1636 | + { 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13 } , 1637 | + { 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9 } , 1638 | + { 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11 } , 1639 | + { 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10 } , 1640 | + { 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5 } , 1641 | + { 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13 , 0 } , 1642 | + { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 } , 1643 | + { 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 } 1644 | +}; 1645 | + 1646 | + 1647 | +static void blake2b_set_lastnode( blake2b_state *S ) 1648 | +{ 1649 | + S->f[1] = (uint64_t)-1; 1650 | +} 1651 | + 1652 | +/* Some helper functions, not necessarily useful */ 1653 | +static int blake2b_is_lastblock( const blake2b_state *S ) 1654 | +{ 1655 | + return S->f[0] != 0; 1656 | +} 1657 | + 1658 | +static void blake2b_set_lastblock( blake2b_state *S ) 1659 | +{ 1660 | + if( S->last_node ) blake2b_set_lastnode( S ); 1661 | + 1662 | + S->f[0] = (uint64_t)-1; 1663 | +} 1664 | + 1665 | +static void blake2b_increment_counter( blake2b_state *S, const uint64_t inc ) 1666 | +{ 1667 | + S->t[0] += inc; 1668 | + S->t[1] += ( S->t[0] < inc ); 1669 | +} 1670 | + 1671 | +static void blake2b_init0( blake2b_state *S ) 1672 | +{ 1673 | + size_t i; 1674 | + memset( S, 0, sizeof( blake2b_state ) ); 1675 | + 1676 | + for( i = 0; i < 8; ++i ) S->h[i] = blake2b_IV[i]; 1677 | +} 1678 | + 1679 | +/* init xors IV with input parameter block */ 1680 | +int blake2b_init_param( blake2b_state *S, const blake2b_param *P ) 1681 | +{ 1682 | + const uint8_t *p = ( const uint8_t * )( P ); 1683 | + size_t i; 1684 | + 1685 | + blake2b_init0( S ); 1686 | + 1687 | + /* IV XOR ParamBlock */ 1688 | + for( i = 0; i < 8; ++i ) 1689 | + S->h[i] ^= load64( p + sizeof( S->h[i] ) * i ); 1690 | + 1691 | + S->outlen = P->digest_length; 1692 | + return 0; 1693 | +} 1694 | + 1695 | + 1696 | + 1697 | +int blake2b_init( blake2b_state *S, size_t outlen ) 1698 | +{ 1699 | + blake2b_param P[1]; 1700 | + 1701 | + if ( ( !outlen ) || ( outlen > BLAKE2B_OUTBYTES ) ) return -1; 1702 | + 1703 | + P->digest_length = (uint8_t)outlen; 1704 | + P->key_length = 0; 1705 | + P->fanout = 1; 1706 | + P->depth = 1; 1707 | + store32( &P->leaf_length, 0 ); 1708 | + store32( &P->node_offset, 0 ); 1709 | + store32( &P->xof_length, 0 ); 1710 | + P->node_depth = 0; 1711 | + P->inner_length = 0; 1712 | + memset( P->reserved, 0, sizeof( P->reserved ) ); 1713 | + memset( P->salt, 0, sizeof( P->salt ) ); 1714 | + memset( P->personal, 0, sizeof( P->personal ) ); 1715 | + return blake2b_init_param( S, P ); 1716 | +} 1717 | + 1718 | + 1719 | +int blake2b_init_key( blake2b_state *S, size_t outlen, const void *key, size_t keylen ) 1720 | +{ 1721 | + blake2b_param P[1]; 1722 | + 1723 | + if ( ( !outlen ) || ( outlen > BLAKE2B_OUTBYTES ) ) return -1; 1724 | + 1725 | + if ( !key || !keylen || keylen > BLAKE2B_KEYBYTES ) return -1; 1726 | + 1727 | + P->digest_length = (uint8_t)outlen; 1728 | + P->key_length = (uint8_t)keylen; 1729 | + P->fanout = 1; 1730 | + P->depth = 1; 1731 | + store32( &P->leaf_length, 0 ); 1732 | + store32( &P->node_offset, 0 ); 1733 | + store32( &P->xof_length, 0 ); 1734 | + P->node_depth = 0; 1735 | + P->inner_length = 0; 1736 | + memset( P->reserved, 0, sizeof( P->reserved ) ); 1737 | + memset( P->salt, 0, sizeof( P->salt ) ); 1738 | + memset( P->personal, 0, sizeof( P->personal ) ); 1739 | + 1740 | + if( blake2b_init_param( S, P ) < 0 ) return -1; 1741 | + 1742 | + { 1743 | + uint8_t block[BLAKE2B_BLOCKBYTES]; 1744 | + memset( block, 0, BLAKE2B_BLOCKBYTES ); 1745 | + memcpy( block, key, keylen ); 1746 | + blake2b_update( S, block, BLAKE2B_BLOCKBYTES ); 1747 | + secure_zero_memory( block, BLAKE2B_BLOCKBYTES ); /* Burn the key from stack */ 1748 | + } 1749 | + return 0; 1750 | +} 1751 | + 1752 | +#define G(r,i,a,b,c,d) \ 1753 | + do { \ 1754 | + a = a + b + m[blake2b_sigma[r][2*i+0]]; \ 1755 | + d = rotr64(d ^ a, 32); \ 1756 | + c = c + d; \ 1757 | + b = rotr64(b ^ c, 24); \ 1758 | + a = a + b + m[blake2b_sigma[r][2*i+1]]; \ 1759 | + d = rotr64(d ^ a, 16); \ 1760 | + c = c + d; \ 1761 | + b = rotr64(b ^ c, 63); \ 1762 | + } while(0) 1763 | + 1764 | +#define ROUND(r) \ 1765 | + do { \ 1766 | + G(r,0,v[ 0],v[ 4],v[ 8],v[12]); \ 1767 | + G(r,1,v[ 1],v[ 5],v[ 9],v[13]); \ 1768 | + G(r,2,v[ 2],v[ 6],v[10],v[14]); \ 1769 | + G(r,3,v[ 3],v[ 7],v[11],v[15]); \ 1770 | + G(r,4,v[ 0],v[ 5],v[10],v[15]); \ 1771 | + G(r,5,v[ 1],v[ 6],v[11],v[12]); \ 1772 | + G(r,6,v[ 2],v[ 7],v[ 8],v[13]); \ 1773 | + G(r,7,v[ 3],v[ 4],v[ 9],v[14]); \ 1774 | + } while(0) 1775 | + 1776 | +static void blake2b_compress( blake2b_state *S, const uint8_t block[BLAKE2B_BLOCKBYTES] ) 1777 | +{ 1778 | + uint64_t m[16]; 1779 | + uint64_t v[16]; 1780 | + size_t i; 1781 | + 1782 | + for( i = 0; i < 16; ++i ) { 1783 | + m[i] = load64( block + i * sizeof( m[i] ) ); 1784 | + } 1785 | + 1786 | + for( i = 0; i < 8; ++i ) { 1787 | + v[i] = S->h[i]; 1788 | + } 1789 | + 1790 | + v[ 8] = blake2b_IV[0]; 1791 | + v[ 9] = blake2b_IV[1]; 1792 | + v[10] = blake2b_IV[2]; 1793 | + v[11] = blake2b_IV[3]; 1794 | + v[12] = blake2b_IV[4] ^ S->t[0]; 1795 | + v[13] = blake2b_IV[5] ^ S->t[1]; 1796 | + v[14] = blake2b_IV[6] ^ S->f[0]; 1797 | + v[15] = blake2b_IV[7] ^ S->f[1]; 1798 | + 1799 | + ROUND( 0 ); 1800 | + ROUND( 1 ); 1801 | + ROUND( 2 ); 1802 | + ROUND( 3 ); 1803 | + ROUND( 4 ); 1804 | + ROUND( 5 ); 1805 | + ROUND( 6 ); 1806 | + ROUND( 7 ); 1807 | + ROUND( 8 ); 1808 | + ROUND( 9 ); 1809 | + ROUND( 10 ); 1810 | + ROUND( 11 ); 1811 | + 1812 | + for( i = 0; i < 8; ++i ) { 1813 | + S->h[i] = S->h[i] ^ v[i] ^ v[i + 8]; 1814 | + } 1815 | +} 1816 | + 1817 | +#undef G 1818 | +#undef ROUND 1819 | + 1820 | +int blake2b_update( blake2b_state *S, const void *pin, size_t inlen ) 1821 | +{ 1822 | + const unsigned char * in = (const unsigned char *)pin; 1823 | + if( inlen > 0 ) 1824 | + { 1825 | + size_t left = S->buflen; 1826 | + size_t fill = BLAKE2B_BLOCKBYTES - left; 1827 | + if( inlen > fill ) 1828 | + { 1829 | + S->buflen = 0; 1830 | + memcpy( S->buf + left, in, fill ); /* Fill buffer */ 1831 | + blake2b_increment_counter( S, BLAKE2B_BLOCKBYTES ); 1832 | + blake2b_compress( S, S->buf ); /* Compress */ 1833 | + in += fill; inlen -= fill; 1834 | + while(inlen > BLAKE2B_BLOCKBYTES) { 1835 | + blake2b_increment_counter(S, BLAKE2B_BLOCKBYTES); 1836 | + blake2b_compress( S, in ); 1837 | + in += BLAKE2B_BLOCKBYTES; 1838 | + inlen -= BLAKE2B_BLOCKBYTES; 1839 | + } 1840 | + } 1841 | + memcpy( S->buf + S->buflen, in, inlen ); 1842 | + S->buflen += inlen; 1843 | + } 1844 | + return 0; 1845 | +} 1846 | + 1847 | +int blake2b_final( blake2b_state *S, void *out, size_t outlen ) 1848 | +{ 1849 | + uint8_t buffer[BLAKE2B_OUTBYTES] = {0}; 1850 | + size_t i; 1851 | + 1852 | + if( out == NULL || outlen < S->outlen ) 1853 | + return -1; 1854 | + 1855 | + if( blake2b_is_lastblock( S ) ) 1856 | + return -1; 1857 | + 1858 | + blake2b_increment_counter( S, S->buflen ); 1859 | + blake2b_set_lastblock( S ); 1860 | + memset( S->buf + S->buflen, 0, BLAKE2B_BLOCKBYTES - S->buflen ); /* Padding */ 1861 | + blake2b_compress( S, S->buf ); 1862 | + 1863 | + for( i = 0; i < 8; ++i ) /* Output full hash to temp buffer */ 1864 | + store64( buffer + sizeof( S->h[i] ) * i, S->h[i] ); 1865 | + 1866 | + memcpy( out, buffer, S->outlen ); 1867 | + secure_zero_memory(buffer, sizeof(buffer)); 1868 | + return 0; 1869 | +} 1870 | + 1871 | +/* inlen, at least, should be uint64_t. Others can be size_t. */ 1872 | +int blake2b( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen ) 1873 | +{ 1874 | + blake2b_state S[1]; 1875 | + 1876 | + /* Verify parameters */ 1877 | + if ( NULL == in && inlen > 0 ) return -1; 1878 | + 1879 | + if ( NULL == out ) return -1; 1880 | + 1881 | + if( NULL == key && keylen > 0 ) return -1; 1882 | + 1883 | + if( !outlen || outlen > BLAKE2B_OUTBYTES ) return -1; 1884 | + 1885 | + if( keylen > BLAKE2B_KEYBYTES ) return -1; 1886 | + 1887 | + if( keylen > 0 ) 1888 | + { 1889 | + if( blake2b_init_key( S, outlen, key, keylen ) < 0 ) return -1; 1890 | + } 1891 | + else 1892 | + { 1893 | + if( blake2b_init( S, outlen ) < 0 ) return -1; 1894 | + } 1895 | + 1896 | + blake2b_update( S, ( const uint8_t * )in, inlen ); 1897 | + blake2b_final( S, out, outlen ); 1898 | + return 0; 1899 | +} 1900 | + 1901 | +int blake2( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen ) { 1902 | + return blake2b(out, outlen, in, inlen, key, keylen); 1903 | +} 1904 | + 1905 | +#if defined(SUPERCOP) 1906 | +int crypto_hash( unsigned char *out, unsigned char *in, unsigned long long inlen ) 1907 | +{ 1908 | + return blake2b( out, BLAKE2B_OUTBYTES, in, inlen, NULL, 0 ); 1909 | +} 1910 | +#endif 1911 | + 1912 | +#if defined(BLAKE2B_SELFTEST) 1913 | +#include 1914 | +#include "blake2-kat.h" 1915 | +int main( void ) 1916 | +{ 1917 | + uint8_t key[BLAKE2B_KEYBYTES]; 1918 | + uint8_t buf[BLAKE2_KAT_LENGTH]; 1919 | + size_t i, step; 1920 | + 1921 | + for( i = 0; i < BLAKE2B_KEYBYTES; ++i ) 1922 | + key[i] = ( uint8_t )i; 1923 | + 1924 | + for( i = 0; i < BLAKE2_KAT_LENGTH; ++i ) 1925 | + buf[i] = ( uint8_t )i; 1926 | + 1927 | + /* Test simple API */ 1928 | + for( i = 0; i < BLAKE2_KAT_LENGTH; ++i ) 1929 | + { 1930 | + uint8_t hash[BLAKE2B_OUTBYTES]; 1931 | + blake2b( hash, BLAKE2B_OUTBYTES, buf, i, key, BLAKE2B_KEYBYTES ); 1932 | + 1933 | + if( 0 != memcmp( hash, blake2b_keyed_kat[i], BLAKE2B_OUTBYTES ) ) 1934 | + { 1935 | + goto fail; 1936 | + } 1937 | + } 1938 | + 1939 | + /* Test streaming API */ 1940 | + for(step = 1; step < BLAKE2B_BLOCKBYTES; ++step) { 1941 | + for (i = 0; i < BLAKE2_KAT_LENGTH; ++i) { 1942 | + uint8_t hash[BLAKE2B_OUTBYTES]; 1943 | + blake2b_state S; 1944 | + uint8_t * p = buf; 1945 | + size_t mlen = i; 1946 | + int err = 0; 1947 | + 1948 | + if( (err = blake2b_init_key(&S, BLAKE2B_OUTBYTES, key, BLAKE2B_KEYBYTES)) < 0 ) { 1949 | + goto fail; 1950 | + } 1951 | + 1952 | + while (mlen >= step) { 1953 | + if ( (err = blake2b_update(&S, p, step)) < 0 ) { 1954 | + goto fail; 1955 | + } 1956 | + mlen -= step; 1957 | + p += step; 1958 | + } 1959 | + if ( (err = blake2b_update(&S, p, mlen)) < 0) { 1960 | + goto fail; 1961 | + } 1962 | + if ( (err = blake2b_final(&S, hash, BLAKE2B_OUTBYTES)) < 0) { 1963 | + goto fail; 1964 | + } 1965 | + 1966 | + if (0 != memcmp(hash, blake2b_keyed_kat[i], BLAKE2B_OUTBYTES)) { 1967 | + goto fail; 1968 | + } 1969 | + } 1970 | + } 1971 | + 1972 | + puts( "ok" ); 1973 | + return 0; 1974 | +fail: 1975 | + puts("error"); 1976 | + return -1; 1977 | +} 1978 | +#endif 1979 | diff --git a/sdk/BUILD.gn b/sdk/BUILD.gn 1980 | index e76b68c92e..fbc7f16b90 100644 1981 | --- a/sdk/BUILD.gn 1982 | +++ b/sdk/BUILD.gn 1983 | @@ -1053,6 +1053,15 @@ if (is_ios || is_mac) { 1984 | "objc/api/peerconnection/RTCVideoTrack+Private.h", 1985 | "objc/api/peerconnection/RTCVideoTrack.h", 1986 | "objc/api/peerconnection/RTCVideoTrack.mm", 1987 | + "objc/api/peerconnection/ThreemaGroupCallFrameCryptoContext+Private.h", 1988 | + "objc/api/peerconnection/ThreemaGroupCallFrameCryptoContext.h", 1989 | + "objc/api/peerconnection/ThreemaGroupCallFrameCryptoContext.mm", 1990 | + "objc/api/peerconnection/ThreemaGroupCallFrameCryptoDecryptor+Private.h", 1991 | + "objc/api/peerconnection/ThreemaGroupCallFrameCryptoDecryptor.h", 1992 | + "objc/api/peerconnection/ThreemaGroupCallFrameCryptoDecryptor.mm", 1993 | + "objc/api/peerconnection/ThreemaGroupCallFrameCryptoEncryptor+Private.h", 1994 | + "objc/api/peerconnection/ThreemaGroupCallFrameCryptoEncryptor.h", 1995 | + "objc/api/peerconnection/ThreemaGroupCallFrameCryptoEncryptor.mm", 1996 | ] 1997 | 1998 | configs += [ 1999 | @@ -1079,6 +1088,7 @@ if (is_ios || is_mac) { 2000 | "../api:dtmf_sender_interface", 2001 | "../api:enable_media", 2002 | "../api:field_trials_view", 2003 | + "../api:frame_transformer_interface", 2004 | "../api:libjingle_peerconnection_api", 2005 | "../api:media_stream_interface", 2006 | "../api:rtc_event_log_output_file", 2007 | @@ -1107,6 +1117,7 @@ if (is_ios || is_mac) { 2008 | "../media:rtc_media_base", 2009 | "../modules/video_coding:video_codec_interface", 2010 | "../pc:peer_connection_factory", 2011 | + "../pc:threema_group_call_frame_transformer", 2012 | "../pc:webrtc_sdp", 2013 | "../rtc_base:checks", 2014 | "../rtc_base:event_tracer", 2015 | @@ -1122,6 +1133,7 @@ if (is_ios || is_mac) { 2016 | "../stats:rtc_stats", 2017 | "../system_wrappers:field_trial", 2018 | "../system_wrappers:metrics", 2019 | + "//third_party/boringssl", 2020 | ] 2021 | 2022 | if (is_ios) { 2023 | @@ -1367,6 +1379,9 @@ if (is_ios || is_mac) { 2024 | "objc/api/peerconnection/RTCCryptoOptions.h", 2025 | "objc/api/peerconnection/RTCVideoSource.h", 2026 | "objc/api/peerconnection/RTCVideoTrack.h", 2027 | + "objc/api/peerconnection/ThreemaGroupCallFrameCryptoContext.h", 2028 | + "objc/api/peerconnection/ThreemaGroupCallFrameCryptoEncryptor.h", 2029 | + "objc/api/peerconnection/ThreemaGroupCallFrameCryptoDecryptor.h", 2030 | "objc/api/video_codec/RTCVideoCodecConstants.h", 2031 | "objc/api/video_codec/RTCVideoDecoderVP8.h", 2032 | "objc/api/video_codec/RTCVideoDecoderVP9.h", 2033 | @@ -1482,6 +1497,9 @@ if (is_ios || is_mac) { 2034 | "objc/api/peerconnection/RTCTracing.h", 2035 | "objc/api/peerconnection/RTCVideoSource.h", 2036 | "objc/api/peerconnection/RTCVideoTrack.h", 2037 | + "objc/api/peerconnection/ThreemaGroupCallFrameCryptoContext.h", 2038 | + "objc/api/peerconnection/ThreemaGroupCallFrameCryptoDecryptor.h", 2039 | + "objc/api/peerconnection/ThreemaGroupCallFrameCryptoEncryptor.h", 2040 | "objc/api/video_codec/RTCVideoDecoderAV1.h", 2041 | "objc/api/video_codec/RTCVideoDecoderVP8.h", 2042 | "objc/api/video_codec/RTCVideoDecoderVP9.h", 2043 | diff --git a/sdk/android/BUILD.gn b/sdk/android/BUILD.gn 2044 | index 340f9fd5d9..28299e54d5 100644 2045 | --- a/sdk/android/BUILD.gn 2046 | +++ b/sdk/android/BUILD.gn 2047 | @@ -314,6 +314,7 @@ if (is_android) { 2048 | "api/org/webrtc/SessionDescription.java", 2049 | "api/org/webrtc/StatsObserver.java", 2050 | "api/org/webrtc/StatsReport.java", 2051 | + "api/org/webrtc/ThreemaGroupCallFrameCryptoContext.java", 2052 | "api/org/webrtc/TurnCustomizer.java", 2053 | "api/org/webrtc/VideoProcessor.java", 2054 | "api/org/webrtc/VideoSource.java", 2055 | @@ -778,6 +779,8 @@ if (current_os == "linux" || is_android) { 2056 | "src/jni/pc/ssl_certificate_verifier_wrapper.h", 2057 | "src/jni/pc/stats_observer.cc", 2058 | "src/jni/pc/stats_observer.h", 2059 | + "src/jni/pc/threema_group_call_frame_crypto_context.cc", 2060 | + "src/jni/pc/threema_group_call_frame_crypto_context.h", 2061 | "src/jni/pc/turn_customizer.cc", 2062 | "src/jni/pc/turn_customizer.h", 2063 | ] 2064 | @@ -794,6 +797,7 @@ if (current_os == "linux" || is_android) { 2065 | "..:media_constraints", 2066 | "../../api:dtmf_sender_interface", 2067 | "../../api:enable_media", 2068 | + "../../api:frame_transformer_interface", 2069 | "../../api:libjingle_peerconnection_api", 2070 | "../../api:media_stream_interface", 2071 | "../../api:rtc_event_log_output_file", 2072 | @@ -814,6 +818,7 @@ if (current_os == "linux" || is_android) { 2073 | "../../modules/utility", 2074 | "../../pc:media_stream_observer", 2075 | "../../pc:peer_connection_factory", 2076 | + "../../pc:threema_group_call_frame_transformer", 2077 | "../../pc:webrtc_sdp", 2078 | "../../rtc_base:checks", 2079 | "../../rtc_base:event_tracer", 2080 | @@ -828,6 +833,7 @@ if (current_os == "linux" || is_android) { 2081 | "../../stats:rtc_stats", 2082 | "../../system_wrappers:field_trial", 2083 | "//third_party/abseil-cpp/absl/memory", 2084 | + "//third_party/boringssl", 2085 | "//third_party/jni_zero", 2086 | ] 2087 | } 2088 | @@ -1453,6 +1459,7 @@ if (current_os == "linux" || is_android) { 2089 | "api/org/webrtc/SessionDescription.java", 2090 | "api/org/webrtc/StatsObserver.java", 2091 | "api/org/webrtc/StatsReport.java", 2092 | + "api/org/webrtc/ThreemaGroupCallFrameCryptoContext.java", 2093 | "api/org/webrtc/TurnCustomizer.java", 2094 | ] 2095 | namespace = "webrtc::jni" 2096 | diff --git a/sdk/android/api/org/webrtc/ThreemaGroupCallFrameCryptoContext.java b/sdk/android/api/org/webrtc/ThreemaGroupCallFrameCryptoContext.java 2097 | new file mode 100644 2098 | index 0000000000..a4ba76feaa 2099 | --- /dev/null 2100 | +++ b/sdk/android/api/org/webrtc/ThreemaGroupCallFrameCryptoContext.java 2101 | @@ -0,0 +1,198 @@ 2102 | +/* 2103 | + * Copyright 2023 Threema GmbH. All Rights Reserved. 2104 | + * 2105 | + * Use of this source code is governed by a BSD-style license that can be found 2106 | + * in the LICENSE file in the root of the source tree. 2107 | + */ 2108 | + 2109 | +package org.webrtc; 2110 | + 2111 | +import androidx.annotation.NonNull; 2112 | +import androidx.annotation.Nullable; 2113 | +import java.util.HashMap; 2114 | +import java.util.Map; 2115 | + 2116 | +/** 2117 | + * Context holding references to encryptor and decryptors. 2118 | + * 2119 | + * IMPORTANT: This is **NOT** thread safe. It is your job to always call it from 2120 | + * the same thread or use synchronisation primitives. 2121 | + */ 2122 | +public class ThreemaGroupCallFrameCryptoContext { 2123 | + private static native long nativeCreate(@NonNull byte[] gckh); 2124 | + 2125 | + private static native void nativeEncryptorSetPcmk( 2126 | + long context, @NonNull byte[] pcmk, short epoch, short ratchetCounter); 2127 | + private static native void nativeEncryptorAttach( 2128 | + long context, long nativeRtpSender, @NonNull String tag); 2129 | + 2130 | + private static native void nativeDecryptorAdd(long context, short participantId); 2131 | + private static native void nativeDecryptorRemove(long context, short participantId); 2132 | + private static native void nativeDecryptorAddPcmk( 2133 | + long context, short participantId, @NonNull byte[] pcmk, short epoch, short ratchetCounter); 2134 | + private static native void nativeDecryptorAttach( 2135 | + long context, short participantId, long nativeRtpReceiver, @NonNull String tag); 2136 | + 2137 | + private static short MIDS_MAX = 790; 2138 | + 2139 | + private static void validateMediaKey(@NonNull byte[] pcmk, short epoch, short ratchetCounter) { 2140 | + if (pcmk.length != 32) { 2141 | + throw new IllegalArgumentException("'pcmk' must be 32 bytes"); 2142 | + } 2143 | + if (epoch < 0 || epoch > 255) { 2144 | + throw new IllegalArgumentException("'epoch' must be a u8"); 2145 | + } 2146 | + if (ratchetCounter < 0 || ratchetCounter > 255) { 2147 | + throw new IllegalArgumentException("'ratchetCounter' must be a u8"); 2148 | + } 2149 | + } 2150 | + 2151 | + private static void validateParticipantId(short participantId) { 2152 | + if (participantId < 0 || participantId >= MIDS_MAX) { 2153 | + throw new IllegalArgumentException("'participantId' must be >= 0 and < MIDS_MAX"); 2154 | + } 2155 | + } 2156 | + 2157 | + /** Encrypts outgoing audio/video frames. */ 2158 | + public class Encryptor { 2159 | + private Encryptor() {} 2160 | + 2161 | + /** Initialise or update the key material of the encryptor. */ 2162 | + public void setPcmk(@NonNull byte[] pcmk, short epoch, short ratchetCounter) { 2163 | + validateMediaKey(pcmk, epoch, ratchetCounter); 2164 | + nativeEncryptorSetPcmk(ensureContext(), pcmk, epoch, ratchetCounter); 2165 | + } 2166 | + 2167 | + /** 2168 | + * Attach an RTP sender to the encryptor, encrypting all upcoming 2169 | + * audio/video frames. 2170 | + */ 2171 | + public void attach(@NonNull RtpSender sender, @NonNull String tag) { 2172 | + final long nativeSender = sender.getNativeRtpSender(); 2173 | + nativeEncryptorAttach(ensureContext(), nativeSender, tag); 2174 | + } 2175 | + } 2176 | + 2177 | + /** Decrypts incoming audio/video frames of another participant. */ 2178 | + public class Decryptor { 2179 | + public final short participantId; 2180 | + private boolean active = true; 2181 | + 2182 | + private Decryptor(short participantId) { 2183 | + validateParticipantId(participantId); 2184 | + this.participantId = participantId; 2185 | + nativeDecryptorAdd(ensureContext(), participantId); 2186 | + } 2187 | + 2188 | + /** Initialise or update the key material of the decryptor. */ 2189 | + public void addPcmk(@NonNull byte[] pcmk, short epoch, short ratchetCounter) { 2190 | + ensureActive(); 2191 | + validateMediaKey(pcmk, epoch, ratchetCounter); 2192 | + nativeDecryptorAddPcmk(ensureContext(), participantId, pcmk, epoch, ratchetCounter); 2193 | + } 2194 | + 2195 | + /** 2196 | + * Attach an RTP receiver to the decryptor, decrypting all upcoming 2197 | + * audio/video frames. 2198 | + */ 2199 | + public void attach(@NonNull RtpReceiver receiver, @NonNull String tag) { 2200 | + ensureActive(); 2201 | + final long nativeReceiver = receiver.getNativeRtpReceiver(); 2202 | + nativeDecryptorAttach(ensureContext(), participantId, nativeReceiver, tag); 2203 | + } 2204 | + 2205 | + private void ensureActive() { 2206 | + if (!active) { 2207 | + throw new IllegalStateException("Decryptor has been removed"); 2208 | + } 2209 | + } 2210 | + 2211 | + /** 2212 | + * Remove the decryptor. Note that it doesn't detach from the RTP receiver 2213 | + * for simplicity. 2214 | + */ 2215 | + private void remove() { 2216 | + ensureActive(); 2217 | + nativeDecryptorRemove(ensureContext(), participantId); 2218 | + active = false; 2219 | + } 2220 | + } 2221 | + 2222 | + private long nativeContext; 2223 | + private @NonNull final Encryptor encryptor; 2224 | + private @NonNull final Map decryptors = new HashMap<>(); 2225 | + 2226 | + /** Initialise the context with initial key material. */ 2227 | + public ThreemaGroupCallFrameCryptoContext(@NonNull byte[] gckh) { 2228 | + nativeContext = nativeCreate(gckh); 2229 | + encryptor = new Encryptor(); 2230 | + } 2231 | + 2232 | + /** Returns the encryptor associated with this context. */ 2233 | + public @NonNull Encryptor getEncryptor() { 2234 | + ensureContext(); 2235 | + return encryptor; 2236 | + } 2237 | + 2238 | + /** 2239 | + * Returns the decryptor for the given participant ID iff it exists. Returns 2240 | + * null otherwise. 2241 | + */ 2242 | + public @Nullable Decryptor getDecryptor(short participantId) { 2243 | + ensureContext(); 2244 | + return decryptors.get(participantId); 2245 | + } 2246 | + 2247 | + /** 2248 | + * Adds a decryptor for the given participant ID. After calling this it can be 2249 | + * obtained by calling `getDecryptor` with the same participant ID until it is 2250 | + * removed by `removeDecryptor`. 2251 | + * 2252 | + * Throws an exception in case a decryptor with the same participant ID 2253 | + * already exists. 2254 | + */ 2255 | + public @NonNull Decryptor addDecryptor(short participantId) { 2256 | + ensureContext(); 2257 | + if (decryptors.containsKey(participantId)) { 2258 | + throw new IllegalArgumentException("Decryptor for the given participant id already existing"); 2259 | + } 2260 | + final Decryptor decryptor = new Decryptor(participantId); 2261 | + decryptors.put(participantId, decryptor); 2262 | + return decryptor; 2263 | + } 2264 | + 2265 | + /** 2266 | + * Removes the decyrptor for this participant ID. Does nothing if there is no 2267 | + * decryptor for the provided participant ID. 2268 | + */ 2269 | + public void removeDecryptor(short participantId) { 2270 | + ensureContext(); 2271 | + final Decryptor decryptor = decryptors.remove(participantId); 2272 | + if (decryptor != null) { 2273 | + decryptor.remove(); 2274 | + } 2275 | + } 2276 | + 2277 | + /** 2278 | + * Disposes this context and its associated native part. Removes all 2279 | + * associated encryptor and decryptors. 2280 | + */ 2281 | + public void dispose() { 2282 | + if (nativeContext == 0) { 2283 | + return; 2284 | + } 2285 | + for (final Decryptor decryptor : decryptors.values()) { 2286 | + decryptor.remove(); 2287 | + }; 2288 | + decryptors.clear(); 2289 | + JniCommon.nativeReleaseRef(nativeContext); 2290 | + nativeContext = 0; 2291 | + } 2292 | + 2293 | + private long ensureContext() { 2294 | + if (nativeContext == 0) { 2295 | + throw new IllegalStateException("ThreemaGroupCallFrameCryptoContext has been disposed"); 2296 | + } 2297 | + return nativeContext; 2298 | + } 2299 | +} 2300 | diff --git a/sdk/android/src/jni/pc/threema_group_call_frame_crypto_context.cc b/sdk/android/src/jni/pc/threema_group_call_frame_crypto_context.cc 2301 | new file mode 100644 2302 | index 0000000000..6c225eab04 2303 | --- /dev/null 2304 | +++ b/sdk/android/src/jni/pc/threema_group_call_frame_crypto_context.cc 2305 | @@ -0,0 +1,138 @@ 2306 | +/* 2307 | + * Copyright 2022 Threema GmbH. All Rights Reserved. 2308 | + * 2309 | + * Use of this source code is governed by a BSD-style license 2310 | + * that can be found in the LICENSE file in the root of the source 2311 | + * tree. 2312 | + */ 2313 | + 2314 | +#include "sdk/android/src/jni/pc/threema_group_call_frame_crypto_context.h" 2315 | + 2316 | +#include "api/make_ref_counted.h" 2317 | +#include "api/rtp_receiver_interface.h" 2318 | +#include "api/rtp_sender_interface.h" 2319 | +#include "pc/threema_group_call_frame_transformer.h" 2320 | +#include "rtc_base/ref_count.h" 2321 | +#include "sdk/android/generated_peerconnection_jni/ThreemaGroupCallFrameCryptoContext_jni.h" 2322 | +#include "sdk/android/native_api/jni/java_types.h" 2323 | +#include "sdk/android/src/jni/jni_helpers.h" 2324 | + 2325 | +namespace webrtc { 2326 | +namespace jni { 2327 | + 2328 | +static threema::Codec GetCodec(cricket::MediaType media_type) { 2329 | + // Determine codec 2330 | + // 2331 | + // Note: This is making an assumption that audio is always Opus and video is 2332 | + // always VP8. If this ever changes, you will have to provide those 2333 | + // parameters. 2334 | + switch (media_type) { 2335 | + case cricket::MediaType::MEDIA_TYPE_AUDIO: 2336 | + return threema::Codec::kOpus; 2337 | + case cricket::MediaType::MEDIA_TYPE_VIDEO: 2338 | + return threema::Codec::kVp8; 2339 | + default: 2340 | + RTC_CHECK_NOTREACHED(); 2341 | + } 2342 | +} 2343 | + 2344 | +static jlong JNI_ThreemaGroupCallFrameCryptoContext_Create( 2345 | + JNIEnv* jni, 2346 | + const jni_zero::JavaParamRef& j_gckh) { 2347 | + auto* gckh = jni->GetByteArrayElements(j_gckh.obj(), nullptr); 2348 | + auto context = webrtc::make_ref_counted( 2349 | + reinterpret_cast(gckh)); 2350 | + // Note: Ownership is moved to the Java object. 2351 | + return jlongFromPointer(context.release()); 2352 | +} 2353 | + 2354 | +// IMPORTANT: Caller must ensure that `j_pcmk` is exactly 32 bytes and 2355 | +// `j_epoch` and `j_ratchet_counter` can be safely casted to u8! 2356 | +static void JNI_ThreemaGroupCallFrameCryptoContext_EncryptorSetPcmk( 2357 | + JNIEnv* jni, 2358 | + jlong j_context_pointer, 2359 | + const jni_zero::JavaParamRef& j_pcmk, 2360 | + jshort j_epoch, 2361 | + jshort j_ratchet_counter) { 2362 | + auto* pcmk = jni->GetByteArrayElements(j_pcmk.obj(), nullptr); 2363 | + auto* context = reinterpret_cast( 2364 | + j_context_pointer); 2365 | + context->GetEncryptor().SetPcmk(reinterpret_cast(pcmk), 2366 | + (uint8_t)j_epoch, (uint8_t)j_ratchet_counter); 2367 | +} 2368 | + 2369 | +static void JNI_ThreemaGroupCallFrameCryptoContext_EncryptorAttach( 2370 | + JNIEnv* jni, 2371 | + jlong j_context_pointer, 2372 | + jlong j_rtp_sender_pointer, 2373 | + const JavaParamRef& j_tag) { 2374 | + auto* sender = reinterpret_cast(j_rtp_sender_pointer); 2375 | + auto tag = JavaToNativeString(jni, j_tag); 2376 | + auto* context = reinterpret_cast( 2377 | + j_context_pointer); 2378 | + auto transformer = context->GetEncryptor().CreateEncryptor( 2379 | + tag, GetCodec(sender->media_type())); 2380 | + sender->SetEncoderToPacketizerFrameTransformer(std::move(transformer)); 2381 | +} 2382 | + 2383 | +// IMPORTANT: Caller must ensure that `j_participant_id` is a valid participant 2384 | +// id! 2385 | +static void JNI_ThreemaGroupCallFrameCryptoContext_DecryptorAdd( 2386 | + JNIEnv* jni, 2387 | + jlong j_context_pointer, 2388 | + jshort j_participant_id) { 2389 | + auto* context = reinterpret_cast( 2390 | + j_context_pointer); 2391 | + context->AddDecryptor((uint16_t)j_participant_id); 2392 | +} 2393 | + 2394 | +// IMPORTANT: Caller must ensure that `j_participant_id` is a valid participant 2395 | +// id! 2396 | +static void JNI_ThreemaGroupCallFrameCryptoContext_DecryptorRemove( 2397 | + JNIEnv* jni, 2398 | + jlong j_context_pointer, 2399 | + jshort j_participant_id) { 2400 | + auto* context = reinterpret_cast( 2401 | + j_context_pointer); 2402 | + context->RemoveDecryptor((uint16_t)j_participant_id); 2403 | +} 2404 | + 2405 | +// IMPORTANT: Caller must ensure that `j_participant_id` is a valid participant 2406 | +// id, `j_pcmk` is exactly 32 bytes and `j_epoch` and `j_ratchet_counter` can be 2407 | +// safely casted to u8! 2408 | +static void JNI_ThreemaGroupCallFrameCryptoContext_DecryptorAddPcmk( 2409 | + JNIEnv* jni, 2410 | + jlong j_context_pointer, 2411 | + jshort j_participant_id, 2412 | + const jni_zero::JavaParamRef& j_pcmk, 2413 | + jshort j_epoch, 2414 | + jshort j_ratchet_counter) { 2415 | + auto* pcmk = jni->GetByteArrayElements(j_pcmk.obj(), nullptr); 2416 | + auto* context = reinterpret_cast( 2417 | + j_context_pointer); 2418 | + context->GetDecryptor((uint16_t)j_participant_id) 2419 | + .AddPcmk(reinterpret_cast(pcmk), (uint8_t)j_epoch, 2420 | + (uint8_t)j_ratchet_counter); 2421 | +} 2422 | + 2423 | +// IMPORTANT: Caller must ensure that `j_participant_id` is a valid participant 2424 | +// id! 2425 | +static void JNI_ThreemaGroupCallFrameCryptoContext_DecryptorAttach( 2426 | + JNIEnv* jni, 2427 | + jlong j_context_pointer, 2428 | + jshort j_participant_id, 2429 | + jlong j_rtp_receiver_pointer, 2430 | + const JavaParamRef& j_tag) { 2431 | + auto* receiver = 2432 | + reinterpret_cast(j_rtp_receiver_pointer); 2433 | + auto tag = JavaToNativeString(jni, j_tag); 2434 | + auto* context = reinterpret_cast( 2435 | + j_context_pointer); 2436 | + auto transformer = 2437 | + context->GetDecryptor((uint16_t)j_participant_id) 2438 | + .CreateDecryptor(tag, GetCodec(receiver->media_type())); 2439 | + receiver->SetDepacketizerToDecoderFrameTransformer(std::move(transformer)); 2440 | +} 2441 | + 2442 | +} // namespace jni 2443 | +} // namespace webrtc 2444 | diff --git a/sdk/android/src/jni/pc/threema_group_call_frame_crypto_context.h b/sdk/android/src/jni/pc/threema_group_call_frame_crypto_context.h 2445 | new file mode 100644 2446 | index 0000000000..5d4b0e60a7 2447 | --- /dev/null 2448 | +++ b/sdk/android/src/jni/pc/threema_group_call_frame_crypto_context.h 2449 | @@ -0,0 +1,21 @@ 2450 | +/* 2451 | + * Copyright 2022 Threema GmbH. All Rights Reserved. 2452 | + * 2453 | + * Use of this source code is governed by a BSD-style license 2454 | + * that can be found in the LICENSE file in the root of the source 2455 | + * tree. 2456 | + */ 2457 | + 2458 | +#ifndef SDK_ANDROID_SRC_JNI_PC_THREEMA_GROUP_CALL_FRAME_CRYPTO_CONTEXT_H_ 2459 | +#define SDK_ANDROID_SRC_JNI_PC_THREEMA_GROUP_CALL_FRAME_CRYPTO_CONTEXT_H_ 2460 | + 2461 | +#include 2462 | + 2463 | +#include "pc/threema_group_call_frame_transformer.h" 2464 | +#include "sdk/android/native_api/jni/scoped_java_ref.h" 2465 | + 2466 | +namespace webrtc { 2467 | +namespace jni {} // namespace jni 2468 | +} // namespace webrtc 2469 | + 2470 | +#endif // SDK_ANDROID_SRC_JNI_PC_THREEMA_GROUP_CALL_FRAME_CRYPTO_CONTEXT_H_ 2471 | diff --git a/sdk/objc/api/peerconnection/ThreemaGroupCallFrameCryptoContext+Private.h b/sdk/objc/api/peerconnection/ThreemaGroupCallFrameCryptoContext+Private.h 2472 | new file mode 100644 2473 | index 0000000000..a290032d3c 2474 | --- /dev/null 2475 | +++ b/sdk/objc/api/peerconnection/ThreemaGroupCallFrameCryptoContext+Private.h 2476 | @@ -0,0 +1,16 @@ 2477 | +#import 2478 | + 2479 | +#import "RTCMediaStreamTrack.h" 2480 | + 2481 | +NS_ASSUME_NONNULL_BEGIN 2482 | + 2483 | +RTC_OBJC_EXPORT 2484 | +@interface RTC_OBJC_TYPE (ThreemaGroupCallFrameCryptoContext) 2485 | + 2486 | ++ (void)validateMediaKey:(NSData *)pcmk; 2487 | + 2488 | ++ (threema::Codec)getCodec:(RTCRtpMediaType)mediaType; 2489 | + 2490 | +@end 2491 | + 2492 | +NS_ASSUME_NONNULL_END 2493 | \ No newline at end of file 2494 | diff --git a/sdk/objc/api/peerconnection/ThreemaGroupCallFrameCryptoContext.h b/sdk/objc/api/peerconnection/ThreemaGroupCallFrameCryptoContext.h 2495 | new file mode 100644 2496 | index 0000000000..8f14f8acb5 2497 | --- /dev/null 2498 | +++ b/sdk/objc/api/peerconnection/ThreemaGroupCallFrameCryptoContext.h 2499 | @@ -0,0 +1,76 @@ 2500 | +#import 2501 | + 2502 | +#import "RTCRtpReceiver.h" 2503 | +#import "RTCRtpSender.h" 2504 | + 2505 | +#import "ThreemaGroupCallFrameCryptoDecryptor.h" 2506 | +#import "ThreemaGroupCallFrameCryptoEncryptor.h" 2507 | + 2508 | +NS_ASSUME_NONNULL_BEGIN 2509 | + 2510 | +RTC_OBJC_EXPORT 2511 | +@protocol RTC_OBJC_TYPE 2512 | +(ThreemaGroupCallFrameCryptoContextProtocol) 2513 | + 2514 | + - (instancetype)initWithgckh : (NSData *)gckh; 2515 | + 2516 | +- (ThreemaGroupCallFrameCryptoEncryptor *)getEncryptor; 2517 | + 2518 | +- (nullable ThreemaGroupCallFrameCryptoDecryptor *)getDecryptorFor: 2519 | + (UInt16)participantId; 2520 | + 2521 | +- (ThreemaGroupCallFrameCryptoDecryptor *)addDecryptorFor:(UInt16)participantId; 2522 | + 2523 | +- (void)removeDecryptorFor:(UInt16)participantId; 2524 | + 2525 | +- (void)dispose; 2526 | + 2527 | +@end 2528 | + 2529 | +/** 2530 | + * Context holding references to encryptor and decryptors. 2531 | + * 2532 | + * IMPORTANT: This is **NOT** thread safe. It is your job to always call it from 2533 | + * the same thread or use synchronisation primitives. 2534 | + */ 2535 | +RTC_OBJC_EXPORT 2536 | +@interface RTC_OBJC_TYPE (ThreemaGroupCallFrameCryptoContext) : NSObject 2537 | + 2538 | +/** Initialise the context with initial key material. */ 2539 | +- (instancetype)initWithgckh:(NSData *)gckh; 2540 | + 2541 | +/** Returns the encryptor associated with this context. */ 2542 | +- (ThreemaGroupCallFrameCryptoEncryptor *)getEncryptor; 2543 | + 2544 | +/** 2545 | + * Returns the decryptor for the given participant ID iff it exists. Returns nil 2546 | + * otherwise. 2547 | + */ 2548 | +- (nullable ThreemaGroupCallFrameCryptoDecryptor *)getDecryptorFor: 2549 | + (UInt16)participantId; 2550 | + 2551 | +/** 2552 | + * Adds a decryptor for the given participant ID. After calling this it can be 2553 | + * obtained by calling `getDecryptor:(participantId)` until it is removed by 2554 | + * `removeDecryptor(participantId)`. 2555 | + * 2556 | + * Throws an exception in case a decryptor with the same participant ID already 2557 | + * exists. 2558 | + */ 2559 | +- (ThreemaGroupCallFrameCryptoDecryptor *)addDecryptorFor:(UInt16)participantId; 2560 | + 2561 | +/** 2562 | + * Removes the decyrptor for this participant ID. Does nothing if there is no 2563 | + * decryptor for the provided participant ID. 2564 | + */ 2565 | +- (void)removeDecryptorFor:(UInt16)participantId; 2566 | + 2567 | +/** 2568 | + * Disposes this context and its associated native part. Removes all associated 2569 | + * encryptor and decryptors. 2570 | + */ 2571 | +- (void)dispose; 2572 | + 2573 | +@end 2574 | + 2575 | +NS_ASSUME_NONNULL_END 2576 | \ No newline at end of file 2577 | diff --git a/sdk/objc/api/peerconnection/ThreemaGroupCallFrameCryptoContext.mm b/sdk/objc/api/peerconnection/ThreemaGroupCallFrameCryptoContext.mm 2578 | new file mode 100644 2579 | index 0000000000..57271fe2b5 2580 | --- /dev/null 2581 | +++ b/sdk/objc/api/peerconnection/ThreemaGroupCallFrameCryptoContext.mm 2582 | @@ -0,0 +1,124 @@ 2583 | +#import 2584 | + 2585 | +#import "RTCMediaStreamTrack.h" 2586 | +#import "RTCRtpReceiver.h" 2587 | +#import "RTCRtpSender.h" 2588 | + 2589 | +#import "pc/threema_group_call_frame_transformer.h" 2590 | + 2591 | +#include "api/rtp_receiver_interface.h" 2592 | +#include "api/rtp_sender_interface.h" 2593 | + 2594 | +#import "ThreemaGroupCallFrameCryptoContext.h" 2595 | +#import "ThreemaGroupCallFrameCryptoDecryptor+Private.h" 2596 | +#import "ThreemaGroupCallFrameCryptoDecryptor.h" 2597 | +#import "ThreemaGroupCallFrameCryptoEncryptor+Private.h" 2598 | +#import "ThreemaGroupCallFrameCryptoEncryptor.h" 2599 | + 2600 | +@implementation ThreemaGroupCallFrameCryptoContext { 2601 | + webrtc::scoped_refptr context; 2602 | + NSMutableDictionary 2603 | + *decryptorDict; 2604 | + ThreemaGroupCallFrameCryptoEncryptor *encryptor; 2605 | +} 2606 | + 2607 | +- (instancetype)initWithgckh:(NSData *)gckh { 2608 | + self = [super init]; 2609 | + if (self) { 2610 | + self->context = 2611 | + webrtc::make_ref_counted( 2612 | + (const uint8_t *)gckh.bytes); 2613 | + self->decryptorDict = [[NSMutableDictionary alloc] init]; 2614 | + 2615 | + self->encryptor = [[ThreemaGroupCallFrameCryptoEncryptor alloc] 2616 | + initWithContext:self->context]; 2617 | + } 2618 | + return self; 2619 | +} 2620 | + 2621 | +- (ThreemaGroupCallFrameCryptoEncryptor *)getEncryptor { 2622 | + [self ensureContext]; 2623 | + return encryptor; 2624 | +} 2625 | + 2626 | +- (nullable ThreemaGroupCallFrameCryptoDecryptor *)getDecryptorFor: 2627 | + (UInt16)participantId { 2628 | + [self ensureContext]; 2629 | + NSString *key = [NSString stringWithFormat:@"%hu", participantId]; 2630 | + return decryptorDict[key]; 2631 | +} 2632 | + 2633 | +- (ThreemaGroupCallFrameCryptoDecryptor *)addDecryptorFor: 2634 | + (UInt16)participantId { 2635 | + [self ensureContext]; 2636 | + NSString *key = [NSString stringWithFormat:@"%hu", participantId]; 2637 | + 2638 | + if (decryptorDict[key] != nil) { 2639 | + @throw [NSException exceptionWithName:NSInvalidArgumentException 2640 | + reason:@"Decryptor for the given " 2641 | + @"participant id already existing." 2642 | + userInfo:nil]; 2643 | + } 2644 | + 2645 | + ThreemaGroupCallFrameCryptoDecryptor *decryptor = 2646 | + [[ThreemaGroupCallFrameCryptoDecryptor alloc] 2647 | + initWithParticipantId:participantId 2648 | + context:self->context]; 2649 | + decryptorDict[key] = decryptor; 2650 | + 2651 | + return decryptor; 2652 | +} 2653 | + 2654 | +- (void)removeDecryptorFor:(UInt16)participantId { 2655 | + [self ensureContext]; 2656 | + NSString *key = [NSString stringWithFormat:@"%hu", participantId]; 2657 | + [decryptorDict removeObjectForKey:key]; 2658 | +} 2659 | + 2660 | +- (void)dispose { 2661 | + if (context == nil) { 2662 | + return; 2663 | + } 2664 | + 2665 | + for (ThreemaGroupCallFrameCryptoDecryptor *decryptor in 2666 | + [decryptorDict allValues]) { 2667 | + [decryptor remove]; 2668 | + } 2669 | + 2670 | + [decryptorDict removeAllObjects]; 2671 | + 2672 | + [encryptor remove]; 2673 | + self->context = nil; 2674 | +} 2675 | + 2676 | +- (webrtc::scoped_refptr)ensureContext { 2677 | + if (self->context) { 2678 | + return self->context; 2679 | + } 2680 | + @throw [NSException exceptionWithName:NSInvalidArgumentException 2681 | + reason:@"Missing Context" 2682 | + userInfo:nil]; 2683 | +} 2684 | + 2685 | ++ (threema::Codec)getCodec:(RTCRtpMediaType)mediaType { 2686 | + if (mediaType == RTCRtpMediaTypeAudio) { 2687 | + return threema::Codec::kOpus; 2688 | + } else if (mediaType == RTCRtpMediaTypeVideo) { 2689 | + return threema::Codec::kVp8; 2690 | + } 2691 | + 2692 | + RTC_CHECK_NOTREACHED(); 2693 | + @throw [NSException exceptionWithName:NSInvalidArgumentException 2694 | + reason:@"Illegal media type" 2695 | + userInfo:nil]; 2696 | +} 2697 | + 2698 | ++ (void)validateMediaKey:(NSData *)pcmk { 2699 | + if (pcmk.length != 32) { 2700 | + @throw [NSException exceptionWithName:NSInvalidArgumentException 2701 | + reason:@"'pcmk' must be 32 bytes" 2702 | + userInfo:nil]; 2703 | + } 2704 | +} 2705 | + 2706 | +@end 2707 | \ No newline at end of file 2708 | diff --git a/sdk/objc/api/peerconnection/ThreemaGroupCallFrameCryptoDecryptor+Private.h b/sdk/objc/api/peerconnection/ThreemaGroupCallFrameCryptoDecryptor+Private.h 2709 | new file mode 100644 2710 | index 0000000000..6b5ba7fa79 2711 | --- /dev/null 2712 | +++ b/sdk/objc/api/peerconnection/ThreemaGroupCallFrameCryptoDecryptor+Private.h 2713 | @@ -0,0 +1,13 @@ 2714 | +#import "ThreemaGroupCallFrameCryptoDecryptor.h" 2715 | + 2716 | +NS_ASSUME_NONNULL_BEGIN 2717 | + 2718 | +@interface RTC_OBJC_TYPE (ThreemaGroupCallFrameCryptoDecryptor) 2719 | +() 2720 | + 2721 | + - (instancetype)initWithParticipantId : (UInt8)participantId context 2722 | + : (webrtc::scoped_refptr)context; 2723 | + 2724 | +@end 2725 | + 2726 | +NS_ASSUME_NONNULL_END 2727 | \ No newline at end of file 2728 | diff --git a/sdk/objc/api/peerconnection/ThreemaGroupCallFrameCryptoDecryptor.h b/sdk/objc/api/peerconnection/ThreemaGroupCallFrameCryptoDecryptor.h 2729 | new file mode 100644 2730 | index 0000000000..c464ae4664 2731 | --- /dev/null 2732 | +++ b/sdk/objc/api/peerconnection/ThreemaGroupCallFrameCryptoDecryptor.h 2733 | @@ -0,0 +1,50 @@ 2734 | +#import 2735 | + 2736 | +#import "RTCRtpReceiver.h" 2737 | + 2738 | +NS_ASSUME_NONNULL_BEGIN 2739 | + 2740 | +RTC_OBJC_EXPORT 2741 | +@protocol RTC_OBJC_TYPE 2742 | +(ThreemaGroupCallFrameCryptoDecryptorProtocol) 2743 | + 2744 | + - (void)addPcmk : (NSData *)pcmk epoch : (UInt8)epoch ratchetCounter 2745 | + : (UInt8)ratchetCounter; 2746 | + 2747 | +- (void)attachWithReceiver:(RTCRtpReceiver *)receiver 2748 | + mediaType:(RTCRtpMediaType)mediaType 2749 | + tag:(NSString *)tag; 2750 | + 2751 | +- (void)remove; 2752 | + 2753 | +@end 2754 | + 2755 | +/** Decrypts incoming audio/video frames of another participant. */ 2756 | +RTC_OBJC_EXPORT 2757 | +@interface RTC_OBJC_TYPE (ThreemaGroupCallFrameCryptoDecryptor) : NSObject 2758 | + 2759 | +/** The participant ID for which this decryptor will decrypt frames. */ 2760 | +@property (nonatomic, assign, readonly) UInt16 participantId; 2761 | + 2762 | +/** Initialise or update the key material of the decryptor. */ 2763 | +- (void)addPcmk:(NSData *)pcmk 2764 | + epoch:(UInt8)epoch 2765 | + ratchetCounter:(UInt8)ratchetCounter; 2766 | + 2767 | +/** 2768 | + * Attach an RTP receiver to the decryptor, decrypting all upcoming audio/video 2769 | + * frames. 2770 | + */ 2771 | +- (void)attachWithReceiver:(RTCRtpReceiver *)receiver 2772 | + mediaType:(RTCRtpMediaType)mediaType 2773 | + tag:(NSString *)tag; 2774 | + 2775 | +/** 2776 | + * Remove the decryptor. Note that it doesn't detach from the RTP receiver for 2777 | + * simplicity. 2778 | + */ 2779 | +- (void)remove; 2780 | + 2781 | +@end 2782 | + 2783 | +NS_ASSUME_NONNULL_END 2784 | \ No newline at end of file 2785 | diff --git a/sdk/objc/api/peerconnection/ThreemaGroupCallFrameCryptoDecryptor.mm b/sdk/objc/api/peerconnection/ThreemaGroupCallFrameCryptoDecryptor.mm 2786 | new file mode 100644 2787 | index 0000000000..333e319c34 2788 | --- /dev/null 2789 | +++ b/sdk/objc/api/peerconnection/ThreemaGroupCallFrameCryptoDecryptor.mm 2790 | @@ -0,0 +1,85 @@ 2791 | +#import 2792 | + 2793 | +#import "RTCRtpReceiver+Private.h" 2794 | +#import "RTCRtpReceiver.h" 2795 | + 2796 | +#import "pc/threema_group_call_frame_transformer.h" 2797 | + 2798 | +#import "ThreemaGroupCallFrameCryptoContext+Private.h" 2799 | +#import "ThreemaGroupCallFrameCryptoDecryptor.h" 2800 | + 2801 | +@implementation ThreemaGroupCallFrameCryptoDecryptor { 2802 | + webrtc::scoped_refptr context; 2803 | +} 2804 | + 2805 | +static const UInt16 MIDS_MAX = 790; 2806 | + 2807 | +- (instancetype)initWithParticipantId:(UInt16)participantId 2808 | + context:(webrtc::scoped_refptr< 2809 | + threema::GroupCallFrameCryptoContext>) 2810 | + context { 2811 | + self = [super init]; 2812 | + if (self) { 2813 | + [self validateParticipantId:participantId]; 2814 | + 2815 | + _participantId = participantId; 2816 | + 2817 | + self->context = context; 2818 | + 2819 | + auto ensuredContext = [self ensureContext]; 2820 | + 2821 | + ensuredContext->AddDecryptor((uint16_t)participantId); 2822 | + } 2823 | + return self; 2824 | +} 2825 | + 2826 | +- (void)addPcmk:(NSData *)pcmk 2827 | + epoch:(UInt8)epoch 2828 | + ratchetCounter:(UInt8)ratchetCounter { 2829 | + [ThreemaGroupCallFrameCryptoContext validateMediaKey:pcmk]; 2830 | + 2831 | + auto ensuredContext = [self ensureContext]; 2832 | + ensuredContext->GetDecryptor((uint16_t)_participantId) 2833 | + .AddPcmk( 2834 | + (const uint8_t *)pcmk.bytes, (uint8_t)epoch, (uint8_t)ratchetCounter); 2835 | +} 2836 | + 2837 | +- (void)attachWithReceiver:(RTCRtpReceiver *)receiver 2838 | + mediaType:(RTCRtpMediaType)mediaType 2839 | + tag:(NSString *)tag { 2840 | + auto ensuredContext = [self ensureContext]; 2841 | + auto transformer = ensuredContext->GetDecryptor((uint16_t)_participantId) 2842 | + .CreateDecryptor(std::string([tag UTF8String]), 2843 | + [ThreemaGroupCallFrameCryptoContext 2844 | + getCodec:mediaType]); 2845 | + 2846 | + auto nativeRtpReceiver = [receiver nativeRtpReceiver]; 2847 | + nativeRtpReceiver->SetDepacketizerToDecoderFrameTransformer( 2848 | + std::move(transformer)); 2849 | +} 2850 | + 2851 | +- (void)remove { 2852 | + auto ensuredContext = [self ensureContext]; 2853 | + ensuredContext->RemoveDecryptor((uint16_t)_participantId); 2854 | + self->context = nil; 2855 | +} 2856 | + 2857 | +- (webrtc::scoped_refptr)ensureContext { 2858 | + if (self->context) { 2859 | + return self->context; 2860 | + } 2861 | + @throw [NSException exceptionWithName:NSInvalidArgumentException 2862 | + reason:@"Decryptor has been removed" 2863 | + userInfo:nil]; 2864 | +} 2865 | + 2866 | +- (void)validateParticipantId:(UInt16)participantId { 2867 | + if (participantId >= MIDS_MAX) { 2868 | + @throw [NSException 2869 | + exceptionWithName:NSInvalidArgumentException 2870 | + reason:@"'participantId' must be >= 0 and < MIDS_MAX" 2871 | + userInfo:nil]; 2872 | + } 2873 | +} 2874 | + 2875 | +@end 2876 | \ No newline at end of file 2877 | diff --git a/sdk/objc/api/peerconnection/ThreemaGroupCallFrameCryptoEncryptor+Private.h b/sdk/objc/api/peerconnection/ThreemaGroupCallFrameCryptoEncryptor+Private.h 2878 | new file mode 100644 2879 | index 0000000000..493351fb57 2880 | --- /dev/null 2881 | +++ b/sdk/objc/api/peerconnection/ThreemaGroupCallFrameCryptoEncryptor+Private.h 2882 | @@ -0,0 +1,13 @@ 2883 | +#import "ThreemaGroupCallFrameCryptoEncryptor.h" 2884 | + 2885 | +NS_ASSUME_NONNULL_BEGIN 2886 | + 2887 | +@interface RTC_OBJC_TYPE (ThreemaGroupCallFrameCryptoEncryptor) 2888 | +() 2889 | + 2890 | + - (instancetype)initWithContext 2891 | + : (webrtc::scoped_refptr)context; 2892 | + 2893 | +@end 2894 | + 2895 | +NS_ASSUME_NONNULL_END 2896 | \ No newline at end of file 2897 | diff --git a/sdk/objc/api/peerconnection/ThreemaGroupCallFrameCryptoEncryptor.h b/sdk/objc/api/peerconnection/ThreemaGroupCallFrameCryptoEncryptor.h 2898 | new file mode 100644 2899 | index 0000000000..db1ede9eba 2900 | --- /dev/null 2901 | +++ b/sdk/objc/api/peerconnection/ThreemaGroupCallFrameCryptoEncryptor.h 2902 | @@ -0,0 +1,43 @@ 2903 | +#import 2904 | + 2905 | +#import "RTCRtpSender.h" 2906 | + 2907 | +NS_ASSUME_NONNULL_BEGIN 2908 | + 2909 | +RTC_OBJC_EXPORT 2910 | +@protocol RTC_OBJC_TYPE 2911 | +(ThreemaGroupCallFrameCryptoEncryptorProtocol) 2912 | + 2913 | + - (void)setPcmk : (NSData *)pcmk epoch : (UInt8)epoch ratchetCounter 2914 | + : (UInt8)ratchetCounter; 2915 | + 2916 | +- (void)attach:(RTCRtpSender *)sender 2917 | + mediaType:(RTCRtpMediaType)mediaType 2918 | + tag:(NSString *)tag; 2919 | + 2920 | +@end 2921 | + 2922 | +/** Decrypts incoming audio/video frames of another participant. */ 2923 | +RTC_OBJC_EXPORT 2924 | +@interface RTC_OBJC_TYPE (ThreemaGroupCallFrameCryptoEncryptor) : NSObject 2925 | + 2926 | +/** Initialise or update the key material of the encryptor. */ 2927 | +- (void)setPcmk:(NSData *)pcmk epoch:(UInt8)epoch ratchetCounter:(UInt8)ratchetCounter; 2928 | + 2929 | +/** 2930 | + * Attach an RTP sender to the encryptor, encrypting all upcoming audio/video 2931 | + * frames. 2932 | + */ 2933 | +- (void)attach:(RTCRtpSender *)sender 2934 | + mediaType:(RTCRtpMediaType)mediaType 2935 | + tag:(NSString *)tag; 2936 | + 2937 | +/** 2938 | + * Remove the encryptor. Note that it doesn't detach from the RTP sender for 2939 | + * simplicity. 2940 | + */ 2941 | +- (void)remove; 2942 | + 2943 | +@end 2944 | + 2945 | +NS_ASSUME_NONNULL_END 2946 | \ No newline at end of file 2947 | diff --git a/sdk/objc/api/peerconnection/ThreemaGroupCallFrameCryptoEncryptor.mm b/sdk/objc/api/peerconnection/ThreemaGroupCallFrameCryptoEncryptor.mm 2948 | new file mode 100644 2949 | index 0000000000..3524163f00 2950 | --- /dev/null 2951 | +++ b/sdk/objc/api/peerconnection/ThreemaGroupCallFrameCryptoEncryptor.mm 2952 | @@ -0,0 +1,64 @@ 2953 | +#import 2954 | + 2955 | +#import "RTCRtpReceiver.h" 2956 | +#import "RTCRtpSender+Private.h" 2957 | +#import "RTCRtpSender.h" 2958 | + 2959 | +#import "pc/threema_group_call_frame_transformer.h" 2960 | + 2961 | +#include "api/rtp_receiver_interface.h" 2962 | +#include "api/rtp_sender_interface.h" 2963 | + 2964 | +#import "ThreemaGroupCallFrameCryptoContext+Private.h" 2965 | +#import "ThreemaGroupCallFrameCryptoEncryptor.h" 2966 | + 2967 | +@implementation ThreemaGroupCallFrameCryptoEncryptor { 2968 | + webrtc::scoped_refptr context; 2969 | +} 2970 | + 2971 | +- (instancetype)initWithContext: 2972 | + (webrtc::scoped_refptr)context { 2973 | + self = [super init]; 2974 | + if (self) { 2975 | + self->context = context; 2976 | + } 2977 | + return self; 2978 | +} 2979 | + 2980 | +- (void)setPcmk:(NSData *)pcmk 2981 | + epoch:(UInt8)epoch 2982 | + ratchetCounter:(UInt8)ratchetCounter { 2983 | + [ThreemaGroupCallFrameCryptoContext validateMediaKey:pcmk]; 2984 | + 2985 | + auto ensuredContext = [self ensureContext]; 2986 | + ensuredContext->GetEncryptor().SetPcmk( 2987 | + (const uint8_t *)pcmk.bytes, (uint8_t)epoch, (uint8_t)ratchetCounter); 2988 | +} 2989 | + 2990 | +- (void)attach:(RTCRtpSender *)sender 2991 | + mediaType:(RTCRtpMediaType)mediaType 2992 | + tag:(NSString *)tag { 2993 | + auto ensuredContext = [self ensureContext]; 2994 | + auto transformer = ensuredContext->GetEncryptor().CreateEncryptor( 2995 | + std::string([tag UTF8String]), 2996 | + [ThreemaGroupCallFrameCryptoContext getCodec:mediaType]); 2997 | + 2998 | + auto nativeRtpSender = [sender nativeRtpSender]; 2999 | + nativeRtpSender->SetEncoderToPacketizerFrameTransformer( 3000 | + std::move(transformer)); 3001 | +} 3002 | + 3003 | +- (void)remove { 3004 | + self->context = nil; 3005 | +} 3006 | + 3007 | +- (webrtc::scoped_refptr)ensureContext { 3008 | + if (self->context) { 3009 | + return self->context; 3010 | + } 3011 | + @throw [NSException exceptionWithName:NSInvalidArgumentException 3012 | + reason:@"Missing Context" 3013 | + userInfo:nil]; 3014 | +} 3015 | + 3016 | +@end 3017 | \ No newline at end of file 3018 | -------------------------------------------------------------------------------- /patches/only-resolve-uuid-mdns-hostnames.patch: -------------------------------------------------------------------------------- 1 | diff --git a/p2p/base/p2p_transport_channel.cc b/p2p/base/p2p_transport_channel.cc 2 | index 8c7d91aeac..9b0a6f455a 100644 3 | --- a/p2p/base/p2p_transport_channel.cc 4 | +++ b/p2p/base/p2p_transport_channel.cc 5 | @@ -19,6 +19,7 @@ 6 | #include 7 | #include 8 | #include 9 | +#include 10 | #include 11 | #include 12 | #include 13 | @@ -1243,13 +1244,23 @@ void P2PTransportChannel::ResolveHostnameCandidate(const Candidate& candidate) { 14 | return; 15 | } 16 | 17 | + // Only allow resolving of mDNS hostnames with .local to avoid 18 | + // any leaks. 19 | + // See: https://github.com/rtcweb-wg/mdns-ice-candidates/issues/121 20 | + auto hostname = candidate.address().HostAsSensitiveURIString(); 21 | + if (!std::regex_match(hostname, uuid_local_hostname_regex_)) { 22 | + RTC_LOG(LS_WARNING) << "Dropping non-UUID ICE candidate hostname " 23 | + << hostname; 24 | + return; 25 | + } 26 | + 27 | auto resolver = async_dns_resolver_factory_->Create(); 28 | auto resptr = resolver.get(); 29 | resolvers_.emplace_back(candidate, std::move(resolver)); 30 | resptr->Start(candidate.address(), 31 | [this, resptr]() { OnCandidateResolved(resptr); }); 32 | RTC_LOG(LS_INFO) << "Asynchronously resolving ICE candidate hostname " 33 | - << candidate.address().HostAsSensitiveURIString(); 34 | + << hostname; 35 | } 36 | 37 | void P2PTransportChannel::AddRemoteCandidate(const Candidate& candidate) { 38 | diff --git a/p2p/base/p2p_transport_channel.h b/p2p/base/p2p_transport_channel.h 39 | index 0e39a82b59..43a267318b 100644 40 | --- a/p2p/base/p2p_transport_channel.h 41 | +++ b/p2p/base/p2p_transport_channel.h 42 | @@ -27,6 +27,7 @@ 43 | #include 44 | #include 45 | #include 46 | +#include 47 | #include 48 | #include 49 | 50 | @@ -491,6 +492,15 @@ class RTC_EXPORT P2PTransportChannel : public IceTransportInternal, 51 | std::unique_ptr resolver_; 52 | }; 53 | std::vector resolvers_ RTC_GUARDED_BY(network_thread_); 54 | + std::regex uuid_local_hostname_regex_ RTC_GUARDED_BY(network_thread_) = 55 | + std::regex{ 56 | + "^" 57 | + "[a-fA-F0-9]{8}-" 58 | + "[a-fA-F0-9]{4}-" 59 | + "[a-fA-F0-9]{4}-" 60 | + "[a-fA-F0-9]{4}-" 61 | + "[a-fA-F0-9]{12}\\.local" 62 | + "$"}; 63 | void FinishAddingRemoteCandidate(const Candidate& new_remote_candidate); 64 | void OnCandidateResolved(webrtc::AsyncDnsResolverInterface* resolver); 65 | void AddRemoteCandidateWithResult( 66 | -------------------------------------------------------------------------------- /patches/srtp-cipher-suites.patch: -------------------------------------------------------------------------------- 1 | diff --git a/api/crypto/crypto_options.cc b/api/crypto/crypto_options.cc 2 | index 37df0d561e..c66ae00112 100644 3 | --- a/api/crypto/crypto_options.cc 4 | +++ b/api/crypto/crypto_options.cc 5 | @@ -35,25 +35,21 @@ CryptoOptions CryptoOptions::NoGcm() { 6 | 7 | std::vector CryptoOptions::GetSupportedDtlsSrtpCryptoSuites() const { 8 | std::vector crypto_suites; 9 | - // Note: kSrtpAes128CmSha1_80 is what is required to be supported (by 10 | - // draft-ietf-rtcweb-security-arch), but kSrtpAes128CmSha1_32 is allowed as 11 | - // well, and saves a few bytes per packet if it ends up selected. 12 | - // As the cipher suite is potentially insecure, it will only be used if 13 | - // enabled by both peers. 14 | - if (srtp.enable_aes128_sha1_32_crypto_cipher) { 15 | - crypto_suites.push_back(rtc::kSrtpAes128CmSha1_32); 16 | + // We want to prefer GCM cipher suites even though they increase the packet 17 | + // size. 18 | + if (srtp.enable_gcm_crypto_suites) { 19 | + crypto_suites.push_back(rtc::kSrtpAeadAes256Gcm); 20 | + crypto_suites.push_back(rtc::kSrtpAeadAes128Gcm); 21 | } 22 | + 23 | + // Note: kSrtpAes128CmSha1_80 is what is required to be supported (by 24 | + // draft-ietf-rtcweb-security-arch). 25 | + // kSrtpAes128CmSha1_32 is a joke, so we won't even consider it, 26 | + // regardless of the boolean option. 27 | if (srtp.enable_aes128_sha1_80_crypto_cipher) { 28 | crypto_suites.push_back(rtc::kSrtpAes128CmSha1_80); 29 | } 30 | 31 | - // Note: GCM cipher suites are not the top choice since they increase the 32 | - // packet size. In order to negotiate them the other side must not support 33 | - // kSrtpAes128CmSha1_80. 34 | - if (srtp.enable_gcm_crypto_suites) { 35 | - crypto_suites.push_back(rtc::kSrtpAeadAes256Gcm); 36 | - crypto_suites.push_back(rtc::kSrtpAeadAes128Gcm); 37 | - } 38 | RTC_CHECK(!crypto_suites.empty()); 39 | return crypto_suites; 40 | } 41 | diff --git a/rtc_base/openssl_stream_adapter.cc b/rtc_base/openssl_stream_adapter.cc 42 | index e1e367b1ba..d4f12f09f8 100644 43 | --- a/rtc_base/openssl_stream_adapter.cc 44 | +++ b/rtc_base/openssl_stream_adapter.cc 45 | @@ -89,10 +89,9 @@ struct SslCipherMapEntry { 46 | 47 | // This isn't elegant, but it's better than an external reference 48 | constexpr SrtpCipherMapEntry kSrtpCipherMap[] = { 49 | - {"SRTP_AES128_CM_SHA1_80", kSrtpAes128CmSha1_80}, 50 | - {"SRTP_AES128_CM_SHA1_32", kSrtpAes128CmSha1_32}, 51 | + {"SRTP_AEAD_AES_256_GCM", kSrtpAeadAes256Gcm}, 52 | {"SRTP_AEAD_AES_128_GCM", kSrtpAeadAes128Gcm}, 53 | - {"SRTP_AEAD_AES_256_GCM", kSrtpAeadAes256Gcm}}; 54 | + {"SRTP_AES128_CM_SHA1_80", kSrtpAes128CmSha1_80}}; 55 | 56 | #ifdef OPENSSL_IS_BORINGSSL 57 | // Enabled by EnableTimeCallbackForTesting. Should never be set in production 58 | --------------------------------------------------------------------------------