Skip to content

Commit

Permalink
Implement initial bindings
Browse files Browse the repository at this point in the history
  • Loading branch information
JordanMartinez committed Jun 15, 2023
0 parents commit 729af71
Show file tree
Hide file tree
Showing 12 changed files with 737 additions and 0 deletions.
62 changes: 62 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
name: CI

# Run CI when a PR is opened against the branch `master`
# and when one pushes a commit to `master`.
on:
push:
branches: [master]
pull_request:
branches: [master]

# Run CI on all 3 latest OSes
jobs:
build:
strategy:
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
runs-on: ${{ matrix.os }}

steps:
- uses: actions/checkout@v3

- name: Set up Node toolchain
uses: actions/setup-node@v3
with:
node-version: "18"

- name: Cache NPM dependencies
uses: actions/cache@v3
env:
cache-name: cache-node-modules
with:
path: ~/.npm
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package.json') }}
restore-keys: |
${{ runner.os }}-build-${{ env.cache-name }}-
${{ runner.os }}-build-
${{ runner.os }}-
- name: Setup PureScript tooling
run:
npm i -g purescript@latest purs-tidy@latest purescript-psa@latest spago@latest

- name: Cache PureScript dependencies
uses: actions/cache@v3
with:
key: ${{ runner.os }}-spago-${{ hashFiles('**/*.dhall') }}
path: |
.spago
output
# Compile the library/project
# censor-lib: ignore warnings emitted by dependencies
# strict: convert warnings into errors
# Note: `purs-args` actually forwards these args to `psa`
- name: Build the project
run: |
npx spago build --purs-args "--censor-lib --strict"
- name: Check Formatting
if: runner.os == 'Linux'
run: |
purs-tidy check src test
10 changes: 10 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/bower_components/
/node_modules/
/.pulp-cache/
/output/
/generated-docs/
/.psc-package/
/.psc*
/.purs*
/.psa*
/.spago
40 changes: 40 additions & 0 deletions packages.dhall
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
let upstream =
https://github.com/purescript/package-sets/releases/download/psc-0.15.8-20230615/packages.dhall
sha256:96d5db51eb6ce51906b52377d615fcdca3528ac05e0dc58e71ace8bbaceac108

in upstream
with node-event-emitter.version = "v2.0.0"
with node-event-emitter.dependencies =
[ "effect"
, "either"
, "functions"
, "prelude"
, "unsafe-coerce"
]
with node-streams.version = "eb31fe5d4040d5da4b4143c9f33d11a3ab1a9bdd"
with node-streams.dependencies =
[ "effect"
, "exceptions"
, "maybe"
, "node-buffer"
, "node-event-emitter"
, "nullable"
, "prelude"
, "unsafe-coerce"
]
with node-net.version = "4d408a3b5f1025f56f5c1e97cb6f833a8f04651b"
with node-net.dependencies =
[ "console"
, "datetime"
, "effect"
, "exceptions"
, "maybe"
, "node-buffer"
, "node-event-emitter"
, "node-fs"
, "node-streams"
, "nullable"
, "partial"
, "prelude"
, "unsafe-coerce"
]
21 changes: 21 additions & 0 deletions spago.dhall
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{ name = "my-project"
, dependencies =
[ "console"
, "effect"
, "either"
, "exceptions"
, "foreign"
, "maybe"
, "node-buffer"
, "node-event-emitter"
, "node-net"
, "node-streams"
, "nullable"
, "partial"
, "prelude"
, "unsafe-coerce"
]
, packages = ./packages.dhall
, sources = [ "src/**/*.purs" ]
, license = "MIT"
}
10 changes: 10 additions & 0 deletions src/Main.purs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
module Main where

import Prelude

import Effect (Effect)
import Effect.Console (log)

main :: Effect Unit
main = do
log "🍝"
14 changes: 14 additions & 0 deletions src/Node/TLS.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import tls from "node:tls";
export {
rootCertificates,
DEFAULT_ECDH_CURVE as defaultEcdhCurve,
DEFAULT_MAX_VERSION as defaultMinVersion,
DEFAULT_MIN_VERSION as defaultMaxVersion,
DEFAULT_CIPHERS as defaultCiphers,
} from "node:tls";

export const checkServerIdentityImpl = (hostname, cert) => tls.checkServerIdentity(hostname, cert);
export const createSecureContext = () => tls.createSecureContext();
export const createSecureContextOptionsImpl = (o) => tls.createSecureContext(o);
export const getCiphers = () => tls.getCiphers();

