package maintenance import ( "bytes" "crypto/ecdh" "crypto/rand" "encoding/binary" "reflect" "strings" "testing" ) func TestInputExactSignedBytes(t *testing.T) { in := Input{Arguments: []string{"operator-v1", "", "space value", "$(not-a-shell)"}, Stdin: []byte(" \n{\"signed\": \"\\u003c\", \"n\": 1e0} \n")} frame, err := EncodeInput(in) if err != nil { t.Fatal(err) } got, err := DecodeInput(frame) if err != nil || !reflect.DeepEqual(in, got) { t.Fatalf("round trip: %v", err) } clear(frame) if !reflect.DeepEqual(in, got) { t.Fatal("decoded input aliases frame") } priv, err := ecdh.P256().GenerateKey(rand.Reader) if err != nil { t.Fatal(err) } fingerprint, _ := RecipientFingerprint(priv.PublicKey()) binding := Binding{Version: 1, Direction: DirectionInput, Account: "account", App: "home", Host: "test.example", RequestID: "request", AttemptID: "attempt", Recipient: fingerprint} sealed, err := SealInput(priv.PublicKey(), binding, in) if err != nil { t.Fatal(err) } opened, err := OpenInput(priv, binding, sealed) if err != nil || !reflect.DeepEqual(in, opened) { t.Fatalf("sealed round trip: %v", err) } for _, direction := range []Direction{DirectionResult, DirectionEnvironment} { wrong := binding wrong.Direction = direction if _, err := SealInput(priv.PublicKey(), wrong, in); err != ErrInput { t.Fatal("accepted wrong sealing direction") } // An otherwise valid envelope of the wrong purpose must also be refused. frame, _ := EncodeInput(in) ciphertext, err := Seal(priv.PublicKey(), wrong, frame) if err != nil { t.Fatal(err) } if _, err := OpenInput(priv, wrong, ciphertext); err != ErrInput { t.Fatal("accepted wrong opening direction") } } } func TestInputLimitsAndRefusals(t *testing.T) { maxJSON := []byte(`"` + strings.Repeat("x", MaxInputBytes-2) + `"`) args := make([]string, MaxArguments) args[0] = strings.Repeat("x", MaxArgumentBytes) boundary := Input{Arguments: args, Stdin: maxJSON} frame, err := EncodeInput(boundary) if err != nil { t.Fatal(err) } if _, err := DecodeInput(frame); err != nil { t.Fatal(err) } bad := []Input{ {nil, []byte(`{}`)}, {make([]string, MaxArguments+1), []byte(`{}`)}, {[]string{strings.Repeat("x", MaxArgumentBytes), "x"}, []byte(`{}`)}, {[]string{"ok\x00bad"}, []byte(`{}`)}, {[]string{string([]byte{255})}, []byte(`{}`)}, {[]string{"ok"}, append(bytes.Clone(maxJSON), ' ')}, {[]string{"ok"}, nil}, {[]string{"ok"}, []byte(`{} {}`)}, {[]string{"ok"}, []byte{'"', 255, '"'}}, } for i, in := range bad { if _, err := EncodeInput(in); err != ErrInput { t.Fatalf("case %d encode accepted", i) } // Construct unchecked frames so decoder limits cannot rely on the encoder. raw := binary.BigEndian.AppendUint32([]byte(inputPrefix), uint32(len(in.Arguments))) for _, arg := range in.Arguments { raw = appendField(raw, []byte(arg)) } raw = appendField(raw, in.Stdin) if _, err := DecodeInput(raw); err != ErrInput { t.Fatalf("case %d decode accepted", i) } } } func TestInputMalformedFrames(t *testing.T) { frame, _ := EncodeInput(Input{[]string{"operator-v1", "argument"}, []byte(`{"ok":true}`)}) for n := 0; n < len(frame); n++ { if _, err := DecodeInput(frame[:n]); err != ErrInput { t.Fatalf("accepted truncation %d", n) } } for _, offset := range []int{0, 4, 8, 8 + 4 + len("operator-v1"), 8 + 4 + len("operator-v1") + 4 + len("argument")} { broken := bytes.Clone(frame) binary.BigEndian.PutUint32(broken[offset:offset+4], ^uint32(0)) if _, err := DecodeInput(broken); err != ErrInput { t.Fatalf("accepted corrupt frame at %d", offset) } } if _, err := DecodeInput(append(bytes.Clone(frame), 0)); err != ErrInput { t.Fatal("accepted trailing bytes") } if _, err := DecodeInput(make([]byte, maxInputFrame+1)); err != ErrInput { t.Fatal("accepted oversized frame") } }