microsoft / SEAL

Microsoft SEAL is an easy-to-use and powerful homomorphic encryption library.
https://www.microsoft.com/en-us/research/group/cryptography-research/
MIT License
3.6k stars 709 forks source link

Can SEALContext object transmit by socket? #610

Open hui09-m opened 1 year ago

hui09-m commented 1 year ago

Can SEALContext object transmit by socket? I want to use socket to imitate the comminucations between server and clients. Then I write code to acchieve the fundation. But some errors occured.

image

If I write parameters for SEALContext object in Client, it can print corectly. However, I can not suitable for the parameters from Server. I will show you the code. server: `#include

pragma comment(lib,"ws2_32.lib")

using namespace std; using namespace seal; const int PORT = 8000;

define MaxClient 10

define MaxBufSize 4096

define _CRT_SECURE_NO_WARINGS

//服务线程 DWORD WINAPI ServerThread(LPVOID lpParameter) { SOCKET ClientSocket = (SOCKET)lpParameter; int receByt = 0; // char RecvBuf[MaxBufSize]; char RecvBuf[1]; char SendBuf[MaxBufSize]; while (1) { //数据接收 receByt = recv(ClientSocket, RecvBuf, sizeof(RecvBuf), 0); cout << "receByt = " << receByt << endl; string str_buff = RecvBuf; cout << "str_buff = " << str_buff << endl; if (receByt > 0) { cout << "接收到的消息是:" << RecvBuf << " 来自客户端:" << *ClientSocket << endl; if (str_buff == "1") { cout << "If you are ready for FHE servive,please write 1" << endl; } else cout << "Service wronged!" << endl; } else { cout << "接收消息结束!" << endl; break; } memset(RecvBuf, 0, sizeof(RecvBuf));

    //数据处理

    //数据发送
    fgets(SendBuf, MaxBufSize, stdin);
    EncryptionParameters parms(scheme_type::bfv);
    size_t poly_modulus_degree = 8192;
    parms.set_poly_modulus_degree(poly_modulus_degree);
    parms.set_coeff_modulus(CoeffModulus::BFVDefault(poly_modulus_degree));
    parms.set_plain_modulus(PlainModulus::Batching(poly_modulus_degree, 20));
    SEALContext context(parms);
    print_parameters(context);
    int k = 0;
    //k = send(*ClientSocket, (char*)&parms, sizeof(EncryptionParameters), 0);
    k = send(*ClientSocket, (char*)&context, sizeof(SEALContext), 0);

    if (k < 0) {
        cout << "发送失败" << endl;  
    }

    /* //source
    cout << "请输入要发送到客户端的信息:" << endl;
    fgets(SendBuf, MaxBufSize, stdin);
    int k = 0;
    k = send(*ClientSocket, SendBuf, sizeof(SendBuf), 0);
    if (k < 0) {
        cout << "发送失败" << endl;
    }
    memset(SendBuf, 0, sizeof(SendBuf));

/ } closesocket(ClientSocket); free(ClientSocket); return 0; }

int main() { WSAData wsd; WSAStartup(MAKEWORD(2, 2), &wsd); SOCKET ListenSocket = socket(AF_INET, SOCK_STREAM, 0); SOCKADDR_IN ListenAddr; ListenAddr.sin_family = AF_INET; ListenAddr.sin_addr.S_un.S_addr = INADDR_ANY;//表示填入本机ip ListenAddr.sin_port = htons(PORT); int n; //n = bind(ListenSocket, (LPSOCKADDR)&ListenAddr, sizeof(ListenAddr)); n = ::bind(ListenSocket, (LPSOCKADDR)&ListenAddr, sizeof(ListenAddr));

if (n == SOCKET_ERROR) {
    cout << "端口绑定失败!" << endl;
    return -1;
}
else {
    cout << "端口绑定成功:" << PORT << endl;
}
int l = listen(ListenSocket, 20);
cout << "服务端准备就绪,等待连接请求" << endl;

while (1) {
    //循环接收客户端连接请求并创建服务线程
    SOCKET* ClientSocket = new SOCKET;
    ClientSocket = (SOCKET*)malloc(sizeof(SOCKET));
    //接收客户端连接请求
    int SockAddrlen = sizeof(sockaddr);
    *ClientSocket = accept(ListenSocket, 0, 0);
    cout << "一个客户端已连接到服务器,socket是:" << *ClientSocket << endl;
    CreateThread(NULL, 0, &ServerThread, ClientSocket, 0, NULL);
}//while
closesocket(ListenSocket);
WSACleanup();
return(0);

}//main`

