Skip to content

Commit 498db70

Browse files
InterLinked1Friendly Automation
authored and
Friendly Automation
committed
func_scramble: Audio scrambler function
Adds a function to scramble audio on a channel using whole spectrum frequency inversion. This can be used as a privacy enhancement with applications like ChanSpy or other potentially sensitive audio. ASTERISK-29542 Change-Id: I01020769d91060a1f56a708eb405f87648d1a67e
1 parent a099f13 commit 498db70

File tree

2 files changed

+240
-0
lines changed

2 files changed

+240
-0
lines changed

doc/CHANGES-staging/func_scramble.txt

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
Subject: func_scramble
2+
3+
Adds an audio scrambler function that may be used to
4+
distort voice audio on a channel as a privacy
5+
enhancement.

funcs/func_scramble.c

+235
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,235 @@
1+
/*
2+
* Asterisk -- An open source telephony toolkit.
3+
*
4+
* Copyright (C) 2021, Naveen Albert
5+
*
6+
* Naveen Albert <asterisk@phreaknet.org>
7+
*
8+
* See http://www.asterisk.org for more information about
9+
* the Asterisk project. Please do not directly contact
10+
* any of the maintainers of this project for assistance;
11+
* the project provides a web site, mailing lists and IRC
12+
* channels for your use.
13+
*
14+
* This program is free software, distributed under the terms of
15+
* the GNU General Public License Version 2. See the LICENSE file
16+
* at the top of the source tree.
17+
*/
18+
19+
/*! \file
20+
*
21+
* \brief Frequency inverter
22+
*
23+
* \author Naveen Albert <asterisk@phreaknet.org>
24+
*
25+
* \ingroup functions
26+
*
27+
*/
28+
29+
/*** MODULEINFO
30+
<support_level>extended</support_level>
31+
***/
32+
33+
/*** DOCUMENTATION
34+
<function name="SCRAMBLE" language="en_US">
35+
<synopsis>
36+
Scrambles audio on a channel.
37+
</synopsis>
38+
<syntax>
39+
<parameter name="direction" required="false">
40+
<para>Must be <literal>TX</literal> or <literal>RX</literal>
41+
to limit to a specific direction, or <literal>both</literal>
42+
for both directions. <literal>remove</literal>
43+
will remove an existing scrambler.</para>
44+
</parameter>
45+
</syntax>
46+
<description>
47+
<para>Scrambles audio on a channel using whole spectrum inversion.
48+
This is not intended to be used for securely scrambling
49+
audio. It merely renders obfuscates audio on a channel
50+
to render it unintelligible, as a privacy enhancement.</para>
51+
</description>
52+
<see-also>
53+
<ref type="application">ChanSpy</ref>
54+
</see-also>
55+
</function>
56+
***/
57+
58+
#include "asterisk.h"
59+
60+
#include "asterisk/module.h"
61+
#include "asterisk/channel.h"
62+
#include "asterisk/pbx.h"
63+
#include "asterisk/utils.h"
64+
#include "asterisk/audiohook.h"
65+
#include "asterisk/app.h"
66+
67+
#include <stdio.h>
68+
#include <string.h>
69+
70+
struct scramble_information {
71+
struct ast_audiohook audiohook;
72+
unsigned short int tx;
73+
unsigned short int rx;
74+
unsigned short int state;
75+
};
76+
77+
static void destroy_callback(void *data)
78+
{
79+
struct scramble_information *ni = data;
80+
81+
/* Destroy the audiohook, and destroy ourselves */
82+
ast_audiohook_lock(&ni->audiohook);
83+
ast_audiohook_detach(&ni->audiohook);
84+
ast_audiohook_unlock(&ni->audiohook);
85+
ast_audiohook_destroy(&ni->audiohook);
86+
ast_free(ni);
87+
88+
return;
89+
}
90+
91+
/*! \brief Static structure for datastore information */
92+
static const struct ast_datastore_info scramble_datastore = {
93+
.type = "scramble",
94+
.destroy = destroy_callback
95+
};
96+
97+
/* modifies buffer pointed to by 'amp' with inverted values */
98+
static inline void freq_invert(short *amp, int samples)
99+
{
100+
int i;
101+
/* invert every other sample by 1 */
102+
for (i = 0; i < samples; i += 2) {
103+
amp[i] = -amp[i];
104+
}
105+
}
106+
107+
static int scramble_callback(struct ast_audiohook *audiohook, struct ast_channel *chan, struct ast_frame *frame, enum ast_audiohook_direction direction)
108+
{
109+
struct ast_datastore *datastore = NULL;
110+
struct scramble_information *ni = NULL;
111+
112+
/* If the audiohook is stopping it means the channel is shutting down.... but we let the datastore destroy take care of it */
113+
if (audiohook->status == AST_AUDIOHOOK_STATUS_DONE) {
114+
return 0;
115+
}
116+
117+
/* Grab datastore which contains our gain information */
118+
if (!(datastore = ast_channel_datastore_find(chan, &scramble_datastore, NULL))) {
119+
return 0;
120+
}
121+
122+
if (frame->frametype == AST_FRAME_VOICE) { /* only invert voice frequencies */
123+
/* Based on direction of frame, and confirm it is applicable */
124+
if (!(direction == AST_AUDIOHOOK_DIRECTION_READ ? &ni->rx : &ni->tx)) {
125+
return 0;
126+
}
127+
/* Scramble the sample now */
128+
freq_invert(frame->data.ptr, frame->samples);
129+
}
130+
return 0;
131+
}
132+
133+
/*! \internal \brief Disable scrambling on the channel */
134+
static int remove_scrambler(struct ast_channel *chan)
135+
{
136+
struct ast_datastore *datastore = NULL;
137+
struct scramble_information *data;
138+
SCOPED_CHANNELLOCK(chan_lock, chan);
139+
140+
datastore = ast_channel_datastore_find(chan, &scramble_datastore, NULL);
141+
if (!datastore) {
142+
ast_log(AST_LOG_WARNING, "Cannot remove SCRAMBLE from %s: SCRAMBLE not currently enabled\n",
143+
ast_channel_name(chan));
144+
return -1;
145+
}
146+
data = datastore->data;
147+
148+
if (ast_audiohook_remove(chan, &data->audiohook)) {
149+
ast_log(AST_LOG_WARNING, "Failed to remove SCRAMBLE audiohook from channel %s\n", ast_channel_name(chan));
150+
return -1;
151+
}
152+
153+
if (ast_channel_datastore_remove(chan, datastore)) {
154+
ast_log(AST_LOG_WARNING, "Failed to remove SCRAMBLE datastore from channel %s\n",
155+
ast_channel_name(chan));
156+
return -1;
157+
}
158+
ast_datastore_free(datastore);
159+
160+
return 0;
161+
}
162+
163+
static int scramble_write(struct ast_channel *chan, const char *cmd, char *data, const char *value)
164+
{
165+
char *parse;
166+
struct ast_datastore *datastore = NULL;
167+
struct scramble_information *ni = NULL;
168+
int tx = 1, rx = 1;
169+
170+
AST_DECLARE_APP_ARGS(args,
171+
AST_APP_ARG(direction);
172+
);
173+
174+
if (!chan) {
175+
ast_log(LOG_WARNING, "No channel was provided to %s function.\n", cmd);
176+
return -1;
177+
}
178+
179+
parse = ast_strdupa(value);
180+
AST_STANDARD_APP_ARGS(args, parse);
181+
182+
if (!strcasecmp(args.direction, "remove")) {
183+
return remove_scrambler(chan);
184+
}
185+
if (!strcasecmp(args.direction, "tx")) {
186+
tx = 1;
187+
rx = 0;
188+
} else if (!strcasecmp(args.direction, "rx")) {
189+
rx = 0;
190+
tx = 1;
191+
} else if (strcasecmp(args.direction, "both")) {
192+
ast_log(LOG_ERROR, "Direction must be either RX, TX, both, or remove\n");
193+
return -1;
194+
}
195+
ast_channel_lock(chan);
196+
if (!(datastore = ast_channel_datastore_find(chan, &scramble_datastore, NULL))) {
197+
/* Allocate a new datastore to hold the reference to this audiohook information */
198+
if (!(datastore = ast_datastore_alloc(&scramble_datastore, NULL))) {
199+
return 0;
200+
}
201+
if (!(ni = ast_calloc(1, sizeof(*ni)))) {
202+
ast_datastore_free(datastore);
203+
return 0;
204+
}
205+
ast_audiohook_init(&ni->audiohook, AST_AUDIOHOOK_TYPE_MANIPULATE, "Voice scrambler", AST_AUDIOHOOK_MANIPULATE_ALL_RATES);
206+
ni->audiohook.manipulate_callback = scramble_callback;
207+
datastore->data = ni;
208+
ast_channel_datastore_add(chan, datastore);
209+
ast_audiohook_attach(chan, &ni->audiohook);
210+
} else {
211+
ni = datastore->data;
212+
}
213+
ni->tx = tx;
214+
ni->rx = rx;
215+
ast_channel_unlock(chan);
216+
217+
return 0;
218+
}
219+
220+
static struct ast_custom_function scramble_function = {
221+
.name = "SCRAMBLE",
222+
.write = scramble_write,
223+
};
224+
225+
static int unload_module(void)
226+
{
227+
return ast_custom_function_unregister(&scramble_function);
228+
}
229+
230+
static int load_module(void)
231+
{
232+
return ast_custom_function_register(&scramble_function);
233+
}
234+
235+
AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Frequency inverting voice scrambler");

0 commit comments

Comments
 (0)