=begin pod

=head1 NAME

OpenSSL - OpenSSL bindings

=head1 SYNOPSIS

=begin code :lang<raku>
use OpenSSL;

my $conn = IO::Socket::INET.new: :host<raku.org>, :port(443);

my $openssl = OpenSSL.new(:client);
$openssl.set-fd($conn.native-descriptor);

$openssl.connect;

$openssl.write("HEAD / HTTP/1.1\r\nHost: raku.org\r\n\r\n");
say $openssl.read(1024);
$openssl.close;
=end code

=head1 DESCRIPTION

A module which provides OpenSSL bindings, making us able to set up a
TLS/SSL connection.

=head1 METHODS

=head2 method new

=begin code :lang<raku>
method new(Bool :$client = False, ProtocolVersion :$version = -1)
=end code

A constructor. Initializes OpenSSL library, sets method and context.
If C<$version> is not specified, the highest possible version is negotiated.

For client connections, construct the object with C<:client> or call
C<.set-connect-state> before C<.connect>.

=head2 method set-fd

=begin code :lang<raku>
method set-fd(OpenSSL:, int32 $fd)
=end code

Assigns connection's file descriptor (file handle) C<$fd> to the SSL object.

This must be done before calling C<.connect> or C<.accept>.

=head2 method set-connect-state

=begin code :lang<raku>
method set-connect-state(OpenSSL:)
=end code

Sets SSL object to connect (client) state.

Use it when you want to connect to SSL servers.

If the object was not constructed with C<:client>, call this before
C<.connect>.

=head2 method set-accept-state

=begin code :lang<raku>
method set-accept-state(OpenSSL:)
=end code

Sets SSL object to accept (server) state.

Use it when you want to provide an SSL server.

=head2 method connect

=begin code :lang<raku>
method connect(OpenSSL:)
=end code

Connects to the server using C<$fd> (passed using C<.set-fd>).

Does all the SSL stuff like handshaking.

For client connections, construct with C<:client> or call
C<.set-connect-state> first.

=head2 method accept

=begin code :lang<raku>
method accept(OpenSSL:)
=end code

Accepts new client connection.

Does all the SSL stuff like handshaking.

=head2 method write

=begin code :lang<raku>
method write(OpenSSL:, Str $s)
=end code

Sends C<$s> to the other side (server/client).

=head2 method read

=begin code :lang<raku>
method read(OpenSSL:, Int $n, Bool :$bin)
=end code

Reads C<$n> bytes from the other side (server/client).

Use C<:$bin> if you want it to return C<Buf> instead of C<Str>.

=head2 method use-certificate-file

=begin code :lang<raku>
method use-certificate-file(OpenSSL:, Str $file)
=end code

Assigns a certificate (from file) to the SSL object.

=head2 method use-privatekey-file
=begin code :lang<raku>
method use-privatekey-file(OpenSSL:, Str $file)

=end code

Assigns a private key (from file) to the SSL object.

=head2 method check-private-key

=begin code :lang<raku>
method check-private-key(OpenSSL:)
=end code

Checks if private key is valid.

=head2 method version-code

=begin code :lang<raku>
method version-code(--> Int)
=end code

Returns the negotiated protocol version as an OpenSSL numeric constant.

This method is useful after a successful C<.connect> or C<.accept>.

=head2 method version-name

=begin code :lang<raku>
method version-name(--> Str)
=end code

Returns the negotiated protocol version as a string, for example
C<TLSv1.2> or C<TLSv1.3>.

This method is useful after a successful C<.connect> or C<.accept>.

=head2 method protocol-version

=begin code :lang<raku>
method protocol-version(--> ProtocolVersion)
=end code

Returns the negotiated protocol version normalized to C<ProtocolVersion>.

Returns one of the values accepted by C<ProtocolVersion>, including C<1.3>,
or C<-1> if the negotiated version is unknown.

This method is useful after a successful C<.connect> or C<.accept>.

=head2 method set-sni-host-name

=begin code :lang<raku>
method set-sni-host-name(Str $host --> OpenSSL)
=end code

Sets the TLS SNI (Server Name Indication) host name.

For client connections this should usually be called before C<.connect>,
especially when connecting to virtual hosts or HTTPS servers by name.

=head2 method set-alpn-protocols

=begin code :lang<raku>
method set-alpn-protocols(*@protos --> OpenSSL)
=end code

Sets the ALPN protocols offered by the client, for example
C<h2> and C<http/1.1>.

This must be called before C<.connect>.

Each protocol must be non-empty and must not exceed 255 bytes.

=head2 method selected-alpn

=begin code :lang<raku>
method selected-alpn(--> Str)
=end code

Returns the negotiated ALPN protocol as a string, for example C<h2> or
C<http/1.1>.

Returns C<Nil> if no ALPN protocol was selected.

This method is useful after a successful C<.connect> or C<.accept>.

=head2 method shutdown

=begin code :lang<raku>
method shutdown(OpenSSL:)
=end code

Turns off the connection.

=head2 method ctx-free

=begin code :lang<raku>
method ctx-free(OpenSSL:)
=end code

Frees C's SSL_CTX struct.

=head2 method ssl-free

=begin code :lang<raku>
method ssl-free(OpenSSL:)
=end code

Frees C's SSL struct.

=head2 method close

=begin code :lang<raku>
method close(OpenSSL:)
=end code

Closes the connection.

Unlike C<.shutdown>, this also calls C<.ssl-free> and C<.ctx-free>.

=head1 TOOLS

Public key signing tools.

=head2 OpenSSL::RSATools

=begin code :lang<raku>
use OpenSSL::RSATools;

my $pem = slurp 'key.pem';
my $rsa = OpenSSL::RSAKey.new(private-pem => $pem);
my $data = 'as df jk l';
my $signature = $rsa.sign($data.encode);
my $rsa = OpenSSL::RSAKey.new(public-pem => $public);
if $rsa.verify($data.encode, $signature) { ... }
=end code

=head2 OpenSSL::CryptTools

Symmetric encryption tools (currently only AES256/192/128 encrypt/decrypt)

=begin code :lang<raku>
use OpenSSL::CryptTools;

my $ciphertext = encrypt("asdf".encode,
                         :aes256,
                         :iv(("0" x 16).encode),
                         :key(('x' x 32).encode));
my $plaintext = decrypt($ciphertext,
                        :aes256,
                        :iv(("0" x 16).encode),
                        :key(('x' x 32).encode));
=end code

=head2 OpenSSL::Digest

=begin code :lang<raku>
use OpenSSL::Digest;

my Blob $digest = md5("filename".IO);    # IO::Path object
my Blob $digest = md5(Blob.new(1,2,3));  # Blob object
my Blob $digest = md5("foo bar");        # coercible to string

say md5-hex("foo bar");  # 327b6f07435811239bc47e1544353273
=end code

Digest Functions exported as subroutines.  Takes either an C<IO::Path>
object of a path of which to create a digest, or a C<Blob> object, or
an object that can be coerced to a string.  A C<Blob> is always returned.

=item md5
=item sha1
=item sha224
=item sha256
=item sha384
=item sha512

These subroutines have hexified counterparts with the same name, but
postfixed with "-hex", which return a string (lowercase hexadecimal
characters) representation of the digest.

=item md5-hex
=item sha1-hex
=item sha224-hex
=item sha256-hex
=item sha384-hex
=item sha512-hex

=head2 OpenSSL::Digest::MD5

OO-Interface supporting incremental digesting

=begin code :lang<raku>
use OpenSSL::Digest::MD5;

my $md5 = OpenSSL::Digest::MD5.new; # Create fresh object
$md5.add('abc');                    # pass in Str or Blob
$md5.add('def');                    # Add some more data
my $digest = $md5.hash;             # Blob hash (and reset)
$md5.addfile('myfile');             # Read a file
my $hexdigest = $md5.hex;           # hex hash  (and reset)
=end code

=head1 CAVEATS

=head2 MacOS

Many native libraries on MacOS are installed with the C<brew> command
line interface.  For this module one would typically have to do a
C<brew install openssl>.

The use of native libraries is slightly more complicated on the MacOS
operating system than on other operating systems.  This generally means
that a symlink needs to be installed in a trusted filesystem location.
If the L<C<MacOS::NativeLib>|https://raku.land/zef:lizmat/MacOS::NativeLib>
distribution is installed, then these symlinks will be automatically
created when this module is built.

=head1 SEE ALSO

L<IO::Socket::SSL>

=head1 AUTHORS

=item Filip Sergot
=item Elizabeth Mattijsen

Source can be located at: https://github.com/raku-community-modules/OpenSSL .
Comments and Pull Requests are welcome.

=head1 COPYRIGHT AND LICENSE

Copyright 2014 - 2022 Filip Sergot

Copyright 2023 - 2026 The Raku Community

This library is free software; you can redistribute it and/or modify it under the MIT License.

=end pod

# vim: expandtab shiftwidth=4