46 changes: 46 additions & 0 deletions src/Node/TLS.purs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
module Node.TLS
( checkServerIdentity
, createSecureContext
, createSecureContext'
, getCiphers
, rootCertificates
, defaultEcdhCurve
, defaultMinVersion
, defaultMaxVersion
) where

import Prelude

import Data.Maybe (Maybe)
import Data.Nullable (Nullable, toMaybe)
import Effect (Effect)
import Effect.Exception (Error)
import Effect.Uncurried (EffectFn1, EffectFn2, runEffectFn1, runEffectFn2)
import Foreign (Foreign)
import Node.TLS.Types (CreateSecureContextOptions, SecureContext)
import Prim.Row as Row

checkServerIdentity :: String -> Foreign -> Effect (Maybe Error)
checkServerIdentity hostname cert = map toMaybe $ runEffectFn2 checkServerIdentityImpl hostname cert

foreign import checkServerIdentityImpl :: EffectFn2 (String) (Foreign) (Nullable Error)

foreign import createSecureContext :: Effect (SecureContext)

createSecureContext'
:: forall r trash
. Row.Union r trash (CreateSecureContextOptions ())
=> { | r }
-> Effect SecureContext
createSecureContext' o = runEffectFn1 createSecureContextOptionsImpl o

foreign import createSecureContextOptionsImpl :: forall r. EffectFn1 ({ | r }) (SecureContext)

foreign import getCiphers :: Effect (Array String)

foreign import rootCertificates :: Array String

foreign import defaultEcdhCurve :: String
foreign import defaultMinVersion :: String
foreign import defaultMaxVersion :: String
foreign import defaultCiphers :: String
8 changes: 8 additions & 0 deletions src/Node/TLS/Server.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import tls from "node:tls";

export const createServer = () => tls.createServer();
export const createServerOptionsImpl = (o) => tls.createServer(o);
export const addContextImpl = (s, hostname, context) => s.addContext(hostname, context);
export const getTicketKeysImpl = (s) => s.getTicketKeys();
export const setSecureContextImpl = (s, o) => s.setSecureContext(o);
export const setTicketKeysImpl = (s, b) => s.setTicketKeys(b);
112 changes: 112 additions & 0 deletions src/Node/TLS/Server.purs
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
module Node.TLS.Server
( toNetServer
, createServer
, createServer'
, keylogHandle
, newSessionHandle
, ocspRequestHandle
, resumeSessionHandle
, secureConnectionHandle
, tlsClientErrorHandle
, addContext
, getTicketKeys
, setSecureContext
, setTicketKeys
) where

import Prelude

import Data.Either (Either(..))
import Data.Maybe (Maybe(..))
import Data.Nullable (Nullable, notNull, null)
import Effect (Effect)
import Effect.Exception (Error)
import Effect.Uncurried (EffectFn1, EffectFn2, EffectFn3, mkEffectFn1, mkEffectFn2, mkEffectFn3, runEffectFn1, runEffectFn2, runEffectFn3)
import Node.Buffer (Buffer)
import Node.EventEmitter (EventHandle(..))
import Node.EventEmitter.UtilTypes (EventHandle2, EventHandle3, EventHandle1)
import Node.Net.Types as NetTypes
import Node.TLS.Types (CreateSecureContextOptions, Server, TlsCreateServerOptions, TlsServer, TlsSocket)
import Prim.Row as Row
import Unsafe.Coerce (unsafeCoerce)

toNetServer :: TlsServer -> NetTypes.Server NetTypes.TCP
toNetServer = unsafeCoerce

foreign import createServer :: Effect (TlsServer)

createServer'
:: forall r trash
. Row.Union r trash (TlsCreateServerOptions Server (CreateSecureContextOptions (NetTypes.NewServerOptions ())))
=> { | r }
-> Effect (TlsServer)
createServer' r = runEffectFn1 createServerOptionsImpl r

foreign import createServerOptionsImpl :: forall r. EffectFn1 { | r } (TlsServer)

keylogHandle :: forall endpoint. EventHandle2 TlsServer Buffer (TlsSocket endpoint)
keylogHandle = EventHandle "keylog" \cb -> mkEffectFn2 \a b -> cb a b

newSessionHandle :: EventHandle3 TlsServer Buffer Buffer (Effect Unit)
newSessionHandle = EventHandle "newSession" \cb -> mkEffectFn3 \a b c -> cb a b c

