From 94779a0cf10912a27e0c2f266eea5a83283b4420 Mon Sep 17 00:00:00 2001 From: Jonathan Pittman Date: Wed, 23 Oct 2013 12:44:23 -0400 Subject: [PATCH] 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 --- ssh/certs.go | 32 +++++++++++++++++++++++++++----- ssh/keys_test.go | 7 +++---- 2 files changed, 30 insertions(+), 9 deletions(-) diff --git a/ssh/certs.go b/ssh/certs.go index 1023d8a..d958f31 100644 --- a/ssh/certs.go +++ b/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 diff --git a/ssh/keys_test.go b/ssh/keys_test.go index 99eac94..3c4b735 100644 --- a/ssh/keys_test.go +++ b/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())