1package auth
2
3import (
4 "encoding/binary"
5 "io"
6
7 "github.com/bnema/neferafk/internal/ports"
8)
9
10// The bootstrap record is the first and only non-frame traffic on the worker's
11// stdin pipe (fd 3), written by the visual process once per lock:
12//
13// "NAB1" | u32 length | generation u64 | 3 x (u32 length | bytes)
14//
15// where the byte strings are PINSource, PINReference and EnvPIN.
16const bootstrapMax = 8 + 3*4 + 16 + ports.AuthMaxMetadata + ports.AuthMaxSecret
17
18// WriteBootstrap encodes b; it wipes its encoded copy but not b.EnvPIN.
19func WriteBootstrap(w io.Writer, b ports.AuthBootstrap) error {
20 if b.Generation == 0 || len(b.PINSource) > 16 || len(b.PINReference) > ports.AuthMaxMetadata || len(b.EnvPIN) > ports.AuthMaxSecret {
21 return ErrProtocol
22 }
23 p := make([]byte, 8, bootstrapMax+8)
24 binary.BigEndian.PutUint64(p, b.Generation)
25 for _, f := range [][]byte{[]byte(b.PINSource), []byte(b.PINReference), b.EnvPIN} {
26 p = binary.BigEndian.AppendUint32(p, uint32(len(f)))
27 p = append(p, f...)
28 }
29 out := append(append(make([]byte, 0, 8+len(p)), "NAB1"...), 0, 0, 0, 0)
30 binary.BigEndian.PutUint32(out[4:], uint32(len(p)))
31 out = append(out, p...)
32 clear(p)
33 defer clear(out)
34 return writeAll(w, out)
35}
36
37// ReadBootstrap decodes one record. The caller owns and wipes EnvPIN.
38func ReadBootstrap(r io.Reader) (ports.AuthBootstrap, error) {
39 var h [8]byte
40 if _, err := io.ReadFull(r, h[:]); err != nil {
41 return ports.AuthBootstrap{}, err
42 }
43 n := binary.BigEndian.Uint32(h[4:])
44 if string(h[:4]) != "NAB1" || n < 8+12 || n > bootstrapMax {
45 return ports.AuthBootstrap{}, ErrProtocol
46 }
47 p := make([]byte, n)
48 defer clear(p)
49 if _, err := io.ReadFull(r, p); err != nil {
50 return ports.AuthBootstrap{}, err
51 }
52 b := ports.AuthBootstrap{Generation: binary.BigEndian.Uint64(p)}
53 rest := p[8:]
54 var f [3][]byte
55 for i := range f {
56 if len(rest) < 4 {
57 return ports.AuthBootstrap{}, ErrProtocol
58 }
59 l := binary.BigEndian.Uint32(rest)
60 if uint64(l) > uint64(len(rest)-4) {
61 return ports.AuthBootstrap{}, ErrProtocol
62 }
63 f[i], rest = rest[4:4+l], rest[4+l:]
64 }
65 if b.Generation == 0 || len(rest) != 0 || len(f[0]) > 16 || len(f[1]) > ports.AuthMaxMetadata || len(f[2]) > ports.AuthMaxSecret {
66 return ports.AuthBootstrap{}, ErrProtocol
67 }
68 b.PINSource, b.PINReference = ports.PINSource(f[0]), string(f[1])
69 if len(f[2]) > 0 {
70 b.EnvPIN = append([]byte(nil), f[2]...)
71 }
72 return b, nil
73}