dkim.bend checks
raw source on the hub · import 0xfd64aefef43747d3eaeea50c10a112e5/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).
Field@name:String -> @value:String -> Field
type Cut source · line 23 · raw
Data
A message cut at its first empty line.
Cut@head:String -> @body:String -> Cut
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.