diff --git a/src/OpenSSL.jl b/src/OpenSSL.jl index abdfa0f..4138311 100644 --- a/src/OpenSSL.jl +++ b/src/OpenSSL.jl @@ -24,12 +24,12 @@ Error handling: """ export TLSv12ClientMethod, TLSv12ServerMethod, - SSLStream, BigNum, EvpPKey, RSA, DSA, Asn1Time, X509Name, StackOf, X509Certificate, + SSLStream, BigNum, EvpPKey, RSA, DSA, EC, ECBuiltinCurve, Asn1Time, X509Name, StackOf, X509Certificate, X509Request, X509Store, X509Attribute, X509Extension, P12Object, EvpDigestContext, EvpCipherContext, EvpEncNull, EvpBlowFishCBC, EvpBlowFishECB, EvpBlowFishCFB, EvpBlowFishOFB, EvpAES128CBC, EvpAES128ECB, EvpAES128CFB, EvpAES128OFB, EvpMDNull, EvpMD2, EvpMD5, EvpSHA1, EvpDSS1, encrypt_init, cipher, add_extension, add_extensions, decrypt_init, digest_init, digest_update, digest_final, - digest, random_bytes, rsa_generate_key, dsa_generate_key, add_entry, sign_certificate, sign_request, adjust, + digest, random_bytes, rsa_generate_key, dsa_generate_key, ec_generate_key, ec_builtin_curves, add_entry, sign_certificate, sign_request, adjust, add_cert, unpack, eof, isreadable, iswritable, bytesavailable, read, unsafe_write, connect, get_peer_certificate, free, HTTP2_ALPN, UPDATE_HTTP2_ALPN, version @@ -1400,6 +1400,124 @@ function dsa_generate_key(; bits::Int32=Int32(1024))::DSA return dsa end + + +""" + EC structure. +""" +mutable struct EC + ec::Ptr{Cvoid} + + function EC() + ec = ccall( + (:EC_KEY_new, libcrypto), + Ptr{Cvoid}, + ()) + if ec == C_NULL + throw(OpenSSLError()) + end + + ec = new(ec) + finalizer(free, ec) + + return ec + end + + function EC(p::Ptr{Cvoid}) + ec = new(p) + finalizer(free, ec) + return ec + end +end + +function free(ec::EC) + ccall( + (:EC_KEY_free, libcrypto), + Cvoid, + (EC,), + ec) + + ec.ec = C_NULL + return nothing +end + +""" + Generate EC key pair. +""" +function ec_generate_key(nid::Int32)::EC + ecptr = ccall( + (:EC_KEY_new_by_curve_name, libcrypto), + Ptr{Cvoid}, + (Cint,), nid) + if ecptr == C_NULL + throw(OpenSSLError()) + end + return EC(ecptr) +end + +function ec_generate_key(curvename::AbstractString)::EC + found = filter(x -> x.name == curvename, ec_builtin_curves()) + if length(found) == 0 + error("No built in curve with name '$curvename' exists") + end + nid = first(found).id + return ec_generate_key(nid) +end + + +struct ECBuiltinCurve + id::Int32 + name::String + comment::String +end + +struct _EC_builtin_curve + nid::Cint + comment::Ptr{Cchar} +end + +""" + Get all the builtin curves. Use id or name when calling ec_generate_key. +""" +function ec_builtin_curves() + curves = Vector{ECBuiltinCurve}() + ncurves = ccall( + (:EC_get_builtin_curves, libcrypto), + Csize_t, + (Ptr{Cvoid}, Csize_t), + C_NULL, 0) + + builtin_curve_ids = Vector{_EC_builtin_curve}(undef, ncurves) + + GC.@preserve builtin_curve_ids ncurves begin + if ccall( + (:EC_get_builtin_curves, libcrypto), + Csize_t, + (Ptr{Cvoid}, Csize_t), + pointer(builtin_curve_ids), + ncurves) != ncurves + throw(OpenSSLError()) + end + for ecbc in builtin_curve_ids + name = "" + nid = convert(Int32, ecbc.nid) + comment = String(unsafe_string(ecbc.comment)) + namecptr = ccall( + (:OSSL_EC_curve_nid2name, libcrypto), + Ptr{Cchar}, + (Cint,), + ecbc.nid) + if namecptr != C_NULL + name = String(unsafe_string(namecptr)) + end + push!(curves, ECBuiltinCurve(nid, name, comment)) + end + end + return curves +end + + + """ OpenSSL BIOMethod. """ @@ -1809,6 +1927,22 @@ mutable struct EvpPKey return evp_pkey end + function EvpPKey(ec::EC)::EvpPKey + evp_pkey = EvpPKey() + + if ccall( + (:EVP_PKEY_assign, libcrypto), + Cint, (EvpPKey, Cint, EC), + evp_pkey, + EVP_PKEY_EC, + ec) != 1 + throw(OpenSSLError()) + end + + ec.ec = C_NULL + return evp_pkey + end + """ Creates a EvpPKey from PEM string. """ diff --git a/test/runtests.jl b/test/runtests.jl index 707a89f..11fe037 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -187,7 +187,7 @@ end x509_server_cert = OpenSSL.get_peer_certificate(ssl) - @test String(x509_server_cert.issuer_name) == "/C=US/O=Let's Encrypt/CN=R3" + @test String(x509_server_cert.issuer_name) == "/C=US/O=Let's Encrypt/CN=R11" @test String(x509_server_cert.subject_name) == "/CN=httpbingo.julialang.org" request_str = "GET /status/200 HTTP/1.1\r\nHost: httpbingo.julialang.org\r\nUser-Agent: curl\r\nAccept: */*\r\n\r\n" @@ -564,6 +564,20 @@ end dsa = dsa_generate_key() end +@testset "EC" begin + curves = ec_builtin_curves() + if length(curves) > 0 + builtincurve = first(curves) + eckey = EvpPKey(ec_generate_key(builtincurve.id)) + @test eckey.key_type == OpenSSL.EVP_PKEY_EC + free(eckey) + + eckey = EvpPKey(ec_generate_key(builtincurve.name)) + @test eckey.key_type == OpenSSL.EVP_PKEY_EC + free(eckey) + end +end + @testset "X509Attribute" begin attr = X509Attribute() free(attr)