ocspRequestHandle
:: EventHandle
TlsServer
(Buffer -> (Maybe (Either Error Buffer) -> Effect Unit) -> Effect Unit)
(EffectFn2 Buffer (EffectFn2 (Nullable Error) (Nullable Buffer) Unit) Unit)
ocspRequestHandle = EventHandle "ocspRequest" \cb ->
mkEffectFn2 \buff cb' ->
cb buff $ case _ of
Nothing -> runEffectFn2 cb' null null
Just x -> case x of
Left err -> runEffectFn2 cb' (notNull err) null
Right buff' -> runEffectFn2 cb' null (notNull buff')

resumeSessionHandle
:: EventHandle
TlsServer
(Buffer -> (Either Error Buffer -> Effect Unit) -> Effect Unit)
(EffectFn2 Buffer (EffectFn2 (Nullable Error) (Nullable Buffer) Unit) Unit)
resumeSessionHandle = EventHandle "resumeSession" \cb ->
mkEffectFn2 \buff cb' ->
cb buff $ case _ of
Left err -> runEffectFn2 cb' (notNull err) null
Right buff' -> runEffectFn2 cb' null (notNull buff')

secureConnectionHandle :: forall endpoint. EventHandle1 TlsServer (TlsSocket endpoint)
secureConnectionHandle = EventHandle "secureConnection" mkEffectFn1

tlsClientErrorHandle :: forall endpoint. EventHandle2 TlsServer Error (TlsSocket endpoint)
tlsClientErrorHandle = EventHandle "tlsClientError" \cb -> mkEffectFn2 \a b -> cb a b

addContext
:: forall r trash
. Row.Union r trash (CreateSecureContextOptions ())
=> TlsServer
-> String
-> { | r }
-> Effect Unit
addContext s hostname o = runEffectFn3 addContextImpl s hostname o

foreign import addContextImpl :: forall r. EffectFn3 (TlsServer) (String) ({ | r }) (Unit)

getTicketKeys :: TlsServer -> Effect Buffer
getTicketKeys s = runEffectFn1 getTicketKeysImpl s

foreign import getTicketKeysImpl :: EffectFn1 (TlsServer) (Buffer)

setSecureContext
:: forall r trash
. Row.Union r trash (CreateSecureContextOptions ())
=> TlsServer
-> { | r }
-> Effect Unit
setSecureContext s o = runEffectFn2 setSecureContextImpl s o

foreign import setSecureContextImpl :: forall r. EffectFn2 (TlsServer) ({ | r }) (Unit)

setTicketKeys :: TlsServer -> Buffer -> Effect Unit
setTicketKeys t b = runEffectFn2 setTicketKeysImpl t b

foreign import setTicketKeysImpl :: EffectFn2 (TlsServer) (Buffer) (Unit)
27 changes: 27 additions & 0 deletions src/Node/TLS/Socket.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import tls from "node:tls";

export const newTlsSocketImpl = (s) => new tls.TLSSocket(s);
export const newTlsSocketOptionsImpl = (s, o) => new tls.TLSSocket(s, o);
export const connectImpl = (o) => tls.connect(o);
export const authorizationErrorImpl = (s) => s.authorizationError;
export const authorizedImpl = (s) => s.authorized;
// export const disableRenegotiationImpl = (s) => s.disableRenegotiation();
export const enableTraceImpl = (s) => s.enableTrace();
export const encryptedImpl = (s) => s.encrypted;
export const exportKeyingMaterialImpl = (s, len, label) => s.exportKeyingMaterial(len, label);
export const exportKeyingMaterialOptionsImpl = (s, len, label, context) => s.exportKeyingMaterial(len, label, context);
export const getCertificateImpl = (s) => s.getCertificate();
export const getCipherImpl = (s) => s.getCipher();
export const getEphemeralKeyInfoImpl = (s) => s.getEphemeralKeyInfo();
export const getFinishedImpl = (s) => s.getFinished();
export const getPeerCertificateImpl = (s) => s.getPeerCertificate();
export const getPeerCertificateOptionsImpl = (s, o) => s.getPeerCertificate(o);
export const getPeerFinishedImpl = (s) => s.getPeerFinished();
export const getPeerX509CertificateImpl = (s) => s.getPeerX509Certificate();
export const getProtocolImpl = (s) => s.getProtocol();
export const getSessionImpl = (s) => s.getSession();
export const getSharedSigalgsImpl = (s) => s.getSharedSigalgs();
export const getTLSTicketImpl = (s) => s.getTLSTicket();
export const getX509CertificateImpl = (s) => s.getX509Certificate();
export const isSessionReusedImpl = (s) => s.isSessionReused();
export const setMaxSendFragmentImpl = (s, size) => s.setMaxSendFragment(size);
Loading

0 comments on commit 729af71

Please sign in to comment.