~/bend-docscommunity

dkim.bend checks

raw source on the hub · import 0x650cd729029c76d9f6151ee977dc0ae8/dkim.bend as Dkim

DKIM signing (RFC 6376; Ed25519 by RFC 8463): the header fields and the body are put in a canonical form, the body's SHA-256 goes in bh=, and the signature over the canonical fields (the DKIM-Signature itself last, with b= empty) goes in b=. The relaxed forms survive the rewrapping and trailing-space changes relays make; the simple ones take the text as it is. Fields are oversigned on request: named once more than they occur, so one added on the way breaks the signature.

Not done, on purpose: the l= tag (a body length), which lets anyone append to a signed message (RFC 6376 8.2).

This file is the pure part (canonical forms, the field), which LAWS.bend pins; the hash and the signature come from OpenSSL, as Dkim.sha256, Dkim.alg and Dkim.sign in net.bend.

2 imports
import Base
import ./text.bend as T

Types

type Field source · line 19 · raw

Data

A header field: its name and its value as written (folds included).

type Cut source · line 23 · raw

Data

A message cut at its first empty line.

Definitions

def Dkim.cut source · line 31 · raw

@s:String -> @acc:String -> Cut

The header block (its last CRLF kept) and the body after the empty line; acc: the header so far, reversed.

def Dkim.field.of source · line 40 · raw

@parts:List<&2, String> -> Field

def Dkim.wsp source · line 47 · raw

@+l:String -> Bool

def Dkim.fields.put source · line 50 · raw

@+cur:String -> @+acc:List<&2, Field> -> List<&2, Field>

def Dkim.fields.go source · line 56 · raw

@ls:List<&2, String> -> @+cur:String -> @+acc:List<&2, Field> -> List<&2, Field>

The lines of a header block as fields, last first: a line that starts with a space or a tab continues the field before it.

def Dkim.uncr source · line 65 · raw

@s:String -> String

def Dkim.fields source · line 76 · raw

@head:String -> List<&2, Field>

The fields of a header block, the last one first (the order DKIM signs repeated fields in, RFC 6376 5.4.2).

def Dkim.sp source · line 82 · raw

@yes:Bool -> @rest:String -> String

def Dkim.value source · line 91 · raw

@s:String -> @ws:Bool -> @start:Bool -> String

A value unfolded, each run of spaces and tabs as one space, none at the start or the end; ws: a run is pending; start: nothing written yet.

def Dkim.relaxed source · line 107 · raw

@f:Field -> String

A field in relaxed form: the name in lower case, a colon, the value.

def Dkim.simple source · line 112 · raw

@f:Field -> String

A field in simple form: as it is in the message (RFC 6376 3.4.1).

def Dkim.field source · line 116 · raw

@relaxed:Bool -> @f:Field -> String

def Dkim.nls source · line 123 · raw

@n:Nat -> @acc:String -> String

def Dkim.space source · line 130 · raw

@yes:Bool -> @acc:String -> String

def Dkim.body.end source · line 137 · raw

@acc:String -> String

def Dkim.body source · line 148 · raw

@s:String -> @ws:Bool -> @nl:Nat -> @acc:String -> String

A body in relaxed form: runs of spaces and tabs as one space, none at a line's end, no empty lines at the end, and a final CRLF (an empty body stays empty). ws: a run is pending; nl: line ends pending; acc: the output so far, reversed (a tail call per char).

def Dkim.body.simple source · line 166 · raw

@s:String -> @nl:Nat -> @acc:String -> String

A body in simple form (RFC 6376 3.4.3): as it is, but with no empty lines at the end and one final CRLF (an empty body is one CRLF). nl: line ends pending; acc: the output so far, reversed.

def Dkim.body.of source · line 177 · raw

@relaxed:Bool -> @s:String -> String

def Dkim.names.more source · line 187 · raw

@ns:List<&2, String> -> @+col:U32 -> String

def Dkim.names source · line 198 · raw

@ns:List<&2, String> -> String

The h= tag's names, colon-separated, folded before column 70.

def Dkim.named source · line 206 · raw

@fs:List<&2, Field> -> List<&2, String>

Each field's name, in lower case.

def Dkim.over source · line 216 · raw

List<&2, String>

The fields an added copy of which would mislead a reader: each is named once more in h= than it occurs (RFC 6376 5.4), present or not.

def Dkim.c source · line 219 · raw

@relaxed:Bool -> String

def Dkim.tags source · line 228 · raw

@alg:String -> @hr:Bool -> @br:Bool -> @domain:String -> @selector:String -> @+t:U32 -> @fs:List<&2, Field> -> @over:List<&2, String> -> @bh:String -> String

The DKIM-Signature's value up to "b=", its signature still to come. hr, br: relaxed header and body forms; over: the oversigned names.

def Dkim.canon source · line 235 · raw

@+hr:Bool -> @fs:List<&2, Field> -> String

def Dkim.data source · line 245 · raw

@+hr:Bool -> @fs:List<&2, Field> -> @tags:String -> String

What is signed: each field in canonical form with its CRLF, then the DKIM-Signature itself, b= empty and no CRLF (RFC 6376 3.7). An oversigned name with no field left adds nothing (5.4).

def Dkim.fold source · line 249 · raw

@s:String -> @+col:U32 -> String

The signature in lines of at most 72 digits.

def Dkim.header source · line 259 · raw

@tags:String -> @sig:String -> String

The whole field, ready to go in front of the message.