Skip to content

Commit 129f796

Browse files
committed
Audio MD5
1 parent 5121590 commit 129f796

File tree

9 files changed

+295
-16
lines changed

9 files changed

+295
-16
lines changed

src/cmdline.cpp

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
#include "common/utils.h"
33
#include "common/timer.h"
44
#include "file/sac.h"
5+
#include <cstring>
56

67
CmdLine::CmdLine()
78
:mode(ENCODE)
@@ -160,6 +161,20 @@ int CmdLine::Parse(int argc,char *argv[])
160161
return 0;
161162
}
162163

164+
/*#include "common/md5.h"
165+
void MD5Test()
166+
{
167+
MD5::MD5Context ctx;
168+
MD5::Init(&ctx);
169+
//md5 '123'=202cb962ac59075b964b07152d234b70
170+
std::vector<uint8_t> x={'1','2','3'};
171+
MD5::Update(&ctx,&x[0],3);
172+
std::vector <uint8_t> out(16);
173+
MD5::Finalize(&ctx);
174+
for (auto x : ctx.digest) std::cout << std::hex << (int)x;
175+
std::cout << '\n';
176+
}*/
177+
163178
int CmdLine::Process()
164179
{
165180
Timer myTimer;
@@ -227,6 +242,9 @@ int CmdLine::Process()
227242
std::streampos FileSizeSAC = mySac.getFileSize();
228243
std::cout << "ok (" << FileSizeSAC << " Bytes)\n";
229244
if (mySac.ReadHeader()==0) {
245+
uint8_t md5digest[16];
246+
mySac.ReadMD5(md5digest);
247+
230248
double bps=(static_cast<double>(FileSizeSAC)*8.0)/static_cast<double>(mySac.getNumSamples()*mySac.getNumChannels());
231249
int kbps=round((mySac.getSampleRate()*mySac.getNumChannels()*bps)/1000);
232250
mySac.setKBPS(kbps);
@@ -238,18 +256,32 @@ int CmdLine::Process()
238256
}
239257
std::cout << std::endl;
240258
std::cout << " Ratio: " << std::fixed << std::setprecision(3) << bps << " bits per sample\n\n";
259+
std::cout << " Audio MD5: ";
260+
for (auto x : md5digest) std::cout << std::hex << (int)x;
261+
std::cout << std::dec << '\n';
241262

242263

243264
if (mode==LISTFULL) {
244265
Codec myCodec;
245266
myCodec.ScanFrames(mySac);
246267
} else if (mode==DECODE) {
268+
247269
Wav myWav(mySac);
248270
std::cout << "Create: '" << soutputfile << "': ";
249271
if (myWav.OpenWrite(soutputfile)==0) {
250272
std::cout << "ok\n";
251273
Codec myCodec;
252274
myCodec.DecodeFile(mySac,myWav);
275+
MD5::Finalize(&myWav.md5ctx);
276+
bool md5diff=std::memcmp(myWav.md5ctx.digest, md5digest, 16);
277+
std::cout << '\n';
278+
std::cout << " Audio MD5: ";
279+
if (!md5diff) std::cout << "ok\n";
280+
else {
281+
std::cout << "Error (";
282+
for (auto x : myWav.md5ctx.digest) std::cout << std::hex << (int)x;
283+
std::cout << std::dec << ")\n";
284+
}
253285
myWav.Close();
254286
} else std::cout << "could not create\n";
255287
}

src/common/md5.cpp

Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
#include "md5.h"
2+
3+
static uint32_t S[] = {7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22,
4+
5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20,
5+
4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23,
6+
6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21};
7+
8+
static uint32_t K[] = {0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee,
9+
0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501,
10+
0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be,
11+
0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821,
12+
0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa,
13+
0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8,
14+
0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed,
15+
0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a,
16+
0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c,
17+
0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70,
18+
0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05,
19+
0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665,
20+
0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039,
21+
0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1,
22+
0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1,
23+
0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391};
24+
25+
/*
26+
* Padding used to make the size (in bits) of the input congruent to 448 mod 512
27+
*/
28+
static uint8_t PADDING[] = {0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
29+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
30+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
31+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
32+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
33+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
34+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
35+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
36+
37+
uint32_t MD5::rotateLeft(uint32_t x, uint32_t n)
38+
{
39+
return (x << n) | (x >> (32 - n));
40+
}
41+
42+
void MD5::Init(MD5Context *ctx){
43+
ctx->size = (uint64_t)0;
44+
45+
ctx->buffer[0] = (uint32_t)A;
46+
ctx->buffer[1] = (uint32_t)B;
47+
ctx->buffer[2] = (uint32_t)C;
48+
ctx->buffer[3] = (uint32_t)D;
49+
}
50+
51+
/*
52+
* Add some amount of input to the context
53+
*
54+
* If the input fills out a block of 512 bits, apply the algorithm (md5Step)
55+
* and save the result in the buffer. Also updates the overall size.
56+
*/
57+
void MD5::Update(MD5Context *ctx, uint8_t *input_buffer, size_t input_len){
58+
uint32_t input[16];
59+
unsigned int offset = ctx->size % 64;
60+
ctx->size += (uint64_t)input_len;
61+
62+
// Copy each byte in input_buffer into the next space in our context input
63+
for(unsigned int i = 0; i < input_len; ++i){
64+
ctx->input[offset++] = (uint8_t)*(input_buffer + i);
65+
66+
// If we've filled our context input, copy it into our local array input
67+
// then reset the offset to 0 and fill in a new buffer.
68+
// Every time we fill out a chunk, we run it through the algorithm
69+
// to enable some back and forth between cpu and i/o
70+
if(offset % 64 == 0){
71+
for(unsigned int j = 0; j < 16; ++j){
72+
// Convert to little-endian
73+
// The local variable `input` our 512-bit chunk separated into 32-bit words
74+
// we can use in calculations
75+
input[j] = (uint32_t)(ctx->input[(j * 4) + 3]) << 24 |
76+
(uint32_t)(ctx->input[(j * 4) + 2]) << 16 |
77+
(uint32_t)(ctx->input[(j * 4) + 1]) << 8 |
78+
(uint32_t)(ctx->input[(j * 4)]);
79+
}
80+
Step(ctx->buffer, input);
81+
offset = 0;
82+
}
83+
}
84+
}
85+
86+
/*
87+
* Pad the current input to get to 448 bytes, append the size in bits to the very end,
88+
* and save the result of the final iteration into digest.
89+
*/
90+
void MD5::Finalize(MD5Context *ctx){
91+
uint32_t input[16];
92+
unsigned int offset = ctx->size % 64;
93+
unsigned int padding_length = offset < 56 ? 56 - offset : (56 + 64) - offset;
94+
95+
// Fill in the padding and undo the changes to size that resulted from the update
96+
Update(ctx, PADDING, padding_length);
97+
ctx->size -= (uint64_t)padding_length;
98+
99+
// Do a final update (internal to this function)
100+
// Last two 32-bit words are the two halves of the size (converted from bytes to bits)
101+
for(unsigned int j = 0; j < 14; ++j){
102+
input[j] = (uint32_t)(ctx->input[(j * 4) + 3]) << 24 |
103+
(uint32_t)(ctx->input[(j * 4) + 2]) << 16 |
104+
(uint32_t)(ctx->input[(j * 4) + 1]) << 8 |
105+
(uint32_t)(ctx->input[(j * 4)]);
106+
}
107+
input[14] = (uint32_t)(ctx->size * 8);
108+
input[15] = (uint32_t)((ctx->size * 8) >> 32);
109+
110+
Step(ctx->buffer, input);
111+
112+
// Move the result into digest (convert from little-endian)
113+
for(unsigned int i = 0; i < 4; ++i){
114+
ctx->digest[(i * 4) + 0] = (uint8_t)((ctx->buffer[i] & 0x000000FF));
115+
ctx->digest[(i * 4) + 1] = (uint8_t)((ctx->buffer[i] & 0x0000FF00) >> 8);
116+
ctx->digest[(i * 4) + 2] = (uint8_t)((ctx->buffer[i] & 0x00FF0000) >> 16);
117+
ctx->digest[(i * 4) + 3] = (uint8_t)((ctx->buffer[i] & 0xFF000000) >> 24);
118+
}
119+
}
120+
121+
/*
122+
* Step on 512 bits of input with the main MD5 algorithm.
123+
*/
124+
void MD5::Step(uint32_t *buffer, uint32_t *input){
125+
uint32_t AA = buffer[0];
126+
uint32_t BB = buffer[1];
127+
uint32_t CC = buffer[2];
128+
uint32_t DD = buffer[3];
129+
130+
uint32_t E;
131+
132+
unsigned int j;
133+
134+
for(unsigned int i = 0; i < 64; ++i){
135+
switch(i / 16){
136+
case 0:
137+
E = F(BB, CC, DD);
138+
j = i;
139+
break;
140+
case 1:
141+
E = G(BB, CC, DD);
142+
j = ((i * 5) + 1) % 16;
143+
break;
144+
case 2:
145+
E = H(BB, CC, DD);
146+
j = ((i * 3) + 5) % 16;
147+
break;
148+
default:
149+
E = I(BB, CC, DD);
150+
j = (i * 7) % 16;
151+
break;
152+
}
153+
154+
uint32_t temp = DD;
155+
DD = CC;
156+
CC = BB;
157+
BB = BB + rotateLeft(AA + E + K[i] + input[j], S[i]);
158+
AA = temp;
159+
}
160+
161+
buffer[0] += AA;
162+
buffer[1] += BB;
163+
buffer[2] += CC;
164+
buffer[3] += DD;
165+
}

src/common/md5.h

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
#ifndef MD5_H
2+
#define MD5_H
3+
4+
#include <cstdint>
5+
6+
namespace MD5 {
7+
typedef struct{
8+
uint64_t size; // Size of input in bytes
9+
uint32_t buffer[4]; // Current accumulation of hash
10+
uint8_t input[64]; // Input to be used in the next step
11+
uint8_t digest[16]; // Result of algorithm
12+
} MD5Context;
13+
14+
#define A 0x67452301
15+
#define B 0xefcdab89
16+
#define C 0x98badcfe
17+
#define D 0x10325476
18+
19+
20+
/*
21+
* Bit-manipulation functions defined by the MD5 algorithm
22+
*/
23+
#define F(X, Y, Z) ((X & Y) | (~X & Z))
24+
#define G(X, Y, Z) ((X & Z) | (Y & ~Z))
25+
#define H(X, Y, Z) (X ^ Y ^ Z)
26+
#define I(X, Y, Z) (Y ^ (X | ~Z))
27+
28+
/*
29+
* Rotates a 32-bit word left by n bits
30+
*/
31+
uint32_t rotateLeft(uint32_t x, uint32_t n);
32+
void Init(MD5Context *ctx);
33+
void Update(MD5Context *ctx, uint8_t *input, size_t input_len);
34+
void Finalize(MD5Context *ctx);
35+
void Step(uint32_t *buffer, uint32_t *input);
36+
};
37+
38+
#endif

src/file/sac.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,18 @@
22
#include "../common/utils.h"
33
#include <iostream>
44

5+
std::streamsize Sac::WriteMD5(uint8_t digest[16])
6+
{
7+
file.write(reinterpret_cast<char*>(digest),16);
8+
return file.gcount();
9+
}
10+
11+
std::streamsize Sac::ReadMD5(uint8_t digest[16])
12+
{
13+
file.read(reinterpret_cast<char*>(digest), 16);
14+
return file.gcount();
15+
}
16+
517
int Sac::WriteHeader(Wav &myWav)
618
{
719
Chunks &myChunks=myWav.GetChunks();

src/file/sac.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ class Sac : public AudioFile
2121
int GetProfile(){return profile;};
2222
void WriteFrameHeader(tFrameHeader &hdr);
2323
int WriteHeader(Wav &myWav);
24+
std::streamsize WriteMD5(uint8_t digest[16]);
25+
std::streamsize ReadMD5(uint8_t digest[16]);
2426
int ReadHeader();
2527
int UnpackMetaData(Wav &myWav);
2628
std::vector <uint8_t>metadata;

src/file/wav.cpp

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,23 @@ size_t Chunks::UnpackMetaData(const std::vector <uint8_t>&data)
4646
return ofs;
4747
}
4848

49+
Wav::Wav(bool verbose)
50+
:chunkpos(0),datapos(0),endofdata(0),byterate(0),blockalign(0),samplesleft(0),verbose(verbose)
51+
{
52+
MD5::Init(&md5ctx);
53+
};
54+
55+
Wav::Wav(AudioFile &file,bool verbose)
56+
:AudioFile(file),chunkpos(0),verbose(verbose)
57+
{
58+
byterate=samplerate*numchannels*bitspersample/8;
59+
blockalign=numchannels*bitspersample/8;
60+
kbps=(samplerate*numchannels*bitspersample)/1000;
61+
62+
MD5::Init(&md5ctx);
63+
};
64+
65+
4966
void Wav::InitFileBuf(int maxframesize)
5067
{
5168
filebuffer.resize(maxframesize*blockalign);
@@ -57,9 +74,13 @@ int Wav::ReadSamples(std::vector <std::vector <int32_t>>&data,int samplestoread)
5774
if (samplestoread>samplesleft) samplestoread=samplesleft;
5875
int bytestoread=samplestoread*blockalign;
5976
file.read(reinterpret_cast<char*>(&filebuffer[0]),bytestoread);
60-
int samplesread=(file.gcount()/blockalign);
77+
int bytesread=file.gcount();
78+
int samplesread=bytesread/blockalign;
79+
6180
samplesleft-=samplesread;
62-
if (samplesread!=samplestoread) std::cout << "warning: read over eof\n";
81+
if (samplesread!=samplestoread) std::cerr << "warning: read over eof\n";
82+
83+
MD5::Update(&md5ctx, &filebuffer[0], bytestoread);
6384

6485
// decode samples
6586
int bufptr=0;
@@ -87,6 +108,8 @@ int Wav::WriteSamples(std::vector <std::vector <int32_t>>&data,int samplestowrit
87108
}
88109
int bytestowrite=samplestowrite*blockalign;
89110
file.write(reinterpret_cast<char*>(&filebuffer[0]),bytestowrite);
111+
112+
MD5::Update(&md5ctx, &filebuffer[0], bytestowrite);
90113
return 0;
91114
}
92115

src/file/wav.h

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
#define WAV_H
33

44
#include "file.h"
5+
#include "../common/md5.h"
56

67
class Chunks {
78
public:
@@ -25,21 +26,15 @@ class Chunks {
2526

2627
class Wav : public AudioFile {
2728
public:
28-
Wav(bool verbose=false)
29-
:chunkpos(0),datapos(0),endofdata(0),byterate(0),blockalign(0),samplesleft(0),verbose(verbose){};
30-
Wav(AudioFile &file,bool verbose=false)
31-
:AudioFile(file),chunkpos(0),verbose(verbose)
32-
{
33-
byterate=samplerate*numchannels*bitspersample/8;
34-
blockalign=numchannels*bitspersample/8;
35-
kbps=(samplerate*numchannels*bitspersample)/1000;
36-
};
29+
Wav(bool verbose=false);
30+
Wav(AudioFile &file,bool verbose=false);
3731
int ReadHeader();
3832
int WriteHeader();
3933
void InitFileBuf(int maxframesize);
4034
int ReadSamples(std::vector <std::vector <int32_t>>&data,int samplestoread);
4135
int WriteSamples(std::vector <std::vector <int32_t>>&data,int samplestowrite);
4236
Chunks &GetChunks(){return myChunks;};
37+
MD5::MD5Context md5ctx;
4338
private:
4439
Chunks myChunks;
4540
size_t chunkpos;

0 commit comments

Comments
 (0)