diff --git a/cmd/tools/vtest-self.v b/cmd/tools/vtest-self.v index 594a06ce30..ccc8abc111 100644 --- a/cmd/tools/vtest-self.v +++ b/cmd/tools/vtest-self.v @@ -80,6 +80,7 @@ const ( 'vlib/net/http/http_test.v', 'vlib/net/http/status_test.v', 'vlib/net/websocket/ws_test.v', + 'vlib/net/openssl/rsa_test.v', 'vlib/sqlite/sqlite_test.v', 'vlib/sqlite/sqlite_orm_test.v', 'vlib/orm/orm_test.v', @@ -118,6 +119,7 @@ const ( 'vlib/net/unix/unix_test.v', 'vlib/net/unix/use_net_and_net_unix_together_test.v', 'vlib/net/websocket/websocket_test.v', + 'vlib/net/openssl/rsa_test.v', 'vlib/vweb/tests/vweb_test.v', 'vlib/vweb/request_test.v', 'vlib/net/http/request_test.v', diff --git a/vlib/crypto/rsa/rsa.c.v b/vlib/crypto/rsa/rsa.c.v new file mode 100644 index 0000000000..bba632f322 --- /dev/null +++ b/vlib/crypto/rsa/rsa.c.v @@ -0,0 +1,75 @@ +module rsa + +import net.openssl + +pub struct RSAInstance { + pair &C.RSA +pub: + public_key []byte + private_key []byte +} + +pub fn gen_key_pair(len int, exp int) ?RSAInstance { + assert openssl.is_used == 1 + rsa := C.RSA_new() + bn := C.BN_new() + + C.BN_set_word(bn, exp) + + if C.RAND_status() != 1 { + return error('Not seeded') + } + + res := C.RSA_generate_key_ex(rsa, len, bn, voidptr(0)) + C.BN_free(bn) + + if res != 1 { + err := []byte{len: 256} + C.ERR_error_string(C.ERR_get_error(), err.data) + return error(err.bytestr()) + } + + private := C.BIO_new(C.BIO_s_mem()) + public := C.BIO_new(C.BIO_s_mem()) + + C.PEM_write_bio_RSAPrivateKey(private, rsa, voidptr(0), voidptr(0), 0, voidptr(0), + voidptr(0)) + C.PEM_write_bio_RSAPublicKey(public, rsa) + + private_len := C.BIO_pending(private) + public_len := C.BIO_pending(public) + + private_key := []byte{len: private_len} + public_key := []byte{len: public_len} + + C.BIO_read(private, private_key.data, private_len) + C.BIO_read(public, public_key.data, public_len) + + return RSAInstance{ + pair: rsa + public_key: public_key + private_key: private_key + } +} + +pub fn (rsa RSAInstance) encrypt(msg []byte) ?([]byte, int) { + res := []byte{len: C.RSA_size(rsa.pair)} + len := C.RSA_public_encrypt(msg.len, msg.data, res.data, rsa.pair, C.RSA_PKCS1_OAEP_PADDING) + if len == -1 { + err := []byte{len: 256} + C.ERR_error_string(C.ERR_get_error(), err.data) + return error(err.bytestr()) + } + return res, len +} + +pub fn (rsa RSAInstance) decrypt(len int, data []byte) ?[]byte { + res := []byte{len: len} + l := C.RSA_private_decrypt(len, data.data, res.data, rsa.pair, C.RSA_PKCS1_OAEP_PADDING) + if l == -1 { + err := []byte{len: 256} + C.ERR_error_string(C.ERR_get_error(), err.data) + return error(err.bytestr()) + } + return res[0..l] +} diff --git a/vlib/crypto/rsa/rsa.v b/vlib/crypto/rsa/rsa.v new file mode 100644 index 0000000000..6b021bb25a --- /dev/null +++ b/vlib/crypto/rsa/rsa.v @@ -0,0 +1 @@ +module rsa diff --git a/vlib/net/openssl/c.v b/vlib/net/openssl/c.v index 3effc4f9f3..a174f88669 100644 --- a/vlib/net/openssl/c.v +++ b/vlib/net/openssl/c.v @@ -29,6 +29,8 @@ $if $pkgconfig('openssl') { // #include # Please install OpenSSL development headers #include +#include +#include #include [typedef] @@ -47,6 +49,19 @@ pub struct SSL_METHOD { pub struct OPENSSL_INIT_SETTINGS { } +[typedef] +struct C.RSA { + engine voidptr + n &C.BIGNUM + e &C.BIGNUM + d &C.BIGNUM + p &C.BIGNUM + q &C.BIGNUM +} + +[typedef] +struct C.BIGNUM {} + fn C.BIO_new_ssl_connect(ctx &C.SSL_CTX) &C.BIO fn C.BIO_set_conn_hostname(b &C.BIO, name &char) int @@ -66,6 +81,12 @@ fn C.BIO_read(b &C.BIO, buf voidptr, len int) int fn C.BIO_free_all(a &C.BIO) +fn C.BIO_new(voidptr) &C.BIO + +fn C.BIO_s_mem() + +fn C.BIO_pending(&C.BIO) int + fn C.SSL_CTX_new(method &C.SSL_METHOD) &C.SSL_CTX fn C.SSL_CTX_set_options(ctx &C.SSL_CTX, options int) @@ -120,6 +141,31 @@ fn C.TLSv1_2_method() voidptr fn C.OPENSSL_init_ssl(opts u64, settings &OPENSSL_INIT_SETTINGS) int +// RSA +fn C.RSA_generate_key_ex(&C.RSA, int, &C.BIGNUM, voidptr) int +fn C.RSA_generate_key(int, u64, voidptr, voidptr) &C.RSA +fn C.RSA_new() &C.RSA +fn C.RSA_size(&C.RSA) int + +fn C.RSA_public_encrypt(int, voidptr, voidptr, &C.RSA, int) int +fn C.RSA_private_decrypt(int, voidptr, voidptr, &C.RSA, int) int + +fn C.BN_new() &C.BIGNUM +fn C.BN_free(&C.BIGNUM) +fn C.BN_set_word(&C.BIGNUM, int) + +fn C.ENGINE_set_default(voidptr, u32) + +fn C.RAND_seed(voidptr, int) + +fn C.RAND_status() int + +fn C.ERR_get_error() u64 +fn C.ERR_error_string(u64, charptr) charptr + +fn C.PEM_write_bio_RSAPrivateKey(&C.BIO, &C.RSA, voidptr, voidptr, int, voidptr, voidptr) +fn C.PEM_write_bio_RSAPublicKey(&C.BIO, &C.RSA) + fn init() { $if ssl_pre_1_1_version ? { // OPENSSL_VERSION_NUMBER < 0x10100000L diff --git a/vlib/net/openssl/rsa_test.v b/vlib/net/openssl/rsa_test.v new file mode 100644 index 0000000000..b4b8b3e67e --- /dev/null +++ b/vlib/net/openssl/rsa_test.v @@ -0,0 +1,30 @@ +import crypto.rsa + +/* +This test has to be moved here, because in the rsa there is an strange error. +OpenSSL should be a correct place aswell. +*/ + +fn test_rsa() { + instance := rsa.gen_key_pair(1024, 3) or { + eprintln(err) + assert false + return + } + + message := 'abc123456' + + encrypted, len := instance.encrypt(message.bytes()) or { + eprintln(err) + assert false + return + } + + decrypted := instance.decrypt(len, encrypted) or { + eprintln(err) + assert false + return + } + + assert message == decrypted.bytestr() +}