ssh: add support for SSH_AGENT_CONSTRAIN_EXTENSION with id 255

it was changed in the following draft

https://datatracker.ietf.org/doc/html/draft-miller-ssh-agent-03

The id 3 is now used for SSH_AGENT_CONSTRAIN_MAXSIGN key constraint,
an OpenSSH extension to the protocol that we do not currently support.
Instead, we added a compatibility layer for
SSH_AGENT_CONSTRAIN_EXTENSION with ID 3.

Fixes golang/go#62311

Change-Id: I421aee92aee9e693e43f66e6a5515c055333cb9b
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/525355
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Nicola Murino <nicola.murino@gmail.com>
Reviewed-by: Filippo Valsorda <filippo@golang.org>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Than McIntosh <thanm@google.com>
diff --git a/ssh/agent/client.go b/ssh/agent/client.go
index 9f09aae..fecba8e 100644
--- a/ssh/agent/client.go
+++ b/ssh/agent/client.go
@@ -141,9 +141,14 @@
 	agentAddSmartcardKeyConstrained = 26
 
 	// 3.7 Key constraint identifiers
-	agentConstrainLifetime  = 1
-	agentConstrainConfirm   = 2
-	agentConstrainExtension = 3
+	agentConstrainLifetime = 1
+	agentConstrainConfirm  = 2
+	// Constraint extension identifier up to version 2 of the protocol. A
+	// backward incompatible change will be required if we want to add support
+	// for SSH_AGENT_CONSTRAIN_MAXSIGN which uses the same ID.
+	agentConstrainExtensionV00 = 3
+	// Constraint extension identifier in version 3 and later of the protocol.
+	agentConstrainExtension = 255
 )
 
 // maxAgentResponseBytes is the maximum agent reply size that is accepted. This
@@ -205,7 +210,7 @@
 }
 
 type constrainExtensionAgentMsg struct {
-	ExtensionName    string `sshtype:"3"`
+	ExtensionName    string `sshtype:"255|3"`
 	ExtensionDetails []byte
 
 	// Rest is a field used for parsing, not part of message
diff --git a/ssh/agent/server.go b/ssh/agent/server.go
index dd2e0a3..e35ca7c 100644
--- a/ssh/agent/server.go
+++ b/ssh/agent/server.go
@@ -208,7 +208,7 @@
 		case agentConstrainConfirm:
 			confirmBeforeUse = true
 			constraints = constraints[1:]
-		case agentConstrainExtension:
+		case agentConstrainExtension, agentConstrainExtensionV00:
 			var msg constrainExtensionAgentMsg
 			if err = ssh.Unmarshal(constraints, &msg); err != nil {
 				return 0, false, nil, err
diff --git a/ssh/agent/server_test.go b/ssh/agent/server_test.go
index 0af8545..7700d18 100644
--- a/ssh/agent/server_test.go
+++ b/ssh/agent/server_test.go
@@ -243,7 +243,11 @@
 			ExtensionDetails: []byte(fmt.Sprintf("details: %d", i)),
 		}
 		expect = append(expect, ext)
-		data = append(data, agentConstrainExtension)
+		if i%2 == 0 {
+			data = append(data, agentConstrainExtension)
+		} else {
+			data = append(data, agentConstrainExtensionV00)
+		}
 		data = append(data, ssh.Marshal(ext)...)
 	}
 	_, _, extensions, err := parseConstraints(data)