I detoured recv function, i trying to decrypt buffer, but decrypt function change buffer size, and i think decryotion is invalid, code:
int WINAPI OwnRecv(SOCKET s, char FAR *buff, int len, int flags)
{
if(s == GameClientSocket)
{
int received = pTrampolineRecv(s, buff, len, flags);
if(received <= 0)
{
return received;
}
// now strlen(buff) is 2!!
char * plaintext;
plaintext = (char *)aes_decrypt(&Decrypt_Context, (unsigned char*)buff, &received);
(char *) buff = plaintext; // now strlen(buff) is 5!!
return received;
}
return pTrampolineRecv(s, buff, len, flags);
}
What's wrong with my code?
Thanks!
You forgot to implement a protocol! Whatever protocol you use to encrypt and decrypt the data, you have to actually implement it. It has to define block sizes, padding, and so on. It won't just work by magic. (Note that using a stream cipher will make this much easier than using a block cipher.)
Also, don't call strlen on arbitrary binary data! The strlen function is only for C-style strings.
Also, this line of code doesn't do what you think it does:
(char *) buff = plaintext; // now strlen(buff) is 5!!
Changing the value of buff, the variable that holds a pointer to the buffer, has no effect on the contents of the buffer. That's all the caller cares about.
Related
I am new to socket programming, so be kind :)
I am writing a client-server application in C++ and using OpenSSL. Till now I have generated the public-private keys for the client and server and have exchanged it over the network. Now is the part where I want to encrypt my client's message using the server's public key. But my public_encrypt function returns gibberish. I know the methods which I am using are deprecated and there are better methods but the purpose is to get the hands dirty only.
Below is the function that invokes the encryption API. (Ignore the if part, it's for sending the clients public key)
#define RSA_SIZE 256
void sendMessage(int clientFD, uint16_t type, char *data, serverState *server){
uint16_t length = strlen(data);
unsigned char message[MESSAGE_SIZE];
if (server->state == 0)
{
memcpy(message, (char *)&length, sizeof(length));
memcpy(message + 2, (char *)&type, sizeof(type));
memcpy(message + 4, data, length);
send(clientFD, message, 4 + length, 0);
server->state = 1;
}
else
{
unsigned char encrypted[RSA_SIZE] = {0};
length = public_encrypt(reinterpret_cast<unsigned char *>(data), length, server->key, encrypted);
assert(length != -1);
printf("%s\n", encrypted);
memcpy(message, (char *)&length, sizeof(length));
memcpy(message + 2, (char *)&type, sizeof(type));
memcpy(message + 4, encrypted, length);
send(clientFD, message, 4 + length, 0);
}}
This is the code for the encryption
int padding = RSA_PKCS1_OAEP_PADDING;
RSA *createRSA(unsigned char *key, int pub){
RSA *rsa = NULL;
BIO *keybio;
keybio = BIO_new_mem_buf(key, -1);
if (keybio == NULL)
{
printf("Failed to create key BIO");
return 0;
}
if (pub)
{
rsa = PEM_read_bio_RSA_PUBKEY(keybio, &rsa, NULL, NULL);
}
else
{
rsa = PEM_read_bio_RSAPrivateKey(keybio, &rsa, NULL, NULL);
}
if (rsa == NULL)
{
printf("Failed to create RSA");
}
return rsa;}
int public_encrypt(unsigned char *data, int data_len, unsigned char *key, unsigned char *encrypted){
printf("Data:%s\n:", data);
printf("Data Length:%d\n:", data_len);
printf("Server's Key:\n%s\n:", key);
RSA *rsa = createRSA(key, 1);
int result = RSA_public_encrypt(data_len, data, encrypted, rsa, padding);
return result;}
Please check out the link https://i.stack.imgur.com/WJn7e.png to see my output.
PS: Sorry for such a long post.
The output of RSA is a random value between 0 and the modulus of the RSA private key, encoded as an unsigned big endian octet string (octet string is just another name for byte array, a char[] in C / C++). It contains bytes with any value, and it is therefore certainly not ASCII. If you want ASCII you have to base 64 encode the ciphertext.
However, quite often ciphertext is "stringified" for no good reason at all, so only do this if this is necessary within your protocol / system. Python strings are made somewhat readable for you by the Python runtime. I'm not sure if that's a good thing or not - it's certainly not a good idea to copy that string as it is only Python proprietary.
C is not as forgiving, if you treat the binary array as text you'll run into trouble, as it can contain any character, including control characters and the NUL character (00), which can play merry hell with functions such as strlen and many others that expect a textual string instead of an array of bytes (both are usually based on char in C/C++).
I want to send a string from a server to a client. To account for the case that the string is bigger than the buffer that the client reads into, I send a byte count to the client before the actual string. Then on the client side, I split the read operation into multiple recv() calls if needed.
Server:
// Send byte count to client so it knows how much data to expect
strcpy(buffer, std::to_string(string_size).c_str());
int result{ send(socket, buffer, buffer_size, 0) };
// Send actual data
unsigned int bytes_to_send{ sizeof(s) };
while (bytes_to_send > 0) {
send_bytes = std::min(buffer_size, bytes_to_send);
strcpy(buffer, s.substr(0, send_bytes).c_str());
result = send(socket, buffer, send_bytes, 0);
if (s.size() > send_bytes) {
s = s.substr(send_bytes, s.size() - send_bytes);
}
bytes_to_send -= send_bytes;
return_int += send_bytes;
(In the actual code, I check result to see whether send() succeeded)
Client:
result = recv(socket, buffer, buffer_size, 0);
count = std::stoi(buffer);
while(count > 0){
read_bytes = std::min(count, buffer_size);
result = recv(socket, buffer, read_bytes, 0);
if(result < 1 ) {
// throw exception
}else{
return_string += buffer;
}
count -= read_bytes;
What I expected:
Client blocks on first recv() call until server sends byte count
Client proceeds until second recv() call, then blocks until server sends string
What actually happens:
Client blocks on first recv() call until server sends byte count
Client does not block on second recv() call. recv() return value is equal to read_bytes, but does not fill the buffer with anything
Thanks in advance for any help with this
There are quite a few problems with your code.
strcpy(buffer, std::to_string(string_size).c_str()); is a buffer overflow waiting to happen.
send(socket, buffer, buffer_size, 0) - you did not set buffer_size to the number of bytes copied into buffer by strcpy(). It looks like you are sending the entire buffer without regard to how many bytes it is actually holding.
sizeof(s) does not give you the number of characters in a std::string. It gives you the byte size of the std::string class itself, which is not the same thing. You need to use the string's size() or length() method to get the proper number of characters.
TCP is a byte stream, there is no 1-to-1 relationship between send() and recv(). Both can report fewer bytes were processed than requested, but you are not accounting for that properly.
You are completely ignoring the return value of send(), assuming that all requested bytes have been sent in full on each call, when in actuality fewer bytes may have been sent, or none at all. On the 1st call, you are not accounting for the possibility that the entire size string may not be sent in 1 call and may need to be looped. Inside of the loop, you are completely ignoring the return value of send() for purposes of looping.
Likewise, you are ignoring the return value of recv() on the 1st call, assuming the entire string size is received in 1 call, without regard to how many characters are actually in the string size. Inside the loop, you are paying attention to the return value of recv() for purposes of looping, except that you are not accounting for the possibility that recv() returns 0 on a graceful disconnect. You are not breaking your loop if that happens.
It is good that you want to send the string's size before sending its content. However, you are sending the size as another variable-length string, which really defeats the purpose of sending a size at all. You should send the size using a fixed-length integer in binary form instead.
Try something more like this instead:
void send_raw(int socket, const void *buffer, size_t buffer_size)
{
const char *ptr = (const char*) buffer;
while (buffer_size > 0) {
int sent = send(socket, ptr, buffer_size, 0);
if (sent < 0) {
// throw exception
}
ptr += sent;
buffer_size -= sent;
}
}
void send_u32(int socket, uint32_t value)
{
value = htonl(value);
send_raw(socket, &value, sizeof(value));
}
...
// Send byte count to client so it knows how much data to expect
send_u32(socket, s.size());
// Send actual data
send_raw(socket, s.c_str(), s.size());
void recv_raw(int socket, void *buffer, size_t buffer_size)
{
char *ptr = (char*) buffer;
while (buffer_size > 0) {
int recvd = recv(socket, ptr, buffer_size, 0);
if (recvd <= 0) {
// throw exception
}
ptr += recvd;
buffer_size -= recvd;
}
}
uint32_t recv_u32(int socket)
{
uint32_t value;
recv_raw(socket, &value, sizeof(value));
return ntohl(value);
}
...
size_t count = recv_u32(socket);
return_string.resize(count);
recv_raw(socket, &return_string[0], count);
However, if you really want to stick with sending the string size as another string, you need to delimit it, such as sending its null terminator, eg:
// Send byte count to client so it knows how much data to expect
std::string size_buffer = std::to_string(s.size());
send_raw(socket, size_buffer.c_str(), size_buffer.size()+1);
// Send actual data
send_raw(socket, s.c_str(), s.size());
std::string size_buffer;
char ch;
do {
recv_raw(socket, &ch, 1);
if (ch == '\0') break;
size_buffer += ch;
}
while (true);
size_t count = std::stoi(size_buffer);
return_string.resize(count);
recv_raw(socket, &return_string[0], count);
You can add MSG_WAITALL to the recv() flags and do only one read.
I am writing a simple game named "TicTacToe". I also wanted to create a network function using WinSock. While connection between two users goes without errors, I can't receive any correct data. After receiving it, the buffer is filled with a strange characters like "☺$0/A", etc., while it should receive "Hello!".
Here's my code:
const char buf[] = "Hello!";
char rbuf[16];
int bytes = 0;
memset(buf, 0, sizeof(buf));
// sending
send(sox, buf, sizeof(buf), 0);
// recv
bytes = recv(sox, rbuf, 16, 0)
rbuf[bytes] = '\0';
cout << rbuf << endl;
Well, for one, you're not checking the return values of your send/recv calls, also you can't expect it to always receive 16 bytes. In order to ensure you get what you want to receive, you might have to call recv multiple times. Here's an example solution to that problem:
int FixedRecv(SOCKET sox, char* rbuf, int length)
{
int ref, len = 0;
do {
ref = recv(sox, rbuf + len, length - len, 0);
if(ref == 0) return 0;
if(SOCKET_ERROR != ref)
len += ref;
else return SOCKET_ERROR;
} while(len < length);
return len;
}
Same will also apply to your calls to send but I just want you to get a general idea.
Hope that helps, uses a little bit of pointer arithmetic, but nothing
too extensive.
Also, if you're using nonblocking sockets, you will want to check for WSAEWOULDBLOCK with WSAGetLastError.
I got array of lengthy strings. I have to send them as single concatenated string in a send (int __fd, const void *__buf, size_t __n, int __flags) call. I am afraid its a CPU consuming process to construct the single concatenated string(char*). Is there a facility to send array of strings tail to head?
I don't want to call send more than once for single meaningful string as long as recv fires more than once at the receiving end.
I wonder why there ain't a standardized extendable string structure like linked list in C/C++ so that readers can jump on to next buffer at the end of a buffer. I wish atleast std::string implement this.
You don't need to concatenate all strings in one go. This won't be very CPU consuming since it will happen underneath anyways, but it may or may not consume a lot of memory.
If you are using flags in send then you should determine the socket buffer size. Concatenate your strings up to that buffer size and then send them one buffer at a time
void send_strings(int sockfd, char ** strings, size_t numstrings, int flags) {
// get the socket write buffer size
int buflen;
unsigned int m = sizeof(bufsize);
if(getsockopt(sockfd,SOL_SOCKET,SO_SNDBUF,(void *)&buflen, &m)) {
perror("getsockopt"); return; }
char buffer[buflen];
int bufsize = 0;
while (numstrings--) {
char * string = *(strings++);
size_t length = strlen(string);
// if the string would exceed the buffer
while (length > buflen - bufsize) {
memcpy(buffer + bufsize, string, buflen - bufsize);
length -= buflen - bufsize;
string += buflen - bufsize;
// send a full buffer
send(sockfd, buffer, buflen, flags);
bufsize = 0;
}
// copy the string into the buffer
memcpy(buffer + bufsize, string, length);
bufsize += length;
}
// send the rest
if (bufsize) {
send(sockfd, buffer, bufsize, flags);
}
}
http://linux.die.net/man/2/send states the use of MSG_MORE flag. When set, it will initiate transmission only after the send call without MSG_MORE flag. So call send for each data chunk with MSG_MORE except for the last data chunk.
int resp = recv(s, buf, len, flags);
if(resp == 18) {
char data[18];
strcpy(data, buf);
...
}
I expect strlen(data) to be equal 18, but it isn`t. What did I miss?
If your data contains a zero-byte \0, then strlen will only give you the length of the string up to the terminator. If data does not have a terminator, then strlen will continue searching through whatever memory it happens to be at. This is commonly used in buffer overflow attacks.
I think what Joe is trying to say is your code isn't bullet-proof, starting with the number bytes read and copying the data into the data array.
int resp = recv(s, buf, len, flags);
if(resp > 0)
{
// ! This code assumse that all the data will fit into 18 bytes.
char data[18];
memset(data, 0, sizeof(data));
// ! As Joe warned above, this code assumes there's a null terminating
// ! character in the buf you received.
strcpy(data, buf); // consider memcpy if binary data (i.e. not strings)
}