ssh/terminal: handle ctrl+C, ctrl+F, ctrl+B

ctrl+C: terminate readline, which may result in application termination.
ctrl+F: keyRight
ctrl+B: keyLeft

Update golang/go#27147

Change-Id: If319ef79708b98c030cbce102400a785d15137f8
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/228223
Reviewed-by: Daniel Martí <mvdan@mvdan.cc>
Run-TryBot: Daniel Martí <mvdan@mvdan.cc>
TryBot-Result: Gobot Gobot <gobot@golang.org>
diff --git a/terminal.go b/terminal.go
index d1b4fca..2ffb97b 100644
--- a/terminal.go
+++ b/terminal.go
@@ -113,6 +113,7 @@
 }
 
 const (
+	keyCtrlC     = 3
 	keyCtrlD     = 4
 	keyCtrlU     = 21
 	keyEnter     = '\r'
@@ -151,8 +152,12 @@
 		switch b[0] {
 		case 1: // ^A
 			return keyHome, b[1:]
+		case 2: // ^B
+			return keyLeft, b[1:]
 		case 5: // ^E
 			return keyEnd, b[1:]
+		case 6: // ^F
+			return keyRight, b[1:]
 		case 8: // ^H
 			return keyBackspace, b[1:]
 		case 11: // ^K
@@ -738,6 +743,9 @@
 						return "", io.EOF
 					}
 				}
+				if key == keyCtrlC {
+					return "", io.EOF
+				}
 				if key == keyPasteStart {
 					t.pasteActive = true
 					if len(t.line) == 0 {
diff --git a/terminal_test.go b/terminal_test.go
index 2a2facc..c99638d 100644
--- a/terminal_test.go
+++ b/terminal_test.go
@@ -82,6 +82,14 @@
 		line: "ba",
 	},
 	{
+		in:   "a\006b\r", // ^F
+		line: "ab",
+	},
+	{
+		in:   "a\002b\r", // ^B
+		line: "ba",
+	},
+	{
 		in:   "a\177b\r", // backspace
 		line: "b",
 	},
@@ -208,6 +216,16 @@
 		line: "a",
 		err:  ErrPasteIndicator,
 	},
+	{
+		// Ctrl-C terminates readline
+		in:  "\003",
+		err: io.EOF,
+	},
+	{
+		// Ctrl-C at the end of line also terminates readline
+		in:  "a\003\r",
+		err: io.EOF,
+	},
 }
 
 func TestKeyPresses(t *testing.T) {