I tried to understand how the implementation of srtp on the linphone application. When I activate the srtp feature on the phone, the digital data communication will be secured using the AES-ICM encryption method. But I found something interesting in its implementation.
The encryption process in the AES-ICM method works with xor operations between keystream suffix and RTP packet payload (plaintext) to produce ciphertext. The encryption key is used in an aes operation to generate a suffix keystream. This ciphertext will then be sent from the sender to the receiver.
In the implementation of SRTP, I display the plaintext, key and ciphertext used or generated by the sender and receiver. I found the difference between the ciphertext and key for each packet in the sender and receiver. When a plaintext is encrypted using a specific key and produces a ciphertext in the sender, the receiver, the ciphertext that is received and the key that is used has a difference. However, the strange thing that I found was that the results of the decryption of the plaintext on the receiver are the same plaintext as what is encrypted in the sender. And the voice that was communicated arrived, can anyone explain this why?
Code to display text : https://pastelink.net/1re6m
Log endpoint 1 : https://pastelink.net/1re93
Log endpoint 2 : https://pastelink.net/1re9c
Then, I tried to deactivate the encryption feature by using the srtp_cipher_encrypt code in the srtp_protect_mki and srtp_cipher_decrypt modules in the srtp_unprotect_mki module. Isn't that as if it didn't encrypt the payload like the Null Cipher is implemented? However, the same ones found are ciphertext and plaintext that have nothing in common with the sender and receiver as well as the sound on the speakers that contain noise without being able to identify what is being sent. Can anyone explain this too?
For example, the first plaintext used for encryption at endpoint one is:
"d5d5d5d5d5d5d5d5d5d5d5d5d5d5d5d5d5d5d5d ......"
generate ciphertext:
"7c8fb6c2f783e5fdb34116bb5d5ce27475bf400b4 ........"
then at endpoint 2 which acts as a receiver, the first ciphertext received is
"e2bd90a5275e44ac2d4fc332cfff138e743c39c80b ......."
generate plaintext:
"d5d5d5d5d5d5d5d5d5d5d5d5d5d5d5d5d5d5d5d5 ......."
Thank you
I find the answer from another forum. In my case this is happening because the VOIP server which is I am using Asterisk will decrypt every incoming packet for processing before sending it back. This package will then be re-encrypted by Asterisk before being sent to its final destination. Every packet that enters will be decrypted and the one that exits the asterisk will be re-encrypted. So, in my case, this happened because of the voip server technology used.
Related
I have been working to create an RTP/RTCP client and server implementation as a project. I have hit a block when it comes to parsing incoming packets because I am having trouble understanding the encryption method. I have read RFC 3550 several times. Section 9 explains confidentiality and security. I've read about DES-CBC mode here.
Observations I've deduced about encryption
RTP/RTCP packets are encrypted as a unit, meaning ALL bytes are encrypted
Encrypted RTCP packets are prefixed with a 32-bit random number
Encrypted RTP packets are randomized by the timestamp and sequence number
DES-CBC is the default mode
DES-CBC mode requires a 64-bit key
DES-CBC mode requires a 64-bit initialization vector (IV)
DES-CBC has a block size of 64 bits
What I'm confused about:
DES-CBC states that it uses the "Privacy Enhancement for Internet Electronic Mail" (PEM) protocol, but the RTP RFC makes no mention of this. In addition, the encryption diagrams do not include any PEM headers or elements.
Diagram
UDP packet UDP packet
----------------------------- ------------------------------
[random][RR][SDES #CNAME ...] [SR #senderinfo #site1 #site2]
----------------------------- ------------------------------
encrypted not encrypted
So my questions are:
Where does the key come from or what header elements make up the key?
What is the initialization vector for RTP/RTCP?
How do I parse an encrypted packet vs an unencrypted one?
Does RTP/RTCP use the PEM protocol? If so, how?
DES-CBC is a way for encrypting data and it can be used for all different protocols that need it's data to be encrypted.
Where does the key come from or what header elements make up the key?
Encryption keys are generated during connection initialization(at connect time) and can be generated using different ways but generally using Public key cryptography(ex: RSA and DH) and using certificates to prevent MITM attacks which is where the PEM standards are used.
What is the initialization vector for RTP/RTCP?
The IV(initialization vector) is randomly generated number that will be used to ensure that same plaintext will not be encrypted to same ciphertext if using same key (That's why it's Random).
How do I parse an encrypted packet vs an unencrypted one?
For encrypted packets you decrypt first using key(SECRET) you got at connection time then use IV which is sent with data packets(NOT secret) to reverse encryption process. For unencrypted packets you just parse the data since there is no KEY and IV data is just plaintext.
Does RTP/RTCP use the PEM protocol? If so, how?
PEM is not protocol in the sense as network protocols such SSH. This definition I think is accurate enough from wikipedia
Privacy-Enhanced Mail (PEM) is a de facto file format for storing and sending cryptographic keys, certificates, and other data, based on a set of 1993 IETF standards defining "privacy-enhanced mail.
so if your client is using encryption there is a very big chance that it will be using PEM standards for symmetric key initialization.
NOTE: timestamp and sequence number are different from IV these fields are RTP header fields which can serve a similar purpose but they are different from CBC mode IV.
I'm writing a module that creates a secure communication channel using ZeroMQ sockets and Pycryptodome.
The initial handshake between client and server would follow these steps :
Both parties send their public RSA key to each other.
The server generates an AES session key and a signature for that key.
The server RSA-encrypts the session key and the signature before sending it to the client. (*)
The client verifies the signature and stores the session key.
The client generates a token, generates its signature and sends both AES-encrypted to the server.
The server verifies the signature and echoes back the token.
If the token received matches the one sent, the handshake is considered successful.
I found on this thread that it was preferable to sign the message then encrypt it, rather than encrypting then signing it.
The problem is that the signature, for a 2048 bits RSA key, is 256 bytes long. The maximum encryption size for the same key is 190 bytes. This means I can't encrypt the signature as suggested in the thread.
Should I encrypt the signature with the AES session key ? Should I proceed another way ?
I know there are "standardized" key exchange protocols (ECDH for example) but they are not available in Pycryptodome yet.
Cipher: RSA PKCS1 OAEP
Signing : PKCS1 PSS w/ BLAKE2b hash
I have two ESP8266 microcontroller boards:
Board A is running a HTTP server and is able to switch a relay by GET request from Board B, which is the HTTP client.
To ensure that only Board B, and nobody else, will switch the relais on Board A, I want to implement some kind of challenge response authentication.
My idea was the following:
Board B asks Board A to switch the relay
Board A sends some random bytes as a challenge
Board B encrypts these raw bytes with XTEA algorithm and returns the value to Board A
Board A deciphers the response from Board B and compares it with its own result. If the response arrives too late (e.g. after one second) or the response is invalid, the authentication will be aborted and a new challenge will be generated next time. If the response is valid the relay will switch and there will also be a new challenge for the next attempt.
So if an attacker is sniffing network communication, he will receive both the raw bytes and the encrypted ones.
My questions to you:
Is it (easily) possible to calculate the XTEA key if the attacker knows raw bytes and the encryptes ones?
Is the described method a reasonable solution for my problem?
Thanks in advance,
Chris
DISCLAIMER: i am not a cryptography expert.
Is it (easily) possible to calculate the XTEA key if the attacker knows raw bytes and the encryptes ones?
nope, you still have to do a bruteforce to deduce the key and number of rounds used, AFAIK. (at least if you're using 19 rounds or more, as currently the only known XTEA cryptographic attacks affects 18 rounds or less, as of 2009. but given that the default-and-recommended is 32 rounds, that shouldn't be an issue unless you use a custom and low number of rounds.. like 18)
Is the described method a reasonable solution for my problem?
your protocol is vulnerable to bit-flipping attacks from a MITM attacker, as well as not providing protection against snooping/monitoring, a MITM attacker will know what command you're giving, and be able to change the command given, both of which could be easily avoided...
i think it would be better if the client just asks for the random bytes as a token, and sends the actual command together with the token, encrypted. this will protect your command from snooping, it will make a MITM attacker unable to deduce what command you sent even IF the attacker knows how the protocol works, as the token now serves as a salt for the encrypted command.. but you're still vulnerable to bit-flipping from a MITM attacker even if the attacker does not know the key, thus you should also add a checksum to make sure the ciphertext has not been tampered with... how about for the client:
// start with the actual command
$data=encrypt("switch_relay(5);"); // or whatever
function encrypt(string $command){
// because of XTEA length padding, we need to tell the server the inner command length, so add a big endian 16 bit `size header`
$data=to_big_endian_uint16_t(strlen($data)).$data;
// get a unique 1-time-token? this should serve as salt AND protect against replay attack
$token=fetchToken();
// add the token
$data=$token.$data;
// now calculate a checksum to protect against bit-flipping attacks
$checksum=hash('adler32be',$data); // or whatever checksum you prefer. just has to be strong enough to detect random bit-flipping from attackers that can't decrypt-modify-encrypt because they don't know the encryption key, see https://en.wikipedia.org/wiki/Malleability_(cryptography) / https://en.wikipedia.org/wiki/Bit-flipping_attack
// add checksum
$data=$checksum.$data;
// encrypt data
$data=XTEA::encrypt($data, $key, XTEA::PAD_RANDOM, 32);
return $data;
}
after this i would normally add another size header so the server knows how many bytes to read for the entire packet, but since you say you're using the HTTP protocol, i assume you'll use a Content-Length: X header as the outer size header.. (or if you don't, you should probably do another $data=big_endian_uint16_t(strlen($data)).$data; after xtea-encrypting it)
and for the server do like
function decrypt(string $data){
// 4=checksum 8=token 2=inner_command_length
if(strlen($data) < (4+8+2) || strlen($data) % 8 !== 0){
// can't be an xtea-encrypted command, wrong length.
return ERR_INVALID_LENGTH;
}
$data=XTEA::decrypt($data,$key,32);
$checksum=substr($data,0,4);
$data=substr($data,4);
if(hash('adler32be',$data)!=$checksum){
// checksum fail, can't be an xtea-encrypted command (or maybe it was corrupted or tampered with?)
return ERR_INVALID_CHECKSUM;
}
$token=substr($data,0,8);
$data=substr($data,8);
if(!is_valid_token($token)){
return ERR_INVALID_TOKEN;
}
$inner_size_len=big_endian_uint16_t_to_native_number(substr($data,0,2));
$data=substr($data,2);
if(strlen($data) < $inner_size_len){
return ERR_INVALID_INNER_SIZE;
}
// remove padding bytes
$data=substr($data,0,$inner_size_len);
return $data; // the actual decrypted command
}
..?
(i still see 3 potential issues with this, 1: forward secrecy is not provided, for that you'd need something much more complex, i think. 2: an attacker could maybe DoS-attack you by requesting one-time-tokens until you run out of ram or whatever, preventing legitimate clients from generating tokens, but given the token lifetime of 1 second, it would have to be a continuous active attack, and stop working once the attacker is blocked/removed. 3: if your commands can be larger than 65535 bytes, you may want to switch to a 32bit size header, or if your commands can be over 4GB, you may want to switch to an 64bit size header, and so on. but if your commands are small, a 16bit size header at 65535 bytes should suffice?)
I am using TLS_RSA_WITH_3DES_EDE_CBC_SHA cipher suite, and I have sucessfully finished the handshake process, so i should have the correct KEYS for the server side and client side, but when i use the server write key and iv to encrypt the data and put under record layer(applicaiton type) send to client, but when i use wireshark ssl debug, i found out that wireshark didn't decrypt the application data correctly, wonder is the application data use a different key to do the encryption and decryption? Thanks
I have finally found the problem, TLS 1.0 used the last block of encrypted data as new IV to encrypt the data. its a little surprised, the encrypted data is visible on the network, so anyone capture that data can find out the IV.
i try to use chacha20 encryption in my network software but i encounter a problem
if i encrypt 4 bytes data: 0x01 0x02 0x03 0x04 on server
and get the ciphertext: 0xd2 0xd3 0xc4 0xd5, then send it to client
the client may receive <= 4bytes at a time
provided that client only recv 0xd2 0xd3 at first ,
it can decrypt data properly and get plaintext 0x01 0x02
but when client recv the last 2 bytes 0xc4 0xd5
it seems that the data can't be decrypt using the same nonce and key
so is there a way to solve the problem
adding length data as prefix before sending is a solution,but it's weird because i am using stream cipher.
Instead of restarting the ChaCha20 cipher instance (or, more generically, context) you should keep it alive.
Most cryptographic API's will allow you to perform piecemeal encryption / decryption. That usually means calling an update method for the first part and second part, generally followed by a final method when the end of the plaintext / ciphertext is detected. Depending on the API you should expect output for each of these methods.
Only if your API doesn't allow you to handle the stream correctly then you should aggregate the ciphertext and perform the decryption on the full ciphertext.
ChaCha20 generates a stream, using the key and the nonce. Let (S0, S1, S2, S3) the first bytes of the stream, and (M0, M1, M2, M3) the first 4 bytes of your message.
The ciphertext will be computed as (M0⊕S0, M1⊕S1, M2⊕S2, M3⊕S3). This is if you have M0...M3 readily available.
If you encrypt (M0, M1) then (M2, M3) using the same key and nonce, you will end up with (M0⊕S0, M1⊕S1) and (M2⊕S0, M3⊕S1). Which cannot be decrypted using (C0⊕S0, C1⊕S1, C2⊕S2, C3⊕S3).
Even worse, since S0 and S1 have been reused with different messages, an attacker can recover them knowing any message.
The easiest thing to do in order to avoid this is to buffer the data until you reach the block size, and then encrypt the whole block, instead of trying to encrypt partial blocks.