~/bend-docscommunity

scram.bend relies on unsafe/foreign

raw source on the hub · import bend-kit-postgres@0.1.0.2/scram.bend as Scram

SCRAM-SHA-256 (RFC 5802, RFC 7677) client for PostgreSQL SASL authentication, without channel binding.

3 imports
import Base
import bend-kit-bytes@0.3.1.0/bytes.bend as Bytes
import bend-kit-crypto@0.2.2.1/crypto.bend as Crypto

Types

type First source · line 18 · raw

Data

bare: client-first-message-bare. nonce: the client nonce.

type Final source · line 22 · raw

Data

sig: the ServerSignature the server-final-message must carry.

type Parsed source · line 26 · raw

Data

nonce: the combined nonce. salt: decoded octets. iters: at least 1.

Definitions

def mechanism source · line 14 · raw

String

def saslname.of source · line 30 · raw

@+c:U32 -> @+rest:String -> String

saslname (RFC 5802 §5.1): '=' as "=3D", ',' as "=2C".

def saslname source · line 33 · raw

@s:String -> String

def first.with source · line 41 · raw

@+user:String -> @+nonce:String -> Pair(First, String)

The client-first-message for a nonce: gs2 header "n,," (no channel binding), then the bare part.

def first.of source · line 45 · raw

@+user:String -> @r:Result<&1, &1, Pair(U32, String), Pair(U32, Array<U32>)> -> Result<&1, &1, Pair(U32, String), Pair(First, String)>

def first source · line 53 · raw

@+user:String -> IO(Result<&1, &1, Pair(U32, String), Pair(First, String)>)

A fresh 18-octet nonce from the OS secure random source, base64 (24 printable chars, no ',').

def attr source · line 61 · raw

@+name:U32 -> @s:String -> Maybe<&2, String>

The value of attribute name, when s is name=value.

def printable source · line 69 · raw

@s:String -> Bool

printable (RFC 5802 §7): %x21-2B / %x2D-7E.

def digits source · line 76 · raw

@s:String -> Bool

def nonce.ok source · line 84 · raw

@+client:String -> @+server:String -> Bool

The server nonce must be the client nonce plus at least one more printable char.

def parse.iters source · line 87 · raw

@ok:Bool -> @m:Maybe<&2, U32> -> @+nonce:String -> @+salt:String -> Result<&1, &1, Pair(U32, String), Parsed>

def parse.salt.of source · line 94 · raw

@empty:Bool -> @+nonce:String -> @+salt:String -> @+it:String -> Result<&1, &1, Pair(U32, String), Parsed>

def parse.salt source · line 101 · raw

@m:Maybe<&1, 0xb7603dcfa1d7f60d01e06c928738cc73/bytes.Bytes> -> @+nonce:String -> @+it:String -> Result<&1, &1, Pair(U32, String), Parsed>

def parse.nonce source · line 109 · raw

@ok:Bool -> @+nonce:String -> @+salt:String -> @+it:String -> Result<&1, &1, Pair(U32, String), Parsed>

def parse.fields source · line 116 · raw

@+client:String -> @r:Maybe<&2, String> -> @s:Maybe<&2, String> -> @i:Maybe<&2, String> -> Result<&1, &1, Pair(U32, String), Parsed>

def parse.parts source · line 123 · raw

@+client:String -> @parts:List<&2, String> -> Result<&1, &1, Pair(U32, String), Parsed>

def parse source · line 131 · raw

@+client:String -> @+msg:String -> Result<&1, &1, Pair(U32, String), Parsed>

The server-first-message checked against the client nonce.

def octets source · line 136 · raw

@r:Result<&1, &1, Pair(U32, String), Pair(U32, Array<U32>)> -> Result<&1, &1, Pair(U32, String), String>

def hmac.of source · line 143 · raw

@k:0xb7603dcfa1d7f60d01e06c928738cc73/bytes.Bytes -> @d:0xb7603dcfa1d7f60d01e06c928738cc73/bytes.Bytes -> IO(Result<&1, &1, Pair(U32, String), Pair(U32, Array<U32>)>)

def hmac source · line 148 · raw

@+key:String -> @+data:String -> IO(Result<&1, &1, Pair(U32, String), String>)

def sha.of source · line 153 · raw

@b:0xb7603dcfa1d7f60d01e06c928738cc73/bytes.Bytes -> IO(Result<&1, &1, Pair(U32, String), Pair(U32, Array<U32>)>)

def sha source · line 157 · raw

@+data:String -> IO(Result<&1, &1, Pair(U32, String), String>)

def pbkdf2.of source · line 162 · raw

@p:0xb7603dcfa1d7f60d01e06c928738cc73/bytes.Bytes -> @s:0xb7603dcfa1d7f60d01e06c928738cc73/bytes.Bytes -> @+iters:U32 -> IO(Result<&1, &1, Pair(U32, String), Pair(U32, Array<U32>)>)

def pbkdf2 source · line 168 · raw

@+pass:String -> @+salt:String -> @+iters:U32 -> IO(Result<&1, &1, Pair(U32, String), String>)

SaltedPassword = Hi(pass, salt, iters) (RFC 5802 §2.2), which is PBKDF2 with HMAC-SHA-256.

def eq.of source · line 173 · raw

@a:0xb7603dcfa1d7f60d01e06c928738cc73/bytes.Bytes -> @b:0xb7603dcfa1d7f60d01e06c928738cc73/bytes.Bytes -> IO(Bool)

def xor source · line 178 · raw

@a:String -> @b:String -> String

def step.of source · line 187 · raw

@r:Result<&1, &1, Pair(U32, String), String> -> @k:(@_:String -> IO(Result<&1, &1, Pair(U32, String), Pair(Final, String)>)) -> IO(Result<&1, &1, Pair(U32, String), Pair(Final, String)>)

def step source · line 194 · raw

@m:IO(Result<&1, &1, Pair(U32, String), String>) -> @k:(@_:String -> IO(Result<&1, &1, Pair(U32, String), Pair(Final, String)>)) -> IO(Result<&1, &1, Pair(U32, String), Pair(Final, String)>)

def respond.of source · line 200 · raw

@r:Result<&1, &1, Pair(U32, String), Parsed> -> @+bare:String -> @+sf:String -> @+pass:String -> IO(Result<&1, &1, Pair(U32, String), Pair(Final, String)>)

c=biws is base64 of the gs2 header "n,,". AuthMessage joins the three bare messages.

def respond source · line 216 · raw

@st:First -> @+sf:String -> @+pass:String -> IO(Result<&1, &1, Pair(U32, String), Pair(Final, String)>)

The client-final-message with ClientProof for the server-first-message sf and the password.

def final.sig source · line 222 · raw

@m:Maybe<&1, 0xb7603dcfa1d7f60d01e06c928738cc73/bytes.Bytes> -> Result<&1, &1, Pair(U32, String), String>

def final.attr source · line 229 · raw

@s:String -> Result<&1, &1, Pair(U32, String), String>

def final.parts source · line 238 · raw

@parts:List<&2, String> -> Result<&1, &1, Pair(U32, String), String>

def verify.ok source · line 245 · raw

@ok:Bool -> Result<&1, &1, Pair(U32, String), Unit>

def verify.of source · line 252 · raw

@+sig:String -> @r:Result<&1, &1, Pair(U32, String), String> -> IO(Result<&1, &1, Pair(U32, String), Unit>)

def verify source · line 262 · raw

@st:Final -> @+msg:String -> IO(Result<&1, &1, Pair(U32, String), Unit>)

Done when the server-final-message proves the server knows the password, compared in constant time.