carlos / platform Public

Clone
git clone https://amadan.net/carlos/platform

Plain git — no account needed to clone.

Download

Download this file

1package maintenance
2
3import (
4 "bytes"
5 "crypto/ecdh"
6 "encoding/binary"
7 "encoding/json"
8 "errors"
9 "strings"
10 "unicode/utf8"
11)
12
13const (
14 MaxInputBytes = 64 << 10
15 MaxArguments = 32
16 MaxArgumentBytes = 8 << 10
17 inputPrefix = "CMI1"
18 maxInputFrame = len(inputPrefix) + 4 + 4*MaxArguments + MaxArgumentBytes + 4 + MaxInputBytes
19)
20
21var ErrInput = errors.New("maintenance: invalid private input")
22
23// Input contains arguments for the pinned artifact and its exact JSON stdin.
24// The worker supplies the existing database path; this value grants no authority
25// to select an executable, shell, database, environment, or working directory.
26type Input struct {
27 Arguments []string
28 Stdin []byte
29}
30
31func validInput(in Input) bool {
32 if len(in.Arguments) == 0 || len(in.Arguments) > MaxArguments || len(in.Stdin) > MaxInputBytes || !utf8.Valid(in.Stdin) || !json.Valid(in.Stdin) {
33 return false
34 }
35 total := 0
36 for _, arg := range in.Arguments {
37 if len(arg) > MaxArgumentBytes-total || strings.ContainsRune(arg, 0) || !utf8.ValidString(arg) {
38 return false
39 }
40 total += len(arg)
41 }
42 return true
43}
44
45// EncodeInput retains whitespace, escapes, and every other byte of signed input.
46// JSON semantics, including allowed fields, remain the app command's concern.
47func EncodeInput(in Input) ([]byte, error) {
48 if !validInput(in) {
49 return nil, ErrInput
50 }
51 out := binary.BigEndian.AppendUint32([]byte(inputPrefix), uint32(len(in.Arguments)))
52 for _, arg := range in.Arguments {
53 out = appendField(out, []byte(arg))
54 }
55 return appendField(out, in.Stdin), nil
56}
57
58// DecodeInput returns owned data only after checking the complete bounded frame.
59func DecodeInput(frame []byte) (Input, error) {
60 if len(frame) > maxInputFrame || len(frame) < 8 || !bytes.Equal(frame[:4], []byte(inputPrefix)) {
61 return Input{}, ErrInput
62 }
63 count := binary.BigEndian.Uint32(frame[4:8])
64 if count == 0 || count > MaxArguments {
65 return Input{}, ErrInput
66 }
67 rest := frame[8:]
68 take := func() ([]byte, bool) {
69 if len(rest) < 4 {
70 return nil, false
71 }
72 n := binary.BigEndian.Uint32(rest[:4])
73 rest = rest[4:]
74 if uint64(n) > uint64(len(rest)) {
75 return nil, false
76 }
77 value := rest[:int(n)]
78 rest = rest[int(n):]
79 return value, true
80 }
81 in := Input{Arguments: make([]string, 0, int(count))}
82 for range count {
83 arg, ok := take()
84 if !ok {
85 return Input{}, ErrInput
86 }
87 in.Arguments = append(in.Arguments, string(arg))
88 }
89 stdin, ok := take()
90 if !ok || len(rest) != 0 {
91 return Input{}, ErrInput
92 }
93 in.Stdin = stdin
94 if !validInput(in) {
95 return Input{}, ErrInput
96 }
97 in.Stdin = bytes.Clone(stdin)
98 return in, nil
99}
100
101func SealInput(pub *ecdh.PublicKey, binding Binding, in Input) (string, error) {
102 if binding.Direction != DirectionInput {
103 return "", ErrInput
104 }
105 frame, err := EncodeInput(in)
106 if err != nil {
107 return "", ErrInput
108 }
109 defer clear(frame)
110 sealed, err := Seal(pub, binding, frame)
111 if err != nil {
112 return "", ErrInput
113 }
114 return sealed, nil
115}
116
117func OpenInput(priv *ecdh.PrivateKey, binding Binding, sealed string) (Input, error) {
118 if binding.Direction != DirectionInput {
119 return Input{}, ErrInput
120 }
121 frame, err := Open(priv, binding, sealed)
122 if err != nil {
123 return Input{}, ErrInput
124 }
125 defer clear(frame)
126 return DecodeInput(frame)
127}
128