golang / go

The Go programming language
https://go.dev
BSD 3-Clause "New" or "Revised" License
123.33k stars 17.58k forks source link

x/crypto/ssh: unable to parse certificates #45010

Closed c00w closed 2 years ago

c00w commented 3 years ago

What version of Go are you using (go version)?

 % go version                                                          
go version go1.16 linux/amd64

Does this issue reproduce with the latest release?

Yes - AFAIK go1.16 is the latest release

What operating system and processor architecture are you using (go env)?

tl;dr, linux, nixos, x86

go env Output
 % go env
GO111MODULE=""
GOARCH="amd64"
GOBIN=""
GOCACHE="/home/colin/.cache/go-build"
GOENV="/home/colin/.config/go/env"
GOEXE=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="linux"
GOINSECURE=""
GOMODCACHE="/home/colin/pkg/mod"
GONOPROXY=""
GONOSUMDB=""
GOOS="linux"
GOPATH="/home/colin"
GOPRIVATE=""
GOPROXY="https://proxy.golang.org,direct"
GOROOT="/nix/store/hnnwgq22qyycxqhpskdh64nyki1pd6s6-go-1.16/share/go"
GOSUMDB="sum.golang.org"
GOTMPDIR=""
GOTOOLDIR="/nix/store/hnnwgq22qyycxqhpskdh64nyki1pd6s6-go-1.16/share/go/pkg/tool/linux_amd64"
GOVCS=""
GOVERSION="go1.16"
GCCGO="gccgo"
AR="ar"
CC="gcc"
CXX="g++"
CGO_ENABLED="1"
GOMOD="/home/colin/tooling/repro/ssh_bug/go.mod"
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config"
GOGCCFLAGS="-fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build2320329228=/tmp/go-build -gno-record-gcc-switches"

What did you do?

git clone https://git.sr.ht/~c00w/tooling
cd tooling/repro/ssh_bug 
./test.sh

Where test.sh is a pretty basic generate a ssh key, sign it, try and parse it.

% cat test.sh
set -ex
ssh -V
rm -f test test.pub test-cert.pub
ssh-keygen -f test -t ed25519
ssh-keygen -s test -I "dummy" -n "dummy" test
go run test.go

test.go is just a dummy program which tries to parse the file using ssh.ParsePublicKey as https://github.com/golang/go/issues/22046 and the docs at https://pkg.go.dev/golang.org/x/crypto/ssh#Certificate suggest

% cat test.go
package main

import (
    "log"
    "os"

    "golang.org/x/crypto/ssh"
)

func main() {
    b, err := os.ReadFile("test-cert.pub")
    if err != nil {
        panic(err)
    }

    o, err := ssh.ParsePublicKey(b)
    if err != nil {
        panic(err)
    }

    log.Print(o)

}

What did you expect to see?

I expected the certificate to be parsed.

What did you see instead?

The ParsePublicKey method fails with a short read.

% ./test.sh
+ ssh -V
OpenSSH_8.4p1, OpenSSL 1.1.1j  16 Feb 2021
+ rm -f test test.pub test-cert.pub
+ ssh-keygen -f test -t ed25519
Generating public/private ed25519 key pair.
Enter passphrase (empty for no passphrase): 
Enter same passphrase again: 
Your identification has been saved in test
Your public key has been saved in test.pub
The key fingerprint is:
SHA256:BtrbK4/yRnNkQrxG6zZHVXNRd4W0BKvbC381sMUKelo colin@kelvin
The key's randomart image is:
+--[ED25519 256]--+
|     .     .+o=+*|
|      +   .  =..o|
|     o.o .  . o  |
|     o=.+  o . o |
|    .o.=S o . =  |
|      *+o. E o ..|
|     o.=. * .  ..|
|    . o. o o ..  |
|     +ooo   o.   |
+----[SHA256]-----+
+ ssh-keygen -s test -I dummy -n dummy test
Signed user key test-cert.pub: id "dummy" serial 0 for dummy valid forever

+ go run test.go
panic: ssh: short read

goroutine 1 [running]:
main.main()
    /home/test/tooling/repro/ssh_bug/test.go:18 +0xfe
exit status 2

Note that this appears to be key type agnostic - dropping the ed25519 type (so it's thus rsa), fails with the same error message.

c00w commented 3 years ago

And of course now that I've filed the bug report - using ssh.ParseAuthorizedKey appears to work - which does not match the documentation, but does match the linked paste from the original issue :).

cherrymui commented 3 years ago

cc @FiloSottile

antong commented 3 years ago

using ssh.ParseAuthorizedKey appears to work - which does not match the documentation ...

The documentation says that ParsePublicKey "parses an SSH public key formatted for use in the SSH wire protocol ...", and that ParseAuthorizedKey is for the text format, so I guess the behavior does match the documentation. I agree it could be a bit more verbose and clearer.

seankhliao commented 2 years ago

See above