client: `//#include "Client.h"

include "client.h"

pragma comment(lib,"ws2_32.lib")

pragma warning(disable:4996)

using namespace std; using namespace seal; const int PORT = 8000;

define MaxBufSize 4096

define _CRT_SECURE_NO_WARINGS

int main() { WSADATA wsd; WSAStartup(MAKEWORD(2, 2), &wsd); //* SOCKET SocketClient = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); SOCKET SocketClient = socket(AF_INET, SOCK_STREAM, 0);

SOCKADDR_IN  ClientAddr;

ClientAddr.sin_family = AF_INET;
ClientAddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
ClientAddr.sin_port = htons(PORT);
int n = 0;
n = connect(SocketClient, (struct sockaddr*)&ClientAddr, sizeof(ClientAddr));
if (n == SOCKET_ERROR) {
    cout << "连接失败" << endl;
    return -1;
}
cout << "已经连接到服务器,可以向服务器发送消息了!" << endl;
char info[1024], SendBuff[MaxBufSize], RecvBuff[MaxBufSize];
while (1) {

//* cout << "请输入要发送的信息,按回车结束发送:" << endl; //数据处理

    cout << "If you are ready for FHE ,please write 1" << endl;
    //数据处理结束,准备发送数据

    fgets(info, 1024, stdin);
    //*     gets(info);     //error,change to the former
    if (info[0] == '\0')
        break;      
    strcpy(SendBuff, info);
    memset(info, 0, sizeof(info));
    int k = 0;
    k = send(SocketClient, SendBuff, sizeof(SendBuff), 0);
    memset(SendBuff, 0, sizeof(SendBuff));
    if (k < 0) {
        cout << "发送失败" << endl;
    }
    Sleep(500);

    //数据接收

    EncryptionParameters parms(scheme_type::bfv);
    int m = 0;
    //n = recv(SocketClient, (char*)&parms, sizeof(EncryptionParameters), 0);
    /*
    size_t poly_modulus_degree = 8192;
    parms.set_poly_modulus_degree(poly_modulus_degree);
    parms.set_coeff_modulus(CoeffModulus::BFVDefault(poly_modulus_degree));
    parms.set_plain_modulus(PlainModulus::Batching(poly_modulus_degree, 20));
    m = 1;

*/

    SEALContext context(parms);
    m = recv(SocketClient, (char*)&context, sizeof(SEALContext), 0);
    cout << "m = " << m << endl;
    if (m > 0) {
        cout << "Having got the paramerters" << endl;
        //SEALContext context(parms);
        print_parameters(context);  //error,异常,读取访问权限冲突
        cout << endl;

    }

    memset(RecvBuff, 0, sizeof(RecvBuff));

/*//*   
    int n = 0;
    n = recv(SocketClient, RecvBuff, sizeof(RecvBuff), 0);
    if (n > 0) {
        cout << "接收到来自服务器的消息为:" << RecvBuff << endl;
    }
    memset(RecvBuff, 0, sizeof(RecvBuff));
*/  
}
closesocket(SocketClient);
WSACleanup();
return 0;

} `

fionser commented 1 year ago

SEALContext is not serializable. Just exchange EncryptionParmeters and then construct the context.

hui09-m commented 1 year ago
  • It seems you have misunderstood what sizeof(EncryptionParameters) means.
  • Instead using std::stringstream to de/serialize
    std::stringstream ss;
    parms.save(ss); 
    std::string parms_to_send  = ss.str();

Then on the other side, first recv a string and use std::stringstream to de-serialize

EncryptionParmeters parms;
parms.load(a_string);

SEALContext is not serializable. Just exchange EncryptionParmeters and then construct the context.

Well, I tried this method, I can get string data, however, parms.load can't load it and throw exception. For function load() just can support stream datatype, I convert string to stringstream.

image

For thinking about it, for get the parms, may use parms.save() and parms.load() to do this? When I use simply parms.save(ss) and parms.load(ss), it not work and throw logic_error. So I use "parms.save(reinterpret_cast<seal_byte*>(byte_buffer.data()), byte_buffer.size()); ", it works. However, when constract SEALContext, it throw std::invalid_argument error. I have no idea about it. Can you give me some advice?

image image image
fionser commented 1 year ago

The following code work fine for me

void test() {
  seal::EncryptionParameters parms(seal::scheme_type::ckks);
  size_t N = 8192;
  std::vector<int> modulus_bits = {51, 55, 55};
  auto modulus = seal::CoeffModulus::Create(N, modulus_bits);
  parms.set_poly_modulus_degree(N);
  parms.set_coeff_modulus(modulus);
  parms.set_random_generator(
      seal::UniformRandomGeneratorFactory::DefaultFactory());

  seal::SEALContext conetxt(parms);

  std::ostringstream outs;
  parms.save(outs);
  std::string parms_str = outs.str();
  printf("parms bytes %zd\n", parms_str.length());

  // Simulate: send string through socket
  std::vector<unsigned char> recv_bytes(parms_str.length());
  std::copy_n(parms_str.c_str(), recv_bytes.size(), recv_bytes.data());
  // wrap the bits as string
  std::string recv_string(recv_bytes.data(), recv_bytes.data() + recv_bytes.size());

  std::istringstream ins(recv_string);
  seal::EncryptionParameters other_parms;
  other_parms.load(ins);
  seal::SEALContext other_context(other_parms);
  printf("Is SEAL ok = %d\n", other_context.parameters_set());
}
hui09-m commented 1 year ago

The following code work fine for me

void test() {
  seal::EncryptionParameters parms(seal::scheme_type::ckks);
  size_t N = 8192;
  std::vector<int> modulus_bits = {51, 55, 55};
  auto modulus = seal::CoeffModulus::Create(N, modulus_bits);
  parms.set_poly_modulus_degree(N);
  parms.set_coeff_modulus(modulus);
  parms.set_random_generator(
      seal::UniformRandomGeneratorFactory::DefaultFactory());

  seal::SEALContext conetxt(parms);

  std::ostringstream outs;
  parms.save(outs);
  std::string parms_str = outs.str();
  printf("parms bytes %zd\n", parms_str.length());

  // Simulate: send string through socket
  std::vector<unsigned char> recv_bytes(parms_str.length());
  std::copy_n(parms_str.c_str(), recv_bytes.size(), recv_bytes.data());
  // wrap the bits as string
  std::string recv_string(recv_bytes.data(), recv_bytes.data() + recv_bytes.size());

  std::istringstream ins(recv_string);
  seal::EncryptionParameters other_parms;
  other_parms.load(ins);
  seal::SEALContext other_context(other_parms);
  printf("Is SEAL ok = %d\n", other_context.parameters_set());
}

It seems you don't use socket to transmit data, you just use stringstream to do. Socket use send() and recv() to communicate. The fomat like: send( In SOCKET s, _In_readsbytes(len) const char FAR buf, In int len, In int flags ); recv( In SOCKET s, _Out_writes_bytesto(len, return) __out_data_source(NETWORK) char FAR buf, In int len, In int flags ); Always it make client and server be two projects like following picture (project Clients and Server_for_multi_clients), you should define apart.

image
fionser commented 1 year ago

There is no difference between copy_n and send/recv pair. To make sure you are using the right length (but not just a MaxBufSize) and make sure your have initialized your byte vector properly.

hui09-m commented 1 year ago

There is no difference between copy_n and send/recv pair. To make sure you are using the right length (but not just a MaxBufSize) and make sure your have initialized your byte vector properly.

I have try what you said. For me, I set client project and service project apart, when client.cpp use the following data to recv parms that works. std::vector recv_bytes(parms_str.length()); Thanks for answering me!!!