Переглянути джерело

go.crypto/ssh: Implement CertTime to properly handle the "infinite" time
value ^0, which would become negative when expressed as int64.

R=agl, dave, jpsugar, hanwen
CC=golang-dev
https://golang.org/cl/15520047

Jonathan Pittman 12 роки тому
батько
коміт
94779a0cf1
2 змінених файлів з 30 додано та 9 видалено
  1. 27 5
      ssh/certs.go
  2. 3 4
      ssh/keys_test.go

+ 27 - 5
ssh/certs.go

@@ -35,6 +35,28 @@ type tuple struct {
 	Data string
 }
 
+const (
+	maxUint64 = 1<<64 - 1
+	maxInt64  = 1<<63 - 1
+)
+
+// CertTime represents an unsigned 64-bit time value in seconds starting from
+// UNIX epoch.  We use CertTime instead of time.Time in order to properly handle
+// the "infinite" time value ^0, which would become negative when expressed as
+// an int64.
+type CertTime uint64
+
+func (ct CertTime) Time() time.Time {
+	if ct > maxInt64 {
+		return time.Unix(maxInt64, 0)
+	}
+	return time.Unix(int64(ct), 0)
+}
+
+func (ct CertTime) IsInfinite() bool {
+	return ct == maxUint64
+}
+
 // An OpenSSHCertV01 represents an OpenSSH certificate as defined in
 // [PROTOCOL.certkeys]?rev=1.8.
 type OpenSSHCertV01 struct {
@@ -44,7 +66,7 @@ type OpenSSHCertV01 struct {
 	Type                    uint32
 	KeyId                   string
 	ValidPrincipals         []string
-	ValidAfter, ValidBefore time.Time
+	ValidAfter, ValidBefore CertTime
 	CriticalOptions         []tuple
 	Extensions              []tuple
 	Reserved                []byte
@@ -115,8 +137,8 @@ func (cert *OpenSSHCertV01) marshal(includeAlgo, includeSig bool) []byte {
 	r = marshalUint32(r, cert.Type)
 	r = marshalString(r, []byte(cert.KeyId))
 	r = marshalLengthPrefixedNameList(r, cert.ValidPrincipals)
-	r = marshalUint64(r, uint64(cert.ValidAfter.Unix()))
-	r = marshalUint64(r, uint64(cert.ValidBefore.Unix()))
+	r = marshalUint64(r, uint64(cert.ValidAfter))
+	r = marshalUint64(r, uint64(cert.ValidBefore))
 	r = marshalTupleList(r, cert.CriticalOptions)
 	r = marshalTupleList(r, cert.Extensions)
 	r = marshalString(r, cert.Reserved)
@@ -195,13 +217,13 @@ func parseOpenSSHCertV01(in []byte, algo string) (out *OpenSSHCertV01, rest []by
 	if !ok {
 		return
 	}
-	cert.ValidAfter = time.Unix(int64(va), 0)
+	cert.ValidAfter = CertTime(va)
 
 	vb, in, ok := parseUint64(in)
 	if !ok {
 		return
 	}
-	cert.ValidBefore = time.Unix(int64(vb), 0)
+	cert.ValidBefore = CertTime(vb)
 
 	if cert.CriticalOptions, in, ok = parseTupleList(in); !ok {
 		return

+ 3 - 4
ssh/keys_test.go

@@ -9,7 +9,6 @@ import (
 	"reflect"
 	"strings"
 	"testing"
-	"time"
 )
 
 var (
@@ -46,9 +45,9 @@ func init() {
 		Nonce:           []byte{}, // To pass reflect.DeepEqual after marshal & parse, this must be non-nil
 		Key:             ecdsaKey.PublicKey(),
 		ValidPrincipals: []string{"gopher1", "gopher2"}, // increases test coverage
-		ValidAfter:      time.Now().Truncate(time.Second),
-		ValidBefore:     time.Now().Truncate(time.Second).Add(time.Hour),
-		Reserved:        []byte{}, // To pass reflect.DeepEqual after marshal & parse, this must be non-nil
+		ValidAfter:      0,                              // unix epoch
+		ValidBefore:     maxUint64,                      // The end of currently representable time.
+		Reserved:        []byte{},                       // To pass reflect.DeepEqual after marshal & parse, this must be non-nil
 		SignatureKey:    rsaKey.PublicKey(),
 	}
 	sigBytes, _ := rsaKey.Sign(rand.Reader, testCert.BytesForSigning())