The following code tries to write the header of a WAV file to a stream
using a Buffer and then writing it to a WritableStream.
var fs = require("fs")
var samplesLength = 1000;
var sampleRate = 44100;
var outStream = fs.createWriteStream("zaz.wav")
var b = new Buffer(1024)
b.write('RIFF', 0);
/* file length */
b.writeUInt32LE(32 + samplesLength * 2, 4);
//b.writeUint32LE(0, 4);
b.write('WAVE', 8);
/* format chunk identifier */
b.write('fmt ', 12);
/* format chunk length */
b.writeUInt32LE(16, 16);
/* sample format (raw) */
b.writeUInt16LE(1, 20);
/* channel count */
b.writeUInt16LE(2, 22);
/* sample rate */
b.writeUInt32LE(sampleRate, 24);
/* byte rate (sample rate * block align) */
b.writeUInt32LE(sampleRate * 4, 28);
/* block align (channel count * bytes per sample) */
b.writeUInt16LE(4, 32);
/* bits per sample */
b.writeUInt16LE(16, 34);
/* data chunk identifier */
b.write('data', 36);
/* data chunk length */
//b.writeUInt32LE(40, samplesLength * 2);
b.writeUInt32LE(40, 0);
outStream.write(b.slice(0,50))
outStream.end()
For some reason, the first 8 bytes of the file are wrong :
hexdump -C zaz.wav
00000000 28 00 00 00 f0 07 00 00 57 41 56 45 66 6d 74 20 |(.......WAVEfmt |
00000010 10 00 00 00 01 00 02 00 44 ac 00 00 10 b1 02 00 |........D.......|
00000020 04 00 10 00 64 61 74 61 80 5a 57 ac ef 04 00 00 |....data.ZW.....|
00000030 18 57 |.W|
The first line should be :
00000000 52 49 46 46 24 00 ff 7f 57 41 56 45 66 6d 74 20 |RIFF$...WAVEfmt |
Update :
this line is at fault :
b.writeUInt32LE(40, 0);
it needs to be :
b.writeUInt32LE(0, 40);
The first 8 bits are written by b.writeUInt32LE(40, 0);.
= write (int) 40 (= 0x28) in Little Endian at offset 0.
I don't know exactly what you want but this is the problem.
Related
My PSSH-Box for PlayReady is below in base64 and hex format.
"playready": {
"pssh-base64": "AAAD4nBzc2gAAAAAmgTweZhAQoarkuZb4IhflQAAA8LCAwAAAQABALgDPABXAFIATQBIAEUAQQBEAEUAUgAgAHgAbQBsAG4AcwA9ACIAaAB0AHQAcAA6AC8ALwBzAGMAaABlAG0AYQBzAC4AbQBpAGMAcgBvAHMAbwBmAHQALgBjAG8AbQAvAEQAUgBNAC8AMgAwADAANwAvADAAMwAvAFAAbABhAHkAUgBlAGEAZAB5AEgAZQBhAGQAZQByACIAIAB2AGUAcgBzAGkAbwBuAD0AIgA0AC4AMAAuADAALgAwACIAPgA8AEQAQQBUAEEAPgA8AFAAUgBPAFQARQBDAFQASQBOAEYATwA+ADwASwBFAFkATABFAE4APgAxADYAPAAvAEsARQBZAEwARQBOAD4APABBAEwARwBJAEQAPgBBAEUAUwBDAFQAUgA8AC8AQQBMAEcASQBEAD4APAAvAFAAUgBPAFQARQBDAFQASQBOAEYATwA+ADwASwBJAEQAPgB2ADgAMgByAHMARwBsADUATgAwAE8AdQBLADgAcgBPAEgAQQBLAHgAcABRAD0APQA8AC8ASwBJAEQAPgA8AEwAQQBfAFUAUgBMAD4AaAB0AHQAcAA6AC8ALwBwAHIALQBrAGUAeQBvAHMALgBsAGkAYwBlAG4AcwBlAGsAZQB5AHMAZQByAHYAZQByAC4AYwBvAG0ALwBjAG8AcgBlAC8AcgBpAGcAaAB0AHMAbQBhAG4AYQBnAGUAcgAuAGEAcwBtAHgAPAAvAEwAQQBfAFUAUgBMAD4APABEAFMAXwBJAEQAPgBWAGwAUgA3AEkAZABzAEkASgBFAHUAUgBkADAANgBMAGEAcQBzADIAagB3AD0APQA8AC8ARABTAF8ASQBEAD4APABDAFUAUwBUAE8ATQBBAFQAVABSAEkAQgBVAFQARQBTACAAeABtAGwAbgBzAD0AIgAiAD4APABDAEkARAA+AHYAOAAyAHIAcwBHAGwANQBOADAATwB1AEsAOAByAE8ASABBAEsAeABwAFEAPQA9ADwALwBDAEkARAA+ADwARABSAE0AVABZAFAARQA+AHMAbQBvAG8AdABoADwALwBEAFIATQBUAFkAUABFAD4APAAvAEMAVQBTAFQATwBNAEEAVABUAFIASQBCAFUAVABFAFMAPgA8AEMASABFAEMASwBTAFUATQA+ADgARQBLAEQAcQBJAHgAUwAwAFEAWQA9ADwALwBDAEgARQBDAEsAUwBVAE0APgA8AC8ARABBAFQAQQA+ADwALwBXAFIATQBIAEUAQQBEAEUAUgA+AA==",
"pssh-hex": "000003e270737368000000009a04f07998404286ab92e65be0885f95000003c2c203000001000100b8033c00570052004d00480045004100440045005200200078006d006c006e0073003d00220068007400740070003a002f002f0073006300680065006d00610073002e006d006900630072006f0073006f00660074002e0063006f006d002f00440052004d002f0032003000300037002f00300033002f0050006c00610079005200650061006400790048006500610064006500720022002000760065007200730069006f006e003d00220034002e0030002e0030002e00300022003e003c0044004100540041003e003c00500052004f00540045004300540049004e0046004f003e003c004b00450059004c0045004e003e00310036003c002f004b00450059004c0045004e003e003c0041004c004700490044003e004100450053004300540052003c002f0041004c004700490044003e003c002f00500052004f00540045004300540049004e0046004f003e003c004b00490044003e007600380032007200730047006c0035004e0030004f0075004b00380072004f00480041004b007800700051003d003d003c002f004b00490044003e003c004c0041005f00550052004c003e0068007400740070003a002f002f00700072002d006b00650079006f0073002e006c006900630065006e00730065006b00650079007300650072007600650072002e0063006f006d002f0063006f00720065002f007200690067006800740073006d0061006e0061006700650072002e00610073006d0078003c002f004c0041005f00550052004c003e003c00440053005f00490044003e0056006c005200370049006400730049004a004500750052006400300036004c0061007100730032006a0077003d003d003c002f00440053005f00490044003e003c0043005500530054004f004d004100540054005200490042005500540045005300200078006d006c006e0073003d00220022003e003c004300490044003e007600380032007200730047006c0035004e0030004f0075004b00380072004f00480041004b007800700051003d003d003c002f004300490044003e003c00440052004d0054005900500045003e0073006d006f006f00740068003c002f00440052004d0054005900500045003e003c002f0043005500530054004f004d0041005400540052004900420055005400450053003e003c0043004800450043004b00530055004d003e00380045004b00440071004900780053003000510059003d003c002f0043004800450043004b00530055004d003e003c002f0044004100540041003e003c002f00570052004d004800450041004400450052003e00"
}
I use mspr:pro tag in the manifest.
As far as I understand, when using mspr:pro , you enter PlayReady Object not PSSH-Box ( for PSSH-BOX cenc:ppsh tag using in manifest ).
How can I build Playready Object from PSSH ?
Just strip the PSSH box header.
PSSH box syntax:
aligned(8) class ProtectionSystemSpecificHeaderBox extends FullBox(‘pssh’, version, flags=0)
{
unsigned int(8)[16] SystemID;
if (version > 0)
{
unsigned int(32) KID_count;
{
unsigned int(8)[16] KID;
} [KID_count]
}
unsigned int(32) DataSize; unsigned
int(8)[DataSize] Data;
}
PSSH hex from your example:
// PSSH, big endian
00 00 03 E2 70 73 73 68 // box header, 994 bytes "pssh"
00 00 00 00 // full box header, version 0, flags 0
9A 04 F0 79 98 40 42 86 AB 92 E6 5B E0 88 5F 95 // PlayReady system ID
00 00 03 C2 // data size, 962 bytes
// PlayReady Object, little-endian
C2 03 00 00 // object length, 962 bytes
01 00 // record count, 1
01 00 // record type, 1 - PRH
B8 03 // record length, 952 bytes
3C 00 57 00 52 [...] // record value, "<WRMHEADER xmlns=..."
You can also decode this online using: https://tools.axinom.com/decoders/PsshBox
Reference:
ISO/IEC 23001-7
PlayReady Header Specification
I'm running NodeJS 8.12.0 and have to set a signature over a hash, without re-hashing it, performing a raw signature. Or, in other words, encrypt the hashed value with the private key.
const crypto = require('crypto');
// 4096 bits key.
let pk = "<BASE64 DER>";
let pub = "<BASE64 DER>";
// Transform them to PEM.
pk = `-----BEGIN PRIVATE KEY-----\n${pk.replace('\n', '')}\n-----END PRIVATE KEY-----\n`;
pub = `-----BEGIN PUBLIC KEY-----\n${pub.replace('\n', '')}\n-----END PUBLIC KEY-----\n`;
// Load the data to sign and set the signature.
const fingerprint = Buffer.from('2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824','hex');
const signature = crypto.privateEncrypt({
key: pk,
padding: crypto.constants.RSA_PKCS1_PADDING
},
fingerprint
);
// Unfortunately, the server is not able to verify the signature...
console.log(signature.toString('hex'));
So I took a look at raw encryption of the message hash with my private key and ended up with some sort of EMSA encoding and followed these steps:
Apply the hash function to the message
Encode the algorithm ID for the hash function and the hash into ASN.1 value of DigestInfo (Appendix A.2.4)
Generate an octet string PS consisting of emLen - tLen - 3 octets of 0xff
Concatenate PS, the DER encoded value T, and other padding to form the encoded message EM as EM = 0x00 || 0x01 || PS || 0x00 || T
So, working that out
// 2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824
H = 2c f2 4d ba 5f b0 a3 0e 26 e8 3b 2a c5 b9 e2 9e 1b 16 1e 5c 1f a7 42 5e 73 04 33 62 93 8b 98 24
emLen = 512
T = 30 31 30 0d 06 09 60 86 48 01 65 03 04 02 01 05 00 04 20 2c f2 4d ba 5f b0 a3 0e 26 e8 3b 2a c5 b9 e2 9e 1b 16 1e 5c 1f a7 42 5e 73 04 33 62 93 8b 98 24
PS = 04 06 02 00 33 ff ff ff
// 00010406020033ffffff003031300d0609608648016503040201050004202cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824
EM = 00 01 04 06 02 00 33 ff ff ff 00 30 31 30 0d 06 09 60 86 48 01 65 03 04 02 01 05 00 04 20 2c f2 4d ba 5f b0 a3 0e 26 e8 3b 2a c5 b9 e2 9e 1b 16 1e 5c 1f a7 42 5e 73 04 33 62 93 8b 98 24
But when I put that into the privateEncrypt, I do not get the correct output either. Can anyone help me here?
You are trying to do the PKCS1-padding (RSASSA-PKCS1-V1_5) manually. But this isn't necessary. It's enough to concatenate the Hash-ID (here for SHA-256) and your data (fingerprint), the rest is done by the implicitly selected padding (crypto.constants.RSA_PKCS1_PADDING), i.e.
// Signing
var fingerprint = Buffer.from('2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824','hex');
var id = Buffer.from([0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20]);
var allData = Buffer.concat([id, fingerprint]);
var signature = crypto.privateEncrypt(privateKey, allData); // crypto.constants.RSA_PKCS1_PADDING by default
// Verifying with createVerify
var verify = crypto.createVerify('RSA-SHA256');
verify.update('<the signed data>');
var verified = verify.verify(publicKey, signature); // provides true
// Verifying with publicDecrypt
var decryptedFingerprint = crypto.publicDecrypt(publicKey, Buffer.from(signature)).slice(-32); // provides fingerprint
Note: If you want to do the padding manually, you have to set the byte sequence 0x00 || 0x01 || PS || 0x00 before the allData-buffer. PS consists of as many 0xff-bytes as necessary to reach the key length (in bytes). In addition, the flag
crypto.constants.RSA_NO_PADDING must be set explicitly in the privateEncrypt-call. However, this isn't necessary, since the result is the same. The details are described in RFC 8017, Section 8.2.1
I am currently looking at online examples and here is a WAV file contents in bytes
52 49 46 46 24 08 00 00 57 41 56 45 66 6d 74 20 10 00 00 00 01 00 02 00
22 56 00 00 88 58 01 00 04 00 10 00 64 61 74 61 00 08 00 00 00 00 00 00
24 17 1e f3 3c 13 3c 14 16 f9 18 f9 34 e7 23 a6 3c f2 24 f2 11 ce 1a 0d
and here is the visual; representation:
So according to the Subchunk2Size there is 2048 bytes in the data. The formula to calculate the number of samples in a WAV is given as:
Subchunk2Size /(NumChannels * BitsPerSample/8 ) = NumSamples
If I plugin numbers and according to the information given I get NumSamples = 512. But in the diagram the sample rate is 22050. How can the total number fo samples be less than a single second of samples?
For those wondering, here is a link to the source.
I suspect they are just using a bad example where the duration of the wav file would be less than a second. Their formula makes sense and we can use it to verify the data size of a one second wav file.
If our sample rate is 22050 samples/sec and our wav file is one second, then numSamples = 22050. We know that Subchunk2Size is the number of bytes in the data and can be calculated using this formula: Subchunk2Size = numSamples * numChannels * bitsPerSample / 8 , so, assuming numChannels = 2 and bitsPerSample = 16, we know that a one second wav file should be (22050 * 2 * 16 / 8) bytes which is 88200 bytes, so it would make sense that if Subchunk2Size is 2048 bytes, as per the website's example, then the duration of the wav file would be less than a second and thus, numSamples would be less than 22050.
I tried to dump all data sent by a specific process on Linux by hooking a handler to the Kernel's function sock_sendmsg() defined in linux/socket.c.
I could do that by writing a systemtap probe handler for probe kernel.function("sock_sendmsg#net/socket.c") that dumps all data blocks passed with the 2nd argument struct msghdr *msg.
Here's the excerpt from net/socket.c:
int sock_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
{
struct kiocb iocb;
struct sock_iocb siocb;
int ret;
init_sync_kiocb(&iocb, NULL);
iocb.private = &siocb;
ret = __sock_sendmsg(&iocb, sock, msg, size);
if (-EIOCBQUEUED == ret)
ret = wait_on_sync_kiocb(&iocb);
return ret;
}
I tested my systemtap script hook_sendmsg.stp. First I ran hook_sendmsg.stp in one terminal. Then I opened another terminal and invoked telnet command to connect to stackoverflow.com and typed HEAD / HTTP/1.0<Enter twice> in the terminal. I got the following output from hook_sendmsg.stp:
root#debian:~# stap -g hook_sendmsg.stp
message block [0]; type=3(raw); state=1(unconnected)
14 00 00 00 16 00 01 03 ec 95 f4 52 00 00 00 00 |...........R....|
00 00 00 00 |................|
message block [0]; type=3(raw); state=1(unconnected)
14 00 00 00 16 00 01 03 ec 95 f4 52 00 00 00 00 |...........R....|
00 00 00 00 |................|
message block [0]; type=2(udp); state=1(unconnected)
4d 0d 01 00 00 01 00 00 00 00 00 00 0d 73 74 61 |M............sta|
63 6b 6f 76 65 72 66 6c 6f 77 03 63 6f 6d 00 00 |ckoverflow.com..|
01 00 01 |................|
message block [0]; type=2(udp); state=1(unconnected)
0f 1e 01 00 00 01 00 00 00 00 00 00 0d 73 74 61 |.............sta|
63 6b 6f 76 65 72 66 6c 6f 77 03 63 6f 6d 00 00 |ckoverflow.com..|
1c 00 01 |................|
message block [0]; type=1(tcp); state=3(connected)
48 45 41 44 20 2f 20 48 54 54 50 2f 31 2e 30 0d |HEAD / HTTP/1.0.|
0a |................|
message block [0]; type=1(tcp); state=3(connected)
0d 0a |................|
This shows that totally sock_sendmsg() were called 6 times in the context of telnet. Obviously the 3rd and 4th are DNS queries to Google's public DNS servers 8.8.8.8 and 8.8.4.4. The 5th and 6th are the two lines of HTTP request sent from telnet. But what were the 1st and 2nd called for? Did they called internally by the Kernel?
Thanks in advance.
They are NETLINK messages, as you can see if you monitor the telnet command from userspace with strace (check the socket family):
sendto(3, "\24\0\0\0\26\0\1\3?\254\364R\0\0\0\0\0\0\0\0", 20, 0, {sa_family=AF_NETLINK, pid=0, groups=00000000}, 12) = 20
sendto(3, "\24\0\0\0\26\0\1\3?\254\364R\0\0\0\0\0\0\0\0", 20, 0, {sa_family=AF_NETLINK, pid=0, groups=00000000}, 12) = 20
send(3, "\212\355\1\0\0\1\0\0\0\0\0\0\rstackoverflow\3com\0\0"..., 35, MSG_NOSIGNAL) = 35
send(3, "\241\353\1\0\0\1\0\0\0\0\0\0\rstackoverflow\3com\10i"..., 50, MSG_NOSIGNAL) = 50
send(3, "|\341\1\0\0\1\0\0\0\0\0\0\rstackoverflow\3com\0\0"..., 35, MSG_NOSIGNAL) = 35
send(3, "HEAD / HTTP/1.0\r\n", 17, 0) = 17
send(3, "\r\n", 2, 0) = 2
Netlink is an interface used by the userspace processes to communicate with kernel (so, they are send by the telnet), usually for routing, firewalling... pourposes [1]. Some usual network C functions (i.e. getsockname) works internally with this kind of NETLINK messages.
If you want to investigate a bit more you should translate the payload of the send_message to the netlink header structure and check what type of messages are.:
struct nlmsghdr {
__u32 nlmsg_len; /* Length of message including header. */
__u16 nlmsg_type; /* Type of message content. */
__u16 nlmsg_flags; /* Additional flags. */
__u32 nlmsg_seq; /* Sequence number. */
__u32 nlmsg_pid; /* Sender port ID. */
};
[1]http://man7.org/linux/man-pages/man7/netlink.7.html
I am trying to write a parser to extract information from the following FLAC file:
$ hd audio.flac | head -n 6
00000000 66 4c 61 43 00 00 00 22 12 00 12 00 00 00 00 00 |fLaC..."........|
00000010 00 00 0a c4 42 f0 00 78 9f 30 00 00 00 00 00 00 |....B..x.0......|
00000020 00 00 00 00 00 00 00 00 00 00 84 00 02 64 1f 00 |.............d..|
00000030 00 00 47 53 74 72 65 61 6d 65 72 20 65 6e 63 6f |..GStreamer enco|
00000040 64 65 64 20 76 6f 72 62 69 73 63 6f 6d 6d 65 6e |ded vorbiscommen|
00000050 74 10 00 00 00 12 00 00 00 54 49 54 4c 45 3d 52 |t........TITLE=R|
Now, according to the specification, the format should be as follow (numbers are in bits):
<32> "fLaC", the FLAC stream marker in ASCII
<16> The minimum block size (in samples) used in the stream.
<16> The maximum block size (in samples) used in the stream.
<24> The minimum frame size (in bytes) used in the stream.
<24> The maximum frame size (in bytes) used in the stream.
<20> Sample rate in Hz.
<3> (number of channels)-1. FLAC supports from 1 to 8 channels
<5> (bits per sample)-1. FLAC supports from 4 to 32 bits per sample.
<36> Total samples in stream.
<128> MD5 signature of the unencoded audio data.
So, I start to write my parser and, while testing, get very strange results. So I test with a "real" metadata extractor:
$ metaflac --list audio.flac
METADATA block #0
type: 0 (STREAMINFO)
is last: false
length: 34
minimum blocksize: 4608 samples
maximum blocksize: 4608 samples
minimum framesize: 0 bytes
maximum framesize: 0 bytes
sample_rate: 44100 Hz
channels: 2
bits-per-sample: 16
total samples: 7905072
MD5 signature: 00000000000000000000000000000000
From the numbers, I can deduce the following:
66 4c 61 43 00 00 00 22 12 00 12 00 00 00 00 00
~~~~~~~~~~~ ~~~~~~~~~~~ ~~~~~ ~~~~~ ~~~~~~~~ ~~
^ ^ ^ ^ ^ ^
| | | | | |
| | | | | + Etc.
| | | | + Minimum frame size
| | | + Maximum block size
| | + Minimum block size
| + What is that ?!?
+ FLAC stream marker
Where does those 32 bits come from? I see they represent the length of the header, but isn't it against the standard to put it there (Taking into account that we already know the length: (32+16+16+24+20+3+5+36+128)/8)?
The 0x22 (34) is indeed the header block size in bytes as part of the METADATA_BLOCK_HEADER which follows the fLaC marker in the stream. Of the first 8 bits (00), bit 7 indicates that there are more metadatablocks to follow, the next 7 bits indicate that it's a STREAMINFO block. The following 3 bytes (00 00 22) is the length of the contents of the block;
16 + 16 + 24 + 24 + 20 + 3 + 5 + 36 + 128 = 272 bits
272 bits / 8 = 34 (0x22) bytes.