Copyright | (c) Austin Seipp 2014 |
---|---|
License | MIT |
Maintainer | aseipp@pobox.com |
Stability | unstable |
Portability | portable |
Safe Haskell | Trustworthy |
Language | Haskell2010 |
Noise pipes. Pipes encrypt meant for interactive communications like networking sockets, and are built on boxes.
This module exports a simple, high-level interface for communicating with pipes securely and easily. For a full tutorial, see Crypto.Noise.Tutorial.
- connect :: (MonadIO m, MonadCatch m) => HostName -> ServiceName -> PipeConfig -> ((Context, SockAddr) -> m r) -> m r
- serve :: MonadIO m => HostPreference -> ServiceName -> PipeConfig -> ((Context, SockAddr) -> IO ()) -> m ()
- listen :: (MonadIO m, MonadCatch m) => HostPreference -> ServiceName -> ((Socket, SockAddr) -> m r) -> m r
- accept :: (MonadIO m, MonadCatch m) => Socket -> PipeConfig -> ((Context, SockAddr) -> m r) -> m r
- acceptFork :: MonadIO m => Socket -> PipeConfig -> ((Context, SockAddr) -> IO ()) -> m ThreadId
- send :: MonadIO m => Context -> ByteString -> m ()
- recv :: MonadIO m => Context -> m (Maybe ByteString)
- data PipeMsg = PipeMsg ByteString Word32
- pipeClientStream :: PipeConfig -> (InputStream ByteString, OutputStream ByteString) -> IO (InputStream ByteString, OutputStream ByteString)
- pipeServerStream :: PipeConfig -> (InputStream ByteString, OutputStream ByteString) -> IO (InputStream ByteString, OutputStream ByteString)
- pipeContextStreams :: Context -> (InputStream ByteString, OutputStream ByteString)
- pipeContextSocket :: Context -> Socket
- closeContext :: Context -> IO ()
- type PipeMsgPadder = OutputStream PipeMsg -> IO (OutputStream ByteString)
- randomPadStream :: Word32 -> Word32 -> PipeMsgPadder
- constantPadStream :: Word32 -> PipeMsgPadder
- offerX509 :: FilePath -> IO ByteString
- validateX509 :: Maybe String -> ByteString -> IO Bool
- validateX509_ :: Maybe String -> Maybe ValidationCache -> Maybe ([FailedReason] -> IO Bool) -> ByteString -> IO Bool
- withSocketsDo :: IO a -> IO a
- data Context
- data PipeConfig = PipeConfig {}
- defaultPipeConfig :: PipeConfig
- data FailedReason
- = UnknownCriticalExtension
- | Expired
- | InFuture
- | SelfSigned
- | UnknownCA
- | NotAllowedToSign
- | NotAnAuthority
- | AuthorityTooDeep
- | NoCommonName
- | InvalidName String
- | NameMismatch String
- | InvalidWildcard
- | LeafKeyUsageNotAllowed
- | LeafKeyPurposeNotAllowed
- | LeafNotV3
- | EmptyChain
- | CacheSaysNo String
- | InvalidSignature SignatureFailure
- data HostPreference
- type HostName = String
- type ServiceName = String
- data Socket
- data SockAddr
- data IncorrectNoisePipeNegotiation
- data InvalidNoiseX509Certificate
Noise pipes
Noise pipes encrypt interactive channels like sockets. Noise pipes have some additional benefits in addition with the protections offered by boxes:
Full forward secrecy or full key erasure
- Compromise of any long-term private keys never compromises old pipes. But furthermore, compromise of an active endpoint in an ongoinng communication does not compromise prior ciphertexts either - a Noise pipe forgets its chain secrets upon every message.
Resistance to key-compromise impersonation
- Even with a compromised private key, the compromised party can still verify other parties' identities in a Noise pipe.
Efficient, encrypted handshakes with short roundtrip
- Handshakes allow clients and servers to communicate after only one round trip, offering room for validation checks or certificates.
TCP Socket Interface
A simple, no-nonsense TCP interface for Noise servers/clients. This forms the basis of the TCP interface. In the simplest case, both clients and servers are anonymous - identified not by long-term public keys (which have been exchanged), but only by short-term ephemeral keys. This makes the TCP layer as easy to use as any unencrypted socket in the default case.
Client interface
Here's an example of a Noise client:
connect
"www.example.org" "80"defaultPipeConfig
$ \(ctx, remoteAddr) -> do putStrLn $ "Connection established to " ++ show remoteAddr -- Now you can use connSock as you see fit withsend
andrecv
. -- The socket handle will automatically be closed when it goes out of scope.
:: (MonadIO m, MonadCatch m) | |
=> HostName | Server hostname |
-> ServiceName | Server port |
-> PipeConfig | Client configuration |
-> ((Context, SockAddr) -> m r) | Connection handler |
-> m r |
Connect to a Noise-secured TCP server and use the connection.
Any acquired network resources are properly closed and discarded when done or in case of exceptions.
Server interface
Here's an example of a Noise server:
serve
(Host
"127.0.0.1") "8000"defaultPipeConfig
$ \(ctx, remoteAddr) -> do putStrLn $ "Noise connection established from " ++ show remoteAddr -- Now you may usectx
as you please within this scope, -- possibly usingrecv
andsend
to interact with the remote end.
:: MonadIO m | |
=> HostPreference | Preferred host to bind on |
-> ServiceName | Service port |
-> PipeConfig | Server configuration |
-> ((Context, SockAddr) -> IO ()) | Connection handler |
-> m () |
Start a Noise-secured TCP server which accepts incoming connections and handles each of them concurrently in different threads.
Any acquired network resources are properly closed and discarded when done or in case of exceptions.
Internally, this function just performs
and
listen
.acceptFork
:: (MonadIO m, MonadCatch m) | |
=> HostPreference | Preferred host to bind |
-> ServiceName | Port identifier |
-> ((Socket, SockAddr) -> m r) | Handler for listening socket |
-> m r |
Bind a TCP listening socket and use it
The listening socket is closed when done or in case of exceptions.
Note:
is tipically 128, which is too small for
high performance servers. So, we use the maximum between
maxListenQueue
maxListenQueue
and 2048 as the default size of the listening
queue. The
and NoDelay
options are set on the
socket.ReuseAddr
:: (MonadIO m, MonadCatch m) | |
=> Socket | Listening and bound socket |
-> PipeConfig | Server configuration |
-> ((Context, SockAddr) -> m r) | Handler for incoming connection. |
-> m r |
Accept a single incoming connection and use it.
The connection socket is closed when done or in case of exceptions.
:: MonadIO m | |
=> Socket | Listening and bound socket |
-> PipeConfig | Server configuration |
-> ((Context, SockAddr) -> IO ()) | Handler for incoming connection. |
-> m ThreadId |
Accept a single incoming connection and use it in another thread.
The connection socket is closed when done or in case of exceptions.
Sending/receiving data
:: MonadIO m | |
=> Context | Connection context |
-> ByteString | Message |
-> m () |
Encrypts the given
, and writes the bytes to the
socket in a single message. The data can also be padded with random
data to obscure the plaintext length.ByteString
recv :: MonadIO m => Context -> m (Maybe ByteString)
Receives data over the connection, decrypts it and returns the
resulting
. This receives a single Noise message.ByteString
Returns
if the remote end closed the connection or
end-of-input was reached.Nothing
io-streams
interface
A simple io-streams
based interface for Noise pipes. Using these
APIs, you can turn any
or InputStream
into an
encrypted Noise channel. Additionally, you can take a socket
OutputStream
and obtain the underlying streams, to layer in other
transformations.Context
data PipeMsg
A message to send down a noise pipe - simply a message paired with an amount of random padding to add to the message.
pipeClientStream :: PipeConfig -> (InputStream ByteString, OutputStream ByteString) -> IO (InputStream ByteString, OutputStream ByteString)
Turn an
and InputStream
into encrypted noise
pipes for use by clients to talk to servers.OutputStream
pipeServerStream :: PipeConfig -> (InputStream ByteString, OutputStream ByteString) -> IO (InputStream ByteString, OutputStream ByteString)
Turn an
and InputStream
into encrypted noise
pipes for use by servers to talk to clients.OutputStream
pipeContextStreams :: Context -> (InputStream ByteString, OutputStream ByteString)
Pull out the underlying
and InputStream
from the given TCP OutputStream
. Any data written/read from these
streams will be encrypted/decrypted from the other side.Context
pipeContextSocket :: Context -> Socket
closeContext :: Context -> IO ()
Close the given
.Context
Utilities
type PipeMsgPadder = OutputStream PipeMsg -> IO (OutputStream ByteString)
Type of functions which pad pipe messages.
randomPadStream :: Word32 -> Word32 -> PipeMsgPadder
Given an
for a OutputStream
, create an
PipeMsg
that attaches a random number of bytes (within
the specified range) to every message sent down the pipe, to make plaintext
analysis more difficult.OutputStream
For example
let outStream' = randomPadStream
(0, 64) outStream
creates an
where every message has between 0 and 64 bytes
of padding, chosen randomly.OutputStream
constantPadStream :: Word32 -> PipeMsgPadder
Given an
for a OutputStream
, create an
PipeMsg
that attaches a constant number of random bytes
to every message sent down the pipe.OutputStream
For example
let outStream' = constantPadStream
0 outStream
creates an
where there is no padding for the messages
sent down the pipe.OutputStream
X509 certificate authentication support
Simple X509 support for pipe handshakes. The two functions
and offerX509
provide hooks you can use with
validateX509
to do X509 certificate validation as part of the
initial Noise handshake. In addition, the extra PipeConfig
function gives you more control over the client validation (to allow,
for example, self-signed certificates).validateX509_
This verification API can work for verifying server certificates to clients, vice versa, or both.
:: FilePath | Path to the server certificate chain to offer during the handshake, in PEM format. |
-> IO ByteString | Resulting encoded certificate chain. |
Offer up an X509 certificate chain as part of the initial
handshake. Given a
specifying a certificate, this will
cause the configured party to issue the certificate as part of its
initial message.FilePath
Note the other party MUST validate this response using
in their validateX509
setting.confInitialResponse
When this function cannot parse the certificate chain correctly
(for example, using an invalid file), it will throw an
.InvalidNoiseX509Certificate
:: Maybe String | Optional certificate FQDN to verify. |
-> ByteString | Input certificate |
-> IO Bool |
Simple X509 certificate chain validation for initial handshakes.
This is a simple, easy-to-use client API that allows you to validate server-offered certificates using the system certificate store. All it needs is the expected FQDN of the certificate.
If you need more control (for example, TOFU policies or validation
exceptions), please use
instead.validateX509_
Note the server MUST send the initial certificate using
in their offerX509
setting.confInitialMsg
When this function cannot validate the X509 certificate offered to
it, it will throw an
exception.InvalidNoiseX509Certificate
:: Maybe String | Optional certificate FQDN to verify. |
-> Maybe ValidationCache | Optional X509 validation cache. You may use this to enforce self-signing exceptions or TOFU ("Trust On First Use") validation. The default cache is
Note that fingerprints by default use SHA256, not SHA1. |
-> Maybe ([FailedReason] -> IO Bool) | Optional testing predicate. If for some reason you expect the validation to fail, you can use this to check specific results. You're encouraged to never touch this. If you need a self signed certificate, you
should use the X509
The default predicate is |
-> ByteString | Input certificate |
-> IO Bool |
X509 certificate validation. This is the 'extended' version of
the basic
API, allowing more control. Given
an optional hostname and the input certificate chain (encoded), as
well as control over the validation cache and a predicate, this
validates the certificate against the system store.x509ClientValidate
Note the server MUST send the initial certificate using
in their x509ServerMsg
setting.serverInitialMsg
When this function cannot validate the X509 certificate offered to
it, it will throw an
exception.InvalidNoiseX509Certificate
Windows support
withSocketsDo :: IO a -> IO a
On Windows operating systems, the networking subsystem has to be
initialised using withSocketsDo
before any networking operations can
be used. eg.
main = withSocketsDo $ do {...}
Although this is only strictly necessary on Windows platforms, it is harmless on other platforms, so for portability it is good practice to use it all the time.
Types
data Context
TCP connection context, containing internal connection streams.
data PipeConfig
Pipe configuration. This sets options for the long-term
connection and initial setup phase. Most people are probably fine
using
, although depending on your use case,
you may also want to customise the padder (probably using
defaultPipeConfig
).randomPadStream
Note that if you override the initial message/response handler, you must overwrite the dual option in the other parties' handler.
PipeConfig | |
|
defaultPipeConfig :: PipeConfig
The default
uses randomized initial padding
length, no server keypair (anonymous), no expected client key
(anonymous), and no padding for messages (using
PipeConfig
).constantPadStream
0
Re-exported from Data.X509.Validation
data FailedReason
Possible reason of certificate and chain failure
UnknownCriticalExtension | certificate contains an unknown critical extension |
Expired | validity ends before checking time |
InFuture | validity starts after checking time |
SelfSigned | certificate is self signed |
UnknownCA | unknown Certificate Authority (CA) |
NotAllowedToSign | certificate is not allowed to sign |
NotAnAuthority | not a CA |
AuthorityTooDeep | Violation of the optional Basic constraint's path length |
NoCommonName | Certificate doesn't have any common name (CN) |
InvalidName String | Invalid name in certificate |
NameMismatch String | connection name and certificate do not match |
InvalidWildcard | invalid wildcard in certificate |
LeafKeyUsageNotAllowed | the requested key usage is not compatible with the leaf certificate's key usage |
LeafKeyPurposeNotAllowed | the requested key purpose is not compatible with the leaf certificate's extended key usage |
LeafNotV3 | Only authorized an X509.V3 certificate as leaf certificate. |
EmptyChain | empty chain of certificate |
CacheSaysNo String | the cache explicitely denied this certificate |
InvalidSignature SignatureFailure | signature failed |
Re-exported from Network.Simple.TCP
data HostPreference
Preferred host to bind.
HostAny | Any available host. |
HostIPv4 | Any available IPv4 host. |
HostIPv6 | Any available IPv6 host. |
Host HostName | An explicit host name. |
Eq HostPreference | |
Ord HostPreference | |
Read HostPreference | |
Show HostPreference | |
IsString HostPreference | The following special values are recognized: |
Either a host name e.g., "haskell.org"
or a numeric host
address string consisting of a dotted decimal IPv4 address or an
IPv6 address e.g., "192.168.0.1"
.
type ServiceName = String
Exceptions
data IncorrectNoisePipeNegotiation
An exception which is thrown when the Noise pipe cannot be properly negotiated due to key failure or some other error.
data InvalidNoiseX509Certificate
Exception throw by clients when an invalid X509 certificate is offered. This may also be thrown by the server when it cannot decode the X509 certificate.