Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
A
audiowmark
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Stefan Westerfeld
audiowmark
Commits
3ddc62ff
Commit
3ddc62ff
authored
Jan 05, 2019
by
Stefan Westerfeld
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'secure-random'
parents
716b84da
5d1aa2c1
Changes
9
Show whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
402 additions
and
49 deletions
+402
-49
configure.ac
configure.ac
+1
-0
Makefile.am
src/Makefile.am
+7
-3
audiowmark.cc
src/audiowmark.cc
+52
-43
ber-test.sh
src/ber-test.sh
+3
-3
random.cc
src/random.cc
+205
-0
random.hh
src/random.hh
+58
-0
testrandom.cc
src/testrandom.cc
+39
-0
utils.cc
src/utils.cc
+34
-0
utils.hh
src/utils.hh
+3
-0
No files found.
configure.ac
View file @
3ddc62ff
...
...
@@ -46,6 +46,7 @@ AC_DEFUN([AC_FFTW_CHECK],
AC_SNDFILE_REQUIREMENTS
AC_FFTW_CHECK
AM_PATH_LIBGCRYPT
# need c++11 mode
AX_CXX_COMPILE_STDCXX_11(ext)
...
...
src/Makefile.am
View file @
3ddc62ff
bin_PROGRAMS
=
audiowmark
COMMON_SRC
=
utils.hh utils.cc convcode.hh convcode.cc
COMMON_SRC
=
utils.hh utils.cc convcode.hh convcode.cc
random.hh random.cc
audiowmark_SOURCES
=
audiowmark.cc wavdata.cc wavdata.hh fft.cc fft.hh
$(COMMON_SRC)
audiowmark_LDFLAGS
=
$(SNDFILE_LIBS)
$(FFTW_LIBS)
audiowmark_LDFLAGS
=
$(SNDFILE_LIBS)
$(FFTW_LIBS)
$(LIBGCRYPT_LIBS)
noinst_PROGRAMS
=
testconvcode
noinst_PROGRAMS
=
testconvcode
testrandom
testconvcode_SOURCES
=
testconvcode.cc
$(COMMON_SRC)
testconvcode_LDFLAGS
=
$(LIBGCRYPT_LIBS)
testrandom_SOURCES
=
testrandom.cc
$(COMMON_SRC)
testrandom_LDFLAGS
=
$(LIBGCRYPT_LIBS)
src/audiowmark.cc
View file @
3ddc62ff
...
...
@@ -8,6 +8,7 @@
#include "wavdata.hh"
#include "utils.hh"
#include "convcode.hh"
#include "random.hh"
#include <assert.h>
...
...
@@ -30,7 +31,7 @@ namespace Params
static
bool
mix
=
true
;
static
bool
hard
=
false
;
// hard decode bits? (soft decoding is better)
static
int
block_size
=
32
;
// block size for mix step (non-linear bit storage)
static
unsigned
int
seed
=
0
;
static
int
have_key
=
0
;
static
size_t
payload_size
=
128
;
// number of payload bits for the watermark
}
...
...
@@ -55,12 +56,16 @@ print_usage()
printf
(
" * compute bit error rate for decoding with original file
\n
"
);
printf
(
" audiowmark cmp-delta <input_wav> <watermarked_wav> <message_hex>
\n
"
);
printf
(
"
\n
"
);
printf
(
" * generate 128-bit watermarking key, to be used with --key option
\n
"
);
printf
(
" audiowmark gen-key <key_file>
\n
"
);
printf
(
"
\n
"
);
printf
(
"Global options:
\n
"
);
printf
(
" --frame-size frame size (must be power of 2) [%zd]
\n
"
,
Params
::
frame_size
);
printf
(
" --frames-per-bit number of frames per bit [%d]
\n
"
,
Params
::
frames_per_bit
);
printf
(
" --water-delta set watermarking delta [%.4f]
\n
"
,
Params
::
water_delta
);
printf
(
" --pre-scale set scaling used for normalization [%.3f]
\n
"
,
Params
::
pre_scale
);
printf
(
" --linear disable non-linear bit storage
\n
"
);
printf
(
" --key <file> load watermarking key from file
\n
"
);
}
static
bool
...
...
@@ -157,9 +162,15 @@ parse_options (int *argc_p,
{
Params
::
hard
=
true
;
}
else
if
(
check_arg
(
argc
,
argv
,
&
i
,
"--seed"
,
&
opt_arg
))
else
if
(
check_arg
(
argc
,
argv
,
&
i
,
"--test-key"
,
&
opt_arg
))
{
Params
::
have_key
++
;
Random
::
set_global_test_key
(
atoi
(
opt_arg
));
}
else
if
(
check_arg
(
argc
,
argv
,
&
i
,
"--key"
,
&
opt_arg
))
{
Params
::
seed
=
atoi
(
opt_arg
);
Params
::
have_key
++
;
Random
::
load_global_key
(
opt_arg
);
}
}
...
...
@@ -241,49 +252,21 @@ get_frame (const WavData& wav_data, int f, int ch)
return
result
;
}
std
::
mt19937_64
init_rng
(
uint64_t
seed
)
{
const
uint64_t
prime
=
3126986573
;
std
::
mt19937_64
rng
;
rng
.
seed
(
seed
+
prime
*
Params
::
seed
);
return
rng
;
}
void
get_up_down
(
int
f
,
vector
<
int
>&
up
,
vector
<
int
>&
down
)
{
vector
<
int
>
used
(
Params
::
frame_size
/
2
);
std
::
mt19937_64
rng
=
init_rng
(
f
);
// use per frame random seed, may want to have cryptographically secure algorithm
auto
choose_bands
=
[
&
used
,
&
rng
]
(
vector
<
int
>&
bands
)
{
while
(
bands
.
size
()
<
Params
::
bands_per_frame
)
{
int
p
=
rng
()
%
(
Params
::
max_band
-
Params
::
min_band
)
+
Params
::
min_band
;
if
(
!
used
[
p
])
{
bands
.
push_back
(
p
);
used
[
p
]
=
1
;
}
}
};
choose_bands
(
up
);
choose_bands
(
down
);
}
vector
<
int
>
bands_reorder
;
for
(
int
i
=
Params
::
min_band
;
i
<=
Params
::
max_band
;
i
++
)
bands_reorder
.
push_back
(
i
);
template
<
class
T
>
void
gen_shuffle
(
vector
<
T
>&
result
,
int
seed
)
{
std
::
mt19937_64
rng
=
init_rng
(
seed
);
// should use cryptographically secure generator, properly seeded
Random
random
(
f
,
Random
::
Stream
::
up_down
);
// use per frame random seed
random
.
shuffle
(
bands_reorder
);
// Fisher–Yates shuffle
for
(
size_t
i
=
0
;
i
<
result
.
size
()
-
1
;
i
++
)
assert
(
2
*
Params
::
bands_per_frame
<
bands_reorder
.
size
());
for
(
size_t
i
=
0
;
i
<
Params
::
bands_per_frame
;
i
++
)
{
size_t
j
=
i
+
rng
()
%
(
result
.
size
()
-
i
);
std
::
swap
(
result
[
i
],
result
[
j
]);
up
.
push_back
(
bands_reorder
[
i
]
);
down
.
push_back
(
bands_reorder
[
Params
::
bands_per_frame
+
i
]);
}
}
...
...
@@ -295,7 +278,8 @@ randomize_bit_order (const vector<T>& bit_vec, bool encode)
for
(
size_t
i
=
0
;
i
<
bit_vec
.
size
();
i
++
)
order
.
push_back
(
i
);
gen_shuffle
(
order
,
/* seed */
0
);
Random
random
(
/* seed */
0
,
Random
::
Stream
::
bit_order
);
random
.
shuffle
(
order
);
vector
<
T
>
out_bits
(
bit_vec
.
size
());
for
(
size_t
i
=
0
;
i
<
bit_vec
.
size
();
i
++
)
...
...
@@ -330,7 +314,9 @@ gen_mix_entries (int block)
for
(
size_t
i
=
0
;
i
<
up
.
size
();
i
++
)
mix_entries
.
push_back
({
f
,
up
[
i
],
down
[
i
]
});
}
gen_shuffle
(
mix_entries
,
/* seed */
block
);
Random
random
(
/* seed */
block
,
Random
::
Stream
::
mix
);
random
.
shuffle
(
mix_entries
);
return
mix_entries
;
}
...
...
@@ -893,11 +879,30 @@ get_snr (const string& origfile, const string& wmfile)
return
0
;
}
int
gen_key
(
const
string
&
outfile
)
{
FILE
*
f
=
fopen
(
outfile
.
c_str
(),
"w"
);
if
(
!
f
)
{
fprintf
(
stderr
,
"audiowmark: error writing to file %s
\n
"
,
outfile
.
c_str
());
return
1
;
}
fprintf
(
f
,
"# watermarking key for audiowmark
\n\n
key %s
\n
"
,
Random
::
gen_key
().
c_str
());
fclose
(
f
);
return
0
;
}
int
main
(
int
argc
,
char
**
argv
)
{
parse_options
(
&
argc
,
&
argv
);
if
(
Params
::
have_key
>
1
)
{
fprintf
(
stderr
,
"audiowmark: watermark key can at most be set once (--key / --test-key option)
\n
"
);
return
1
;
}
string
op
=
(
argc
>=
2
)
?
argv
[
1
]
:
""
;
if
(
op
==
"add"
&&
argc
==
5
)
...
...
@@ -932,9 +937,13 @@ main (int argc, char **argv)
{
return
get_watermark_delta
(
argv
[
2
],
argv
[
3
],
argv
[
4
]);
}
else
if
(
op
==
"gen-key"
&&
argc
==
3
)
{
return
gen_key
(
argv
[
2
]);
}
else
{
fprintf
(
stderr
,
"audiowmark: error parsing commandline args
\n
"
);
fprintf
(
stderr
,
"audiowmark: error parsing commandline args
(use audiowmark -h)
\n
"
);
return
1
;
}
}
src/ber-test.sh
View file @
3ddc62ff
...
...
@@ -44,7 +44,7 @@ do
PATTERN
=
4e1243bd22c66e76c2ba9eddc1f91394
fi
audiowmark add
"
$i
"
${
AWM_FILE
}
.wav
$PATTERN
$AWM_PARAMS
--
seed
$SEED
>
/dev/null
audiowmark add
"
$i
"
${
AWM_FILE
}
.wav
$PATTERN
$AWM_PARAMS
--
test-key
$SEED
>
/dev/null
if
[
"x
$TRANSFORM
"
==
"xmp3"
]
;
then
if
[
"x
$2
"
==
"x"
]
;
then
echo
"need mp3 bitrate"
>
&2
...
...
@@ -93,9 +93,9 @@ do
exit
1
fi
# blind decoding
audiowmark cmp
${
AWM_FILE
}
.wav
$PATTERN
$AWM_PARAMS
--
seed
$SEED
audiowmark cmp
${
AWM_FILE
}
.wav
$PATTERN
$AWM_PARAMS
--
test-key
$SEED
# decoding with original
# audiowmark cmp-delta "$i" t.wav $PATTERN $AWM_PARAMS --
seed
$SEED
# audiowmark cmp-delta "$i" t.wav $PATTERN $AWM_PARAMS --
test-key
$SEED
done
done
|
grep
bit_error_rate |
{
if
[
"x
$AWM_REPORT
"
==
"xber"
]
;
then
...
...
src/random.cc
0 → 100644
View file @
3ddc62ff
#include "random.hh"
#include "utils.hh"
#include <regex>
#include <assert.h>
using
std
::
string
;
using
std
::
vector
;
using
std
::
regex
;
using
std
::
regex_match
;
static
vector
<
unsigned
char
>
aes_key
(
16
);
// 128 bits
static
constexpr
auto
GCRY_CIPHER
=
GCRY_CIPHER_AES128
;
static
void
uint64_to_buffer
(
uint64_t
u
,
unsigned
char
*
buffer
)
{
/* this has to be endian independent: use big endian order */
buffer
[
0
]
=
u
>>
56
;
buffer
[
1
]
=
u
>>
48
;
buffer
[
2
]
=
u
>>
40
;
buffer
[
3
]
=
u
>>
32
;
buffer
[
4
]
=
u
>>
24
;
buffer
[
5
]
=
u
>>
16
;
buffer
[
6
]
=
u
>>
8
;
buffer
[
7
]
=
u
;
}
static
uint64_t
uint64_from_buffer
(
unsigned
char
*
buffer
)
{
/* this has to be endian independent: use big endian order */
return
(
uint64_t
(
buffer
[
0
])
<<
56
)
+
(
uint64_t
(
buffer
[
1
])
<<
48
)
+
(
uint64_t
(
buffer
[
2
])
<<
40
)
+
(
uint64_t
(
buffer
[
3
])
<<
32
)
+
(
uint64_t
(
buffer
[
4
])
<<
24
)
+
(
uint64_t
(
buffer
[
5
])
<<
16
)
+
(
uint64_t
(
buffer
[
6
])
<<
8
)
+
buffer
[
7
];
}
static
void
print
(
const
string
&
label
,
const
vector
<
unsigned
char
>&
data
)
{
printf
(
"%s: "
,
label
.
c_str
());
for
(
auto
ch
:
data
)
printf
(
"%02x "
,
ch
);
printf
(
"
\n
"
);
}
Random
::
Random
(
uint64_t
seed
,
Stream
stream
)
{
vector
<
unsigned
char
>
ctr
=
get_start_counter
(
seed
,
stream
);
// print ("CTR", ctr);
gcry_error_t
gcry_ret
=
gcry_cipher_open
(
&
aes_ctr_cipher
,
GCRY_CIPHER
,
GCRY_CIPHER_MODE_CTR
,
0
);
die_on_error
(
"gcry_cipher_open"
,
gcry_ret
);
gcry_ret
=
gcry_cipher_setkey
(
aes_ctr_cipher
,
&
aes_key
[
0
],
aes_key
.
size
());
die_on_error
(
"gcry_cipher_setkey"
,
gcry_ret
);
gcry_ret
=
gcry_cipher_setctr
(
aes_ctr_cipher
,
&
ctr
[
0
],
ctr
.
size
());
die_on_error
(
"gcry_cipher_setctr"
,
gcry_ret
);
}
Random
::~
Random
()
{
gcry_cipher_close
(
aes_ctr_cipher
);
}
vector
<
unsigned
char
>
Random
::
get_start_counter
(
uint64_t
seed
,
Stream
stream
)
{
gcry_error_t
gcry_ret
;
gcry_cipher_hd_t
cipher_hd
;
gcry_ret
=
gcry_cipher_open
(
&
cipher_hd
,
GCRY_CIPHER
,
GCRY_CIPHER_MODE_ECB
,
0
);
die_on_error
(
"gcry_cipher_open"
,
gcry_ret
);
gcry_ret
=
gcry_cipher_setkey
(
cipher_hd
,
&
aes_key
[
0
],
aes_key
.
size
());
die_on_error
(
"gcry_cipher_setkey"
,
gcry_ret
);
vector
<
unsigned
char
>
cipher_text
(
16
);
vector
<
unsigned
char
>
plain_text
(
16
);
uint64_to_buffer
(
seed
,
&
plain_text
[
0
]);
plain_text
[
8
]
=
uint8_t
(
stream
);
// print ("SEED", plain_text);
gcry_ret
=
gcry_cipher_encrypt
(
cipher_hd
,
&
cipher_text
[
0
],
cipher_text
.
size
(),
&
plain_text
[
0
],
plain_text
.
size
());
die_on_error
(
"gcry_cipher_encrypt"
,
gcry_ret
);
gcry_cipher_close
(
cipher_hd
);
return
cipher_text
;
}
void
Random
::
refill_buffer
()
{
const
size_t
block_size
=
256
;
unsigned
char
zeros
[
block_size
]
=
{
0
,
};
unsigned
char
cipher_text
[
block_size
];
gcry_error_t
gcry_ret
=
gcry_cipher_encrypt
(
aes_ctr_cipher
,
cipher_text
,
block_size
,
zeros
,
block_size
);
die_on_error
(
"gcry_cipher_encrypt"
,
gcry_ret
);
// print ("AES OUT", {cipher_text, cipher_text + block_size});
buffer
.
clear
();
for
(
size_t
i
=
0
;
i
<
block_size
;
i
+=
8
)
buffer
.
push_back
(
uint64_from_buffer
(
cipher_text
+
i
));
buffer_pos
=
0
;
}
void
Random
::
die_on_error
(
const
char
*
func
,
gcry_error_t
error
)
{
if
(
error
)
{
fprintf
(
stderr
,
"%s failed: %s/%s
\n
"
,
func
,
gcry_strsource
(
error
),
gcry_strerror
(
error
));
exit
(
1
);
/* can't recover here */
}
}
void
Random
::
set_global_test_key
(
uint64_t
key
)
{
uint64_to_buffer
(
key
,
&
aes_key
[
0
]);
}
void
Random
::
load_global_key
(
const
string
&
key_file
)
{
FILE
*
f
=
fopen
(
key_file
.
c_str
(),
"r"
);
if
(
!
f
)
{
fprintf
(
stderr
,
"audiowmark: error opening key file: '%s'
\n
"
,
key_file
.
c_str
());
exit
(
1
);
}
const
regex
blank_re
(
R"(\s*(#.*)?[\r\n]+)"
);
const
regex
key_re
(
R"(\s*key\s+([0-9a-f]+)\s*(#.*)?[\r\n]+)"
);
char
buffer
[
1024
];
int
line
=
1
;
int
keys
=
0
;
while
(
fgets
(
buffer
,
1024
,
f
))
{
string
s
=
buffer
;
std
::
smatch
match
;
if
(
regex_match
(
s
,
blank_re
))
{
/* blank line or comment */
}
else
if
(
regex_match
(
s
,
match
,
key_re
))
{
/* line containing aes key */
vector
<
unsigned
char
>
key
=
hex_str_to_vec
(
match
[
1
].
str
());
if
(
key
.
size
()
!=
aes_key
.
size
())
{
fprintf
(
stderr
,
"audiowmark: wrong key length in key file '%s', line %d
\n
=> required key length is %zd bits
\n
"
,
key_file
.
c_str
(),
line
,
aes_key
.
size
()
*
8
);
exit
(
1
);
}
aes_key
=
key
;
keys
++
;
}
else
{
fprintf
(
stderr
,
"audiowmark: parse error in key file '%s', line %d
\n
"
,
key_file
.
c_str
(),
line
);
exit
(
1
);
}
line
++
;
}
fclose
(
f
);
if
(
keys
>
1
)
{
fprintf
(
stderr
,
"audiowmark: key file '%s' contains more than one key
\n
"
,
key_file
.
c_str
());
exit
(
1
);
}
if
(
keys
==
0
)
{
fprintf
(
stderr
,
"audiowmark: key file '%s' contains no key
\n
"
,
key_file
.
c_str
());
exit
(
1
);
}
}
string
Random
::
gen_key
()
{
vector
<
unsigned
char
>
key
(
16
);
gcry_randomize
(
&
key
[
0
],
16
,
/* long term key material strength */
GCRY_VERY_STRONG_RANDOM
);
return
vec_to_hex_str
(
key
);
}
src/random.hh
0 → 100644
View file @
3ddc62ff
#ifndef AUDIOWMARK_RANDOM_HH
#define AUDIOWMARK_RANDOM_HH
#include <gcrypt.h>
#include <stdint.h>
#include <vector>
#include <string>
class
Random
{
public
:
enum
class
Stream
{
up_down
=
1
,
mix
=
2
,
bit_order
=
3
};
private
:
gcry_cipher_hd_t
aes_ctr_cipher
;
std
::
vector
<
uint64_t
>
buffer
;
size_t
buffer_pos
=
0
;
std
::
vector
<
unsigned
char
>
get_start_counter
(
uint64_t
seed
,
Stream
stream
);
void
die_on_error
(
const
char
*
func
,
gcry_error_t
error
);
public
:
Random
(
uint64_t
seed
,
Stream
stream
);
~
Random
();
uint64_t
operator
()()
{
if
(
buffer_pos
==
buffer
.
size
())
refill_buffer
();
return
buffer
[
buffer_pos
++
];
}
void
refill_buffer
();
template
<
class
T
>
void
shuffle
(
std
::
vector
<
T
>&
result
)
{
// Fisher–Yates shuffle
for
(
size_t
i
=
0
;
i
<
result
.
size
();
i
++
)
{
const
uint64_t
random_number
=
(
*
this
)();
size_t
j
=
i
+
random_number
%
(
result
.
size
()
-
i
);
std
::
swap
(
result
[
i
],
result
[
j
]);
}
}
static
void
set_global_test_key
(
uint64_t
seed
);
static
void
load_global_key
(
const
std
::
string
&
key_file
);
static
std
::
string
gen_key
();
};
#endif
/* AUDIOWMARK_RANDOM_HH */
src/testrandom.cc
0 → 100644
View file @
3ddc62ff
#include "utils.hh"
#include "random.hh"
#include <sys/time.h>
using
std
::
vector
;
using
std
::
string
;
static
double
gettime
()
{
timeval
tv
;
gettimeofday
(
&
tv
,
0
);
return
tv
.
tv_sec
+
tv
.
tv_usec
/
1000000.0
;
}
int
main
(
int
argc
,
char
**
argv
)
{
Random
rng
(
0xf00f1234b00b5678U
,
Random
::
Stream
::
bit_order
);
for
(
size_t
i
=
0
;
i
<
20
;
i
++
)
{
uint64_t
x
=
rng
();
printf
(
"%016lx
\n
"
,
x
);
}
uint64_t
s
=
0
;
double
t_start
=
gettime
();
size_t
runs
=
25000000
;
for
(
size_t
i
=
0
;
i
<
runs
;
i
++
)
{
s
+=
rng
();
}
double
t_end
=
gettime
();
printf
(
"s=%016lx
\n\n
"
,
s
);
printf
(
"%f Mvalues/sec
\n
"
,
runs
/
(
t_end
-
t_start
)
/
1000000
);
}
src/utils.cc
View file @
3ddc62ff
...
...
@@ -55,3 +55,37 @@ bit_vec_to_str (const vector<int>& bit_vec)
return
bit_str
;
}
vector
<
unsigned
char
>
hex_str_to_vec
(
const
string
&
str
)
{
vector
<
unsigned
char
>
result
;
if
((
str
.
size
()
%
2
)
!=
0
)
// even length
return
vector
<
unsigned
char
>
();
for
(
size_t
i
=
0
;
i
<
str
.
size
()
/
2
;
i
++
)
{
unsigned
char
h
=
from_hex_nibble
(
str
[
i
*
2
]);
unsigned
char
l
=
from_hex_nibble
(
str
[
i
*
2
+
1
]);
if
(
h
>=
16
||
l
>=
16
)
return
vector
<
unsigned
char
>
();
result
.
push_back
((
h
<<
4
)
+
l
);
}
return
result
;
}
string
vec_to_hex_str
(
const
vector
<
unsigned
char
>&
vec
)
{
string
s
;
for
(
auto
byte
:
vec
)
{
char
buffer
[
256
];
sprintf
(
buffer
,
"%02x"
,
byte
);
s
+=
buffer
;
}
return
s
;
}
src/utils.hh
View file @
3ddc62ff
...
...
@@ -7,4 +7,7 @@
std
::
vector
<
int
>
bit_str_to_vec
(
const
std
::
string
&
bits
);
std
::
string
bit_vec_to_str
(
const
std
::
vector
<
int
>&
bit_vec
);
std
::
vector
<
unsigned
char
>
hex_str_to_vec
(
const
std
::
string
&
str
);
std
::
string
vec_to_hex_str
(
const
std
::
vector
<
unsigned
char
>&
vec
);
#endif
/* AUDIOWMARK_UTILS_HH */
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment