[crypto] ssh: support for marshaling keys using the OpenSSH format

478 views
Skip to first unread message

Mariano Cano (Gerrit)

unread,
Feb 7, 2020, 11:27:15 PM2/7/20
to Ian Lance Taylor, Filippo Valsorda, goph...@pubsubhelper.golang.org, golang-co...@googlegroups.com

Mariano Cano has uploaded this change for review.

View Change

ssh: support for marshaling keys using the OpenSSH format

This adds methods to marshal private keys, encrypted and unecntypted
to the OpenSSH format.

Fixes golang/go#37132

Change-Id: I1a95301f789ce04858e6b147748c6e8b7700384b
---
M ssh/keys.go
M ssh/keys_test.go
2 files changed, 289 insertions(+), 1 deletion(-)

diff --git a/ssh/keys.go b/ssh/keys.go
index d63cbf6..e3f3c29 100644
--- a/ssh/keys.go
+++ b/ssh/keys.go
@@ -13,11 +13,13 @@
"crypto/ecdsa"
"crypto/elliptic"
"crypto/md5"
+ "crypto/rand"
"crypto/rsa"
"crypto/sha256"
"crypto/x509"
"encoding/asn1"
"encoding/base64"
+ "encoding/binary"
"encoding/hex"
"encoding/pem"
"errors"
@@ -289,6 +291,18 @@
return b.Bytes()
}

+// MarshalPrivateKey returns a PEM block with the private key serialized in the
+// OpenSSH format.
+func MarshalPrivateKey(key crypto.PrivateKey, comment string) (*pem.Block, error) {
+ return marshalOpenSSHPrivateKey(key, comment, unencryptedOpenSSHMarshaler)
+}
+
+// MarshalPrivateKeyWithPassphrase returns an encrypted PEM block with the
+// private key serialized in the OpenSSH format.
+func MarshalPrivateKeyWithPassphrase(key crypto.PrivateKey, comment string, passphrase []byte) (*pem.Block, error) {
+ return marshalOpenSSHPrivateKey(key, comment, passphraseProtectedOpenSSHMarshaler(passphrase))
+}
+
// PublicKey is an abstraction of different types of public keys.
type PublicKey interface {
// Type returns the key's type, e.g. "ssh-rsa".
@@ -1247,14 +1261,66 @@
}
}

+func randomSalt(size int) ([]byte, error) {
+ salt := make([]byte, size)
+ _, err := io.ReadFull(rand.Reader, salt)
+ if err != nil {
+ return nil, err
+ }
+ return salt, nil
+}
+
+func unencryptedOpenSSHMarshaler(PrivKeyBlock []byte) ([]byte, string, string, string, error) {
+ key := generateOpenSSHPadding(PrivKeyBlock, 8)
+ return key, "none", "none", "", nil
+}
+
+func passphraseProtectedOpenSSHMarshaler(passphrase []byte) openSSHEncryptFunc {
+ return func(PrivKeyBlock []byte) ([]byte, string, string, string, error) {
+ salt, err := randomSalt(16)
+ if err != nil {
+ return nil, "", "", "", err
+ }
+
+ opts := struct {
+ Salt []byte
+ Rounds uint32
+ }{salt, 16}
+
+ // Derive key to encrypt the private key block.
+ k, err := bcrypt_pbkdf.Key(passphrase, salt, int(opts.Rounds), 32+aes.BlockSize)
+ if err != nil {
+ return nil, "", "", "", err
+ }
+
+ // Add padding matching the block size of AES.
+ keyBlock := generateOpenSSHPadding(PrivKeyBlock, aes.BlockSize)
+
+ // Encrypt the private key using the derived secret.
+ dst := make([]byte, len(keyBlock))
+ iv := k[32 : 32+aes.BlockSize]
+ block, err := aes.NewCipher(k[:32])
+ if err != nil {
+ return nil, "", "", "", err
+ }
+
+ stream := cipher.NewCTR(block, iv)
+ stream.XORKeyStream(dst, keyBlock)
+
+ return dst, "aes256-ctr", "bcrypt", string(Marshal(opts)), nil
+ }
+}
+
+const magic = "openssh-key-v1\x00"
+
type openSSHDecryptFunc func(CipherName, KdfName, KdfOpts string, PrivKeyBlock []byte) ([]byte, error)
+type openSSHEncryptFunc func(PrivKeyBlock []byte) (ProtectedKeyBlock []byte, cipherName, kdfName, kdfOptions string, err error)

// parseOpenSSHPrivateKey parses an OpenSSH private key, using the decrypt
// function to unwrap the encrypted portion. unencryptedOpenSSHKey can be used
// as the decrypt function to parse an unencrypted private key. See
// https://github.com/openssh/openssh-portable/blob/master/PROTOCOL.key.
func parseOpenSSHPrivateKey(key []byte, decrypt openSSHDecryptFunc) (crypto.PrivateKey, error) {
- const magic = "openssh-key-v1\x00"
if len(key) < len(magic) || string(key[:len(magic)]) != magic {
return nil, errors.New("ssh: invalid openssh private key format")
}
@@ -1422,6 +1488,153 @@
}
}

+func marshalOpenSSHPrivateKey(key crypto.PrivateKey, comment string, encrypt openSSHEncryptFunc) (*pem.Block, error) {
+ var w struct {
+ CipherName string
+ KdfName string
+ KdfOpts string
+ NumKeys uint32
+ PubKey []byte
+ PrivKeyBlock []byte
+ }
+ var pk1 struct {
+ Check1 uint32
+ Check2 uint32
+ Keytype string
+ Rest []byte `ssh:"rest"`
+ }
+
+ // Random check bytes.
+ var check uint32
+ if err := binary.Read(rand.Reader, binary.BigEndian, &check); err != nil {
+ return nil, err
+ }
+
+ pk1.Check1 = check
+ pk1.Check2 = check
+ w.NumKeys = 1
+
+ // Use a []byte directly on ed25519 keys.
+ if k, ok := key.(*ed25519.PrivateKey); ok {
+ key = *k
+ }
+
+ switch k := key.(type) {
+ case *rsa.PrivateKey:
+ E := new(big.Int).SetInt64(int64(k.PublicKey.E))
+ // Marshal public key:
+ // E and N are in reversed order in the public and private key.
+ pubKey := struct {
+ KeyType string
+ E *big.Int
+ N *big.Int
+ }{
+ KeyAlgoRSA,
+ E, k.PublicKey.N,
+ }
+ w.PubKey = Marshal(pubKey)
+
+ // Marshal private key.
+ key := struct {
+ N *big.Int
+ E *big.Int
+ D *big.Int
+ Iqmp *big.Int
+ P *big.Int
+ Q *big.Int
+ Comment string
+ }{
+ k.PublicKey.N, E,
+ k.D, k.Precomputed.Qinv, k.Primes[0], k.Primes[1],
+ comment,
+ }
+ pk1.Keytype = KeyAlgoRSA
+ pk1.Rest = Marshal(key)
+ case ed25519.PrivateKey:
+ pub := make([]byte, ed25519.PublicKeySize)
+ priv := make([]byte, ed25519.PrivateKeySize)
+ copy(pub, k[ed25519.PublicKeySize:])
+ copy(priv, k)
+
+ // Marshal public key.
+ pubKey := struct {
+ KeyType string
+ Pub []byte
+ }{
+ KeyAlgoED25519, pub,
+ }
+ w.PubKey = Marshal(pubKey)
+
+ // Marshal private key.
+ key := struct {
+ Pub []byte
+ Priv []byte
+ Comment string
+ }{
+ pub, priv,
+ comment,
+ }
+ pk1.Keytype = KeyAlgoED25519
+ pk1.Rest = Marshal(key)
+ case *ecdsa.PrivateKey:
+ var curve, keyType string
+ switch name := k.Curve.Params().Name; name {
+ case "P-256":
+ curve = "nistp256"
+ keyType = KeyAlgoECDSA256
+ case "P-384":
+ curve = "nistp384"
+ keyType = KeyAlgoECDSA384
+ case "P-521":
+ curve = "nistp521"
+ keyType = KeyAlgoECDSA521
+ default:
+ return nil, errors.New("ssh: unhandled elliptic curve " + name)
+ }
+
+ pub := elliptic.Marshal(k.Curve, k.PublicKey.X, k.PublicKey.Y)
+
+ // Marshal public key.
+ pubKey := struct {
+ KeyType string
+ Curve string
+ Pub []byte
+ }{
+ keyType, curve, pub,
+ }
+ w.PubKey = Marshal(pubKey)
+
+ // Marshal private key.
+ key := struct {
+ Curve string
+ Pub []byte
+ D *big.Int
+ Comment string
+ }{
+ curve, pub, k.D,
+ comment,
+ }
+ pk1.Keytype = keyType
+ pk1.Rest = Marshal(key)
+ default:
+ return nil, fmt.Errorf("ssh: unsupported key type %T", k)
+ }
+
+ var err error
+ // Add padding and encrypt the key if necessary.
+ w.PrivKeyBlock, w.CipherName, w.KdfName, w.KdfOpts, err = encrypt(Marshal(pk1))
+ if err != nil {
+ return nil, err
+ }
+
+ b := Marshal(w)
+ block := &pem.Block{
+ Type: "OPENSSH PRIVATE KEY",
+ Bytes: append([]byte(magic), b...),
+ }
+ return block, nil
+}
+
func checkOpenSSHKeyPadding(pad []byte) error {
for i, b := range pad {
if int(b) != i+1 {
@@ -1431,6 +1644,13 @@
return nil
}

+func generateOpenSSHPadding(block []byte, blockSize int) []byte {
+ for i, l := 0, len(block); (l+i)%blockSize != 0; i++ {
+ block = append(block, byte(i+1))
+ }
+ return block
+}
+
// FingerprintLegacyMD5 returns the user presentation of the key's
// fingerprint as described by RFC 4716 section 4.
func FingerprintLegacyMD5(pubKey PublicKey) string {
diff --git a/ssh/keys_test.go b/ssh/keys_test.go
index d64ef73..9d6f12f 100644
--- a/ssh/keys_test.go
+++ b/ssh/keys_test.go
@@ -281,6 +281,74 @@
}
}

+func TestMarshalPrivateKey(t *testing.T) {
+ tests := []struct {
+ name string
+ }{
+ {"rsa-openssh-format"},
+ {"ed25519"},
+ {"p256-openssh-format"},
+ {"p384-openssh-format"},
+ {"p521-openssh-format"},
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ expected, ok := testPrivateKeys[tt.name]
+ if !ok {
+ t.Fatalf("cannot find key %s", tt.name)
+ }
+
+ block, err := MarshalPrivateKey(expected, "te...@golang.org")
+ if err != nil {
+ t.Fatalf("cannot marshal %s: %v", tt.name, err)
+ }
+
+ key, err := ParseRawPrivateKey(pem.EncodeToMemory(block))
+ if err != nil {
+ t.Fatalf("cannot parse %s: %v", tt.name, err)
+ }
+
+ if !reflect.DeepEqual(expected, key) {
+ t.Errorf("unexpected marshaled key %s", tt.name)
+ }
+ })
+ }
+}
+
+func TestMarshalPrivateKeyWithPassphrase(t *testing.T) {
+ tests := []struct {
+ name string
+ }{
+ {"rsa-openssh-format"},
+ {"ed25519"},
+ {"p256-openssh-format"},
+ {"p384-openssh-format"},
+ {"p521-openssh-format"},
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ expected, ok := testPrivateKeys[tt.name]
+ if !ok {
+ t.Fatalf("cannot find key %s", tt.name)
+ }
+
+ block, err := MarshalPrivateKeyWithPassphrase(expected, "te...@golang.org", []byte("test-passphrase"))
+ if err != nil {
+ t.Fatalf("cannot marshal %s: %v", tt.name, err)
+ }
+
+ key, err := ParseRawPrivateKeyWithPassphrase(pem.EncodeToMemory(block), []byte("test-passphrase"))
+ if err != nil {
+ t.Fatalf("cannot parse %s: %v", tt.name, err)
+ }
+
+ if !reflect.DeepEqual(expected, key) {
+ t.Errorf("unexpected marshaled key %s", tt.name)
+ }
+ })
+ }
+}
+
type testAuthResult struct {
pubKey PublicKey
options []string

To view, visit change 218620. To unsubscribe, or for help writing mail filters, visit settings.

Gerrit-Project: crypto
Gerrit-Branch: master
Gerrit-Change-Id: I1a95301f789ce04858e6b147748c6e8b7700384b
Gerrit-Change-Number: 218620
Gerrit-PatchSet: 1
Gerrit-Owner: Mariano Cano <marian...@gmail.com>
Gerrit-MessageType: newchange

Gobot Gobot (Gerrit)

unread,
Feb 7, 2020, 11:27:22 PM2/7/20
to Mariano Cano, goph...@pubsubhelper.golang.org, golang-co...@googlegroups.com

Congratulations on opening your first change. Thank you for your contribution!

Next steps:
Within the next week or so, a maintainer will review your change and provide
feedback. See https://golang.org/doc/contribute.html#review for more info and
tips to get your patch through code review.

Most changes in the Go project go through a few rounds of revision. This can be
surprising to people new to the project. The careful, iterative review process
is our way of helping mentor contributors and ensuring that their contributions
have a lasting impact.

During May-July and Nov-Jan the Go project is in a code freeze, during which
little code gets reviewed or merged. If a reviewer responds with a comment like
R=go1.11, it means that this CL will be reviewed as part of the next development
cycle. See https://golang.org/s/release for more details.

View Change

    To view, visit change 218620. To unsubscribe, or for help writing mail filters, visit settings.

    Gerrit-Project: crypto
    Gerrit-Branch: master
    Gerrit-Change-Id: I1a95301f789ce04858e6b147748c6e8b7700384b
    Gerrit-Change-Number: 218620
    Gerrit-PatchSet: 1
    Gerrit-Owner: Mariano Cano <marian...@gmail.com>
    Gerrit-CC: Gobot Gobot <go...@golang.org>
    Gerrit-Comment-Date: Sat, 08 Feb 2020 04:27:19 +0000
    Gerrit-HasComments: No
    Gerrit-Has-Labels: No
    Gerrit-MessageType: comment

    Mariano Cano (Gerrit)

    unread,
    Feb 7, 2020, 11:31:45 PM2/7/20
    to goph...@pubsubhelper.golang.org, Filippo Valsorda, Han-Wen Nienhuys, Gobot Gobot, golang-co...@googlegroups.com

    Hi Filippo and Han-Wen,

    I've pushed some code to marshal private keys to the OpenSSH format.

    I think having full interoperability in terms of keys with OpenSSH would be a good enhancement for the x/crypto/ssh package.

    Salut

    View Change

      To view, visit change 218620. To unsubscribe, or for help writing mail filters, visit settings.

      Gerrit-Project: crypto
      Gerrit-Branch: master
      Gerrit-Change-Id: I1a95301f789ce04858e6b147748c6e8b7700384b
      Gerrit-Change-Number: 218620
      Gerrit-PatchSet: 1
      Gerrit-Owner: Mariano Cano <marian...@gmail.com>
      Gerrit-Reviewer: Filippo Valsorda <fil...@golang.org>
      Gerrit-Reviewer: Han-Wen Nienhuys <han...@google.com>
      Gerrit-CC: Gobot Gobot <go...@golang.org>
      Gerrit-Comment-Date: Sat, 08 Feb 2020 04:31:40 +0000

      Mariano Cano (Gerrit)

      unread,
      Aug 13, 2020, 4:00:05 PM8/13/20
      to goph...@pubsubhelper.golang.org, Katie Hockman, Filippo Valsorda, Han-Wen Nienhuys, Gobot Gobot, golang-co...@googlegroups.com

      View Change

      1 comment:

      To view, visit change 218620. To unsubscribe, or for help writing mail filters, visit settings.

      Gerrit-Project: crypto
      Gerrit-Branch: master
      Gerrit-Change-Id: I1a95301f789ce04858e6b147748c6e8b7700384b
      Gerrit-Change-Number: 218620
      Gerrit-PatchSet: 2
      Gerrit-Owner: Mariano Cano <marian...@gmail.com>
      Gerrit-Reviewer: Filippo Valsorda <fil...@golang.org>
      Gerrit-Reviewer: Han-Wen Nienhuys <han...@google.com>
      Gerrit-CC: Gobot Gobot <go...@golang.org>
      Gerrit-CC: Katie Hockman <ka...@golang.org>
      Gerrit-Comment-Date: Thu, 13 Aug 2020 19:59:59 +0000
      Gerrit-HasComments: Yes
      Gerrit-Has-Labels: No
      Gerrit-MessageType: comment

      Mariano Cano (Gerrit)

      unread,
      Feb 17, 2022, 2:48:39 PM2/17/22
      to goph...@pubsubhelper.golang.org, Alex Scheel, Katie Hockman, Filippo Valsorda, Han-Wen Nienhuys, Gopher Robot, golang-co...@googlegroups.com

      Attention is currently required from: Alex Scheel.

      View Change

      2 comments:

      • File ssh/keys.go:

        • Patch Set #2, Line 300: // MarshalPrivateKeyWithPassphrase returns an encrypted PEM block with the

          nit: the PEM block itself isn't encrypted (it is still plaintext). […]

          Do you mean changing the docs to something like:

          MarshalPrivateKeyWithPassphrase returns a PEM block holding the encrypted private key serialized in the OpenSSH format.

        • Patch Set #2, Line 1288: if err != nil {

          nit: I think convention elsewhere in the code base would be to handle this directly in passphrasePro […]

          Ack

      To view, visit change 218620. To unsubscribe, or for help writing mail filters, visit settings.

      Gerrit-Project: crypto
      Gerrit-Branch: master
      Gerrit-Change-Id: I1a95301f789ce04858e6b147748c6e8b7700384b
      Gerrit-Change-Number: 218620
      Gerrit-PatchSet: 2
      Gerrit-Owner: Mariano Cano <marian...@gmail.com>
      Gerrit-Reviewer: Alex Scheel <alex....@hashicorp.com>
      Gerrit-Reviewer: Filippo Valsorda <fil...@golang.org>
      Gerrit-Reviewer: Han-Wen Nienhuys <han...@google.com>
      Gerrit-CC: Gopher Robot <go...@golang.org>
      Gerrit-CC: Katie Hockman <ka...@golang.org>
      Gerrit-Attention: Alex Scheel <alex....@hashicorp.com>
      Gerrit-Comment-Date: Thu, 17 Feb 2022 19:48:35 +0000
      Gerrit-HasComments: Yes
      Gerrit-Has-Labels: No
      Comment-In-Reply-To: Alex Scheel <alex....@hashicorp.com>
      Gerrit-MessageType: comment

      Mariano Cano (Gerrit)

      unread,
      Feb 17, 2022, 9:45:08 PM2/17/22
      to goph...@pubsubhelper.golang.org, golang-co...@googlegroups.com

      Attention is currently required from: Mariano Cano.

      Mariano Cano uploaded patch set #3 to this change.

      View Change

      ssh: support for marshaling keys using the OpenSSH format

      This adds methods to marshal private keys, encrypted and unecntypted
      to the OpenSSH format.

      Fixes golang/go#37132

      Change-Id: I1a95301f789ce04858e6b147748c6e8b7700384b
      ---
      M ssh/keys.go
      M ssh/keys_test.go
      2 files changed, 294 insertions(+), 1 deletion(-)

      To view, visit change 218620. To unsubscribe, or for help writing mail filters, visit settings.

      Gerrit-Project: crypto
      Gerrit-Branch: master
      Gerrit-Change-Id: I1a95301f789ce04858e6b147748c6e8b7700384b
      Gerrit-Change-Number: 218620
      Gerrit-PatchSet: 3
      Gerrit-Owner: Mariano Cano <marian...@gmail.com>
      Gerrit-Reviewer: Alex Scheel <alex....@hashicorp.com>
      Gerrit-Reviewer: Filippo Valsorda <fil...@golang.org>
      Gerrit-Reviewer: Han-Wen Nienhuys <han...@google.com>
      Gerrit-CC: Gopher Robot <go...@golang.org>
      Gerrit-CC: Katie Hockman <ka...@golang.org>
      Gerrit-Attention: Mariano Cano <marian...@gmail.com>
      Gerrit-MessageType: newpatchset

      Mariano Cano (Gerrit)

      unread,
      Feb 17, 2022, 9:46:20 PM2/17/22
      to goph...@pubsubhelper.golang.org, Alex Scheel, Katie Hockman, Filippo Valsorda, Han-Wen Nienhuys, Gopher Robot, golang-co...@googlegroups.com

      Attention is currently required from: Alex Scheel.

      View Change

      2 comments:

      • File ssh/keys.go:

        • Patch Set #2, Line 300: // MarshalPrivateKeyWithPassphrase returns an encrypted PEM block with the

          Yeah, I think that makes sense. […]

          Done

        • Ack

          Done

      To view, visit change 218620. To unsubscribe, or for help writing mail filters, visit settings.

      Gerrit-Project: crypto
      Gerrit-Branch: master
      Gerrit-Change-Id: I1a95301f789ce04858e6b147748c6e8b7700384b
      Gerrit-Change-Number: 218620
      Gerrit-PatchSet: 3
      Gerrit-Owner: Mariano Cano <marian...@gmail.com>
      Gerrit-Reviewer: Alex Scheel <alex....@hashicorp.com>
      Gerrit-Reviewer: Filippo Valsorda <fil...@golang.org>
      Gerrit-Reviewer: Han-Wen Nienhuys <han...@google.com>
      Gerrit-CC: Gopher Robot <go...@golang.org>
      Gerrit-CC: Katie Hockman <ka...@golang.org>
      Gerrit-Attention: Alex Scheel <alex....@hashicorp.com>
      Gerrit-Comment-Date: Fri, 18 Feb 2022 02:46:16 +0000
      Gerrit-HasComments: Yes
      Gerrit-Has-Labels: No
      Comment-In-Reply-To: Alex Scheel <alex....@hashicorp.com>
      Comment-In-Reply-To: Mariano Cano <marian...@gmail.com>
      Gerrit-MessageType: comment

      Alex Scheel (Gerrit)

      unread,
      Feb 18, 2022, 1:08:27 PM2/18/22
      to Mariano Cano, goph...@pubsubhelper.golang.org, Katie Hockman, Filippo Valsorda, Han-Wen Nienhuys, Gopher Robot, golang-co...@googlegroups.com

      Attention is currently required from: Mariano Cano.

      View Change

      1 comment:

      • File ssh/keys.go:

        • Patch Set #2, Line 300: // MarshalPrivateKeyWithPassphrase returns an encrypted PEM block with the

          Do you mean changing the docs to something like: […]

        • Yeah, I think that makes sense.

      To view, visit change 218620. To unsubscribe, or for help writing mail filters, visit settings.

      Gerrit-Project: crypto
      Gerrit-Branch: master
      Gerrit-Change-Id: I1a95301f789ce04858e6b147748c6e8b7700384b
      Gerrit-Change-Number: 218620
      Gerrit-PatchSet: 2
      Gerrit-Owner: Mariano Cano <marian...@gmail.com>
      Gerrit-Reviewer: Alex Scheel <alex....@hashicorp.com>
      Gerrit-Reviewer: Filippo Valsorda <fil...@golang.org>
      Gerrit-Reviewer: Han-Wen Nienhuys <han...@google.com>
      Gerrit-CC: Gopher Robot <go...@golang.org>
      Gerrit-CC: Katie Hockman <ka...@golang.org>
      Gerrit-Attention: Mariano Cano <marian...@gmail.com>
      Gerrit-Comment-Date: Thu, 17 Feb 2022 20:10:32 +0000

      Alex Scheel (Gerrit)

      unread,
      Feb 18, 2022, 1:08:28 PM2/18/22
      to Mariano Cano, goph...@pubsubhelper.golang.org, Katie Hockman, Filippo Valsorda, Han-Wen Nienhuys, Gopher Robot, golang-co...@googlegroups.com

      Attention is currently required from: Mariano Cano.

      Patch set 3:Code-Review +1

      View Change

        To view, visit change 218620. To unsubscribe, or for help writing mail filters, visit settings.

        Gerrit-Project: crypto
        Gerrit-Branch: master
        Gerrit-Change-Id: I1a95301f789ce04858e6b147748c6e8b7700384b
        Gerrit-Change-Number: 218620
        Gerrit-PatchSet: 3
        Gerrit-Owner: Mariano Cano <marian...@gmail.com>
        Gerrit-Reviewer: Alex Scheel <alex....@hashicorp.com>
        Gerrit-Reviewer: Filippo Valsorda <fil...@golang.org>
        Gerrit-Reviewer: Han-Wen Nienhuys <han...@google.com>
        Gerrit-CC: Gopher Robot <go...@golang.org>
        Gerrit-CC: Katie Hockman <ka...@golang.org>
        Gerrit-Attention: Mariano Cano <marian...@gmail.com>
        Gerrit-Comment-Date: Fri, 18 Feb 2022 13:58:01 +0000
        Gerrit-HasComments: No
        Gerrit-Has-Labels: Yes
        Gerrit-MessageType: comment

        Alex Scheel (Gerrit)

        unread,
        Feb 18, 2022, 1:08:28 PM2/18/22
        to Mariano Cano, goph...@pubsubhelper.golang.org, Katie Hockman, Filippo Valsorda, Han-Wen Nienhuys, Gopher Robot, golang-co...@googlegroups.com

        Attention is currently required from: Mariano Cano.

        View Change

        1 comment:

        To view, visit change 218620. To unsubscribe, or for help writing mail filters, visit settings.

        Gerrit-Project: crypto
        Gerrit-Branch: master
        Gerrit-Change-Id: I1a95301f789ce04858e6b147748c6e8b7700384b
        Gerrit-Change-Number: 218620
        Gerrit-PatchSet: 3
        Gerrit-Owner: Mariano Cano <marian...@gmail.com>
        Gerrit-Reviewer: Alex Scheel <alex....@hashicorp.com>
        Gerrit-Reviewer: Filippo Valsorda <fil...@golang.org>
        Gerrit-Reviewer: Han-Wen Nienhuys <han...@google.com>
        Gerrit-CC: Gopher Robot <go...@golang.org>
        Gerrit-CC: Katie Hockman <ka...@golang.org>
        Gerrit-Attention: Mariano Cano <marian...@gmail.com>
        Gerrit-Comment-Date: Fri, 18 Feb 2022 13:57:12 +0000

        Alex Scheel (Gerrit)

        unread,
        Feb 18, 2022, 1:08:28 PM2/18/22
        to Mariano Cano, goph...@pubsubhelper.golang.org, Katie Hockman, Filippo Valsorda, Han-Wen Nienhuys, Gopher Robot, golang-co...@googlegroups.com

        Attention is currently required from: Mariano Cano.

        Patch set 2:Code-Review +1

        View Change

        3 comments:

        • Patchset:

          • Patch Set #2:

            Not part of the Golang team; just a drive by review of two nits with the proposed patch.

        • File ssh/keys.go:

          • Patch Set #2, Line 300: // MarshalPrivateKeyWithPassphrase returns an encrypted PEM block with the

            nit: the PEM block itself isn't encrypted (it is still plaintext). The serialized format contains an encrypted private key using the specified KDF/...

          • nit: I think convention elsewhere in the code base would be to handle this directly in passphraseProtectedOpenSSHMarshaler, like e.g. mime/multipart/writer.go or crypto/tls/key_schedule.go. Especially since it appears to only be used once?

        To view, visit change 218620. To unsubscribe, or for help writing mail filters, visit settings.

        Gerrit-Project: crypto
        Gerrit-Branch: master
        Gerrit-Change-Id: I1a95301f789ce04858e6b147748c6e8b7700384b
        Gerrit-Change-Number: 218620
        Gerrit-PatchSet: 2
        Gerrit-Owner: Mariano Cano <marian...@gmail.com>
        Gerrit-Reviewer: Alex Scheel <alex....@hashicorp.com>
        Gerrit-Reviewer: Filippo Valsorda <fil...@golang.org>
        Gerrit-Reviewer: Han-Wen Nienhuys <han...@google.com>
        Gerrit-CC: Gopher Robot <go...@golang.org>
        Gerrit-CC: Katie Hockman <ka...@golang.org>
        Gerrit-Attention: Mariano Cano <marian...@gmail.com>
        Gerrit-Comment-Date: Thu, 17 Feb 2022 19:03:15 +0000
        Gerrit-HasComments: Yes
        Gerrit-Has-Labels: Yes
        Gerrit-MessageType: comment

        Mariano Cano (Gerrit)

        unread,
        Feb 19, 2022, 1:57:26 AM2/19/22
        to goph...@pubsubhelper.golang.org, Alex Scheel, Katie Hockman, Filippo Valsorda, Han-Wen Nienhuys, Gopher Robot, golang-co...@googlegroups.com

        Attention is currently required from: Alex Scheel.

        View Change

        1 comment:

        • Patchset:

          • Patch Set #2:

            Not part of the Golang team; just a drive by review of two nits with the proposed patch.

          • Ack

        To view, visit change 218620. To unsubscribe, or for help writing mail filters, visit settings.

        Gerrit-Project: crypto
        Gerrit-Branch: master
        Gerrit-Change-Id: I1a95301f789ce04858e6b147748c6e8b7700384b
        Gerrit-Change-Number: 218620
        Gerrit-PatchSet: 3
        Gerrit-Owner: Mariano Cano <marian...@gmail.com>
        Gerrit-Reviewer: Alex Scheel <alex....@hashicorp.com>
        Gerrit-Reviewer: Filippo Valsorda <fil...@golang.org>
        Gerrit-Reviewer: Han-Wen Nienhuys <han...@google.com>
        Gerrit-CC: Gopher Robot <go...@golang.org>
        Gerrit-CC: Katie Hockman <ka...@golang.org>
        Gerrit-Attention: Alex Scheel <alex....@hashicorp.com>
        Gerrit-Comment-Date: Sat, 19 Feb 2022 06:57:22 +0000
        Gerrit-HasComments: Yes
        Gerrit-Has-Labels: No
        Comment-In-Reply-To: Alex Scheel <alex....@hashicorp.com>
        Gerrit-MessageType: comment

        Ivan De Marino (Gerrit)

        unread,
        Feb 22, 2022, 12:32:53 AM2/22/22
        to Mariano Cano, goph...@pubsubhelper.golang.org, Alex Scheel, Katie Hockman, Filippo Valsorda, Gopher Robot, golang-co...@googlegroups.com

        Attention is currently required from: Alex Scheel, Mariano Cano.

        Patch set 3:Code-Review +1

        View Change

        1 comment:

        • Patchset:

          • Patch Set #3:

            Hello. Just wanted to say that at HashiCorp we are working on an update to a Terraform Provider (`terraform-provider-tls`) and we have "cherry-picked" this change and temporarily included it in our codebase (https://github.com/hashicorp/terraform-provider-tls/pull/151).

            The plan is to rip it out once a version of `x/crypto/ssh` with this change applied is released.

        To view, visit change 218620. To unsubscribe, or for help writing mail filters, visit settings.

        Gerrit-Project: crypto
        Gerrit-Branch: master
        Gerrit-Change-Id: I1a95301f789ce04858e6b147748c6e8b7700384b
        Gerrit-Change-Number: 218620
        Gerrit-PatchSet: 3
        Gerrit-Owner: Mariano Cano <marian...@gmail.com>
        Gerrit-Reviewer: Alex Scheel <alex....@hashicorp.com>
        Gerrit-Reviewer: Filippo Valsorda <fil...@golang.org>
        Gerrit-Reviewer: Ivan De Marino <ivan.d...@hashicorp.com>
        Gerrit-CC: Gopher Robot <go...@golang.org>
        Gerrit-CC: Katie Hockman <ka...@golang.org>
        Gerrit-Attention: Alex Scheel <alex....@hashicorp.com>
        Gerrit-Attention: Mariano Cano <marian...@gmail.com>
        Gerrit-Comment-Date: Mon, 21 Feb 2022 17:22:24 +0000

        Matt Layher (Gerrit)

        unread,
        Feb 22, 2022, 6:30:19 AM2/22/22
        to Mariano Cano, goph...@pubsubhelper.golang.org, Ivan De Marino, Alex Scheel, Katie Hockman, Filippo Valsorda, Gopher Robot, golang-co...@googlegroups.com

        Attention is currently required from: Alex Scheel, Mariano Cano.

        Patch set 3:Run-TryBot +1Code-Review +1Trust +1

        View Change

        2 comments:

        • Commit Message:

        • Patchset:

          • Patch Set #3:

            LGTM pending typo fix from a non-cryptogopher perspective.

        To view, visit change 218620. To unsubscribe, or for help writing mail filters, visit settings.

        Gerrit-Project: crypto
        Gerrit-Branch: master
        Gerrit-Change-Id: I1a95301f789ce04858e6b147748c6e8b7700384b
        Gerrit-Change-Number: 218620
        Gerrit-PatchSet: 3
        Gerrit-Owner: Mariano Cano <marian...@gmail.com>
        Gerrit-Reviewer: Alex Scheel <alex....@hashicorp.com>
        Gerrit-Reviewer: Filippo Valsorda <fil...@golang.org>
        Gerrit-Reviewer: Ivan De Marino <ivan.d...@hashicorp.com>
        Gerrit-Reviewer: Matt Layher <mdla...@gmail.com>
        Gerrit-CC: Gopher Robot <go...@golang.org>
        Gerrit-CC: Katie Hockman <ka...@golang.org>
        Gerrit-Attention: Alex Scheel <alex....@hashicorp.com>
        Gerrit-Attention: Mariano Cano <marian...@gmail.com>
        Gerrit-Comment-Date: Tue, 22 Feb 2022 11:30:13 +0000

        Mariano Cano (Gerrit)

        unread,
        Feb 25, 2022, 7:00:20 PM2/25/22
        to goph...@pubsubhelper.golang.org, golang-co...@googlegroups.com

        Attention is currently required from: Alex Scheel, Mariano Cano.

        Mariano Cano uploaded patch set #4 to this change.

        View Change

        ssh: support for marshaling keys using the OpenSSH format

        This adds methods to marshal private keys, encrypted and unencrypted

        to the OpenSSH format.

        Fixes golang/go#37132

        Change-Id: I1a95301f789ce04858e6b147748c6e8b7700384b
        ---
        M ssh/keys.go
        M ssh/keys_test.go
        2 files changed, 294 insertions(+), 1 deletion(-)

        To view, visit change 218620. To unsubscribe, or for help writing mail filters, visit settings.

        Gerrit-Project: crypto
        Gerrit-Branch: master
        Gerrit-Change-Id: I1a95301f789ce04858e6b147748c6e8b7700384b
        Gerrit-Change-Number: 218620
        Gerrit-PatchSet: 4
        Gerrit-Owner: Mariano Cano <marian...@gmail.com>
        Gerrit-Reviewer: Alex Scheel <alex....@hashicorp.com>
        Gerrit-Reviewer: Filippo Valsorda <fil...@golang.org>
        Gerrit-Reviewer: Gopher Robot <go...@golang.org>
        Gerrit-Reviewer: Ivan De Marino <ivan.d...@hashicorp.com>
        Gerrit-Reviewer: Matt Layher <mdla...@gmail.com>
        Gerrit-CC: Katie Hockman <ka...@golang.org>
        Gerrit-Attention: Alex Scheel <alex....@hashicorp.com>
        Gerrit-Attention: Mariano Cano <marian...@gmail.com>
        Gerrit-MessageType: newpatchset

        Mariano Cano (Gerrit)

        unread,
        Feb 25, 2022, 7:01:40 PM2/25/22
        to goph...@pubsubhelper.golang.org, Gopher Robot, Matt Layher, Ivan De Marino, Alex Scheel, Katie Hockman, Filippo Valsorda, golang-co...@googlegroups.com

        Attention is currently required from: Alex Scheel, Matt Layher.

        View Change

        1 comment:

        • Commit Message:

          • Patch Set #3, Line 9: This adds methods to marshal private keys, encrypted and unecntypted

            *unencrypted

            Done

        To view, visit change 218620. To unsubscribe, or for help writing mail filters, visit settings.

        Gerrit-Project: crypto
        Gerrit-Branch: master
        Gerrit-Change-Id: I1a95301f789ce04858e6b147748c6e8b7700384b
        Gerrit-Change-Number: 218620
        Gerrit-PatchSet: 4
        Gerrit-Owner: Mariano Cano <marian...@gmail.com>
        Gerrit-Reviewer: Alex Scheel <alex....@hashicorp.com>
        Gerrit-Reviewer: Filippo Valsorda <fil...@golang.org>
        Gerrit-Reviewer: Gopher Robot <go...@golang.org>
        Gerrit-Reviewer: Ivan De Marino <ivan.d...@hashicorp.com>
        Gerrit-Reviewer: Matt Layher <mdla...@gmail.com>
        Gerrit-CC: Katie Hockman <ka...@golang.org>
        Gerrit-Attention: Alex Scheel <alex....@hashicorp.com>
        Gerrit-Attention: Matt Layher <mdla...@gmail.com>
        Gerrit-Comment-Date: Sat, 26 Feb 2022 00:01:36 +0000
        Gerrit-HasComments: Yes
        Gerrit-Has-Labels: No
        Comment-In-Reply-To: Matt Layher <mdla...@gmail.com>
        Gerrit-MessageType: comment

        Alex Scheel (Gerrit)

        unread,
        Mar 2, 2022, 9:04:31 AM3/2/22
        to Mariano Cano, goph...@pubsubhelper.golang.org, Gopher Robot, Matt Layher, Ivan De Marino, Katie Hockman, Filippo Valsorda, golang-co...@googlegroups.com

        Attention is currently required from: Matt Layher.

        Patch set 4:Code-Review +1

        View Change

          To view, visit change 218620. To unsubscribe, or for help writing mail filters, visit settings.

          Gerrit-Project: crypto
          Gerrit-Branch: master
          Gerrit-Change-Id: I1a95301f789ce04858e6b147748c6e8b7700384b
          Gerrit-Change-Number: 218620
          Gerrit-PatchSet: 4
          Gerrit-Owner: Mariano Cano <marian...@gmail.com>
          Gerrit-Reviewer: Alex Scheel <alex....@hashicorp.com>
          Gerrit-Reviewer: Filippo Valsorda <fil...@golang.org>
          Gerrit-Reviewer: Gopher Robot <go...@golang.org>
          Gerrit-Reviewer: Ivan De Marino <ivan.d...@hashicorp.com>
          Gerrit-Reviewer: Matt Layher <mdla...@gmail.com>
          Gerrit-CC: Katie Hockman <ka...@golang.org>
          Gerrit-Attention: Matt Layher <mdla...@gmail.com>
          Gerrit-Comment-Date: Wed, 02 Mar 2022 14:04:27 +0000

          Carlos Alexandro Becker (Gerrit)

          unread,
          Mar 8, 2022, 9:20:09 AM3/8/22
          to Mariano Cano, goph...@pubsubhelper.golang.org, Alex Scheel, Gopher Robot, Matt Layher, Ivan De Marino, Katie Hockman, Filippo Valsorda, golang-co...@googlegroups.com

          Attention is currently required from: Matt Layher, Mariano Cano.

          View Change

          3 comments:

          • Patchset:

            • Patch Set #4:

              Hey, great patch!

              Did a couple of comments, but I'm not a Go team member, just poking around ed25519 keys and found this patch, so take them with a grain of salt.

              Thanks!

          • File ssh/keys.go:

            • Patch Set #4, Line 1294: func unencryptedOpenSSHMarshaler(PrivKeyBlock []byte) ([]byte, string, string, string, error) {

              I think the convention would be to `PrivKeyBlock` start with a lowercase letter.

              The same issue happens in other places with this var name as well.

          • File ssh/keys_test.go:

            • Patch Set #4, Line 335: block, err := MarshalPrivateKeyWithPassphrase(expected, "te...@golang.org", []byte("test-passphrase"))

              should we also test the output is the same as the one generated by OpenSSH's `ssh-keygen`?

              I tested this changeset on a ed25519 key:

              • created it with `ssh-keygen`
              • parsed with Go's ssh package
              • marshalled it back with this changeset

              The resulting private key is a bit different.
              More specifically, the line breaks are on different places, not sure how bad that is.

          To view, visit change 218620. To unsubscribe, or for help writing mail filters, visit settings.

          Gerrit-Project: crypto
          Gerrit-Branch: master
          Gerrit-Change-Id: I1a95301f789ce04858e6b147748c6e8b7700384b
          Gerrit-Change-Number: 218620
          Gerrit-PatchSet: 4
          Gerrit-Owner: Mariano Cano <marian...@gmail.com>
          Gerrit-Reviewer: Alex Scheel <alex....@hashicorp.com>
          Gerrit-Reviewer: Filippo Valsorda <fil...@golang.org>
          Gerrit-Reviewer: Gopher Robot <go...@golang.org>
          Gerrit-Reviewer: Ivan De Marino <ivan.d...@hashicorp.com>
          Gerrit-Reviewer: Matt Layher <mdla...@gmail.com>
          Gerrit-CC: Carlos Alexandro Becker <caar...@gmail.com>
          Gerrit-CC: Katie Hockman <ka...@golang.org>
          Gerrit-Attention: Matt Layher <mdla...@gmail.com>
          Gerrit-Attention: Mariano Cano <marian...@gmail.com>
          Gerrit-Comment-Date: Tue, 08 Mar 2022 14:20:04 +0000
          Gerrit-HasComments: Yes
          Gerrit-Has-Labels: No
          Gerrit-MessageType: comment

          Mariano Cano (Gerrit)

          unread,
          Mar 12, 2022, 6:52:21 PM3/12/22
          to goph...@pubsubhelper.golang.org, golang-co...@googlegroups.com

          Attention is currently required from: Matt Layher, Mariano Cano.

          Mariano Cano uploaded patch set #5 to this change.

          View Change

          ssh: support for marshaling keys using the OpenSSH format

          This adds methods to marshal private keys, encrypted and unencrypted

          to the OpenSSH format.

          Fixes golang/go#37132

          Change-Id: I1a95301f789ce04858e6b147748c6e8b7700384b
          ---
          M ssh/keys.go
          M ssh/keys_test.go
          2 files changed, 294 insertions(+), 1 deletion(-)

          To view, visit change 218620. To unsubscribe, or for help writing mail filters, visit settings.

          Gerrit-Project: crypto
          Gerrit-Branch: master
          Gerrit-Change-Id: I1a95301f789ce04858e6b147748c6e8b7700384b
          Gerrit-Change-Number: 218620
          Gerrit-PatchSet: 5
          Gerrit-Owner: Mariano Cano <marian...@gmail.com>
          Gerrit-Reviewer: Alex Scheel <alex....@hashicorp.com>
          Gerrit-Reviewer: Filippo Valsorda <fil...@golang.org>
          Gerrit-Reviewer: Gopher Robot <go...@golang.org>
          Gerrit-Reviewer: Ivan De Marino <ivan.d...@hashicorp.com>
          Gerrit-Reviewer: Matt Layher <mdla...@gmail.com>
          Gerrit-CC: Carlos Alexandro Becker <caar...@gmail.com>
          Gerrit-CC: Katie Hockman <ka...@golang.org>
          Gerrit-Attention: Matt Layher <mdla...@gmail.com>
          Gerrit-Attention: Mariano Cano <marian...@gmail.com>
          Gerrit-MessageType: newpatchset

          Mariano Cano (Gerrit)

          unread,
          Mar 12, 2022, 6:53:49 PM3/12/22
          to goph...@pubsubhelper.golang.org, Carlos Alexandro Becker, Alex Scheel, Gopher Robot, Matt Layher, Ivan De Marino, Katie Hockman, Filippo Valsorda, golang-co...@googlegroups.com

          Attention is currently required from: Carlos Alexandro Becker, Matt Layher.

          View Change

          3 comments:

          • Patchset:

          • File ssh/keys.go:

            • Patch Set #4, Line 1294: func unencryptedOpenSSHMarshaler(PrivKeyBlock []byte) ([]byte, string, string, string, error) {

            • I think the convention would be to `PrivKeyBlock` start with a lowercase letter. […]

              Ack

          • File ssh/keys_test.go:

            • should we also test the output is the same as the one generated by OpenSSH's `ssh-keygen`? […]

              The methods added in this pull request return a *pem.Block, that is basically a type for the header and the bytes, how are they encoded later depends on the implementation of encoding/pem.

          To view, visit change 218620. To unsubscribe, or for help writing mail filters, visit settings.

          Gerrit-Project: crypto
          Gerrit-Branch: master
          Gerrit-Change-Id: I1a95301f789ce04858e6b147748c6e8b7700384b
          Gerrit-Change-Number: 218620
          Gerrit-PatchSet: 5
          Gerrit-Owner: Mariano Cano <marian...@gmail.com>
          Gerrit-Reviewer: Alex Scheel <alex....@hashicorp.com>
          Gerrit-Reviewer: Filippo Valsorda <fil...@golang.org>
          Gerrit-Reviewer: Gopher Robot <go...@golang.org>
          Gerrit-Reviewer: Ivan De Marino <ivan.d...@hashicorp.com>
          Gerrit-Reviewer: Matt Layher <mdla...@gmail.com>
          Gerrit-CC: Carlos Alexandro Becker <caar...@gmail.com>
          Gerrit-CC: Katie Hockman <ka...@golang.org>
          Gerrit-Attention: Carlos Alexandro Becker <caar...@gmail.com>
          Gerrit-Attention: Matt Layher <mdla...@gmail.com>
          Gerrit-Comment-Date: Sat, 12 Mar 2022 23:53:45 +0000
          Gerrit-HasComments: Yes
          Gerrit-Has-Labels: No
          Comment-In-Reply-To: Carlos Alexandro Becker <caar...@gmail.com>
          Gerrit-MessageType: comment

          Ian Lance Taylor (Gerrit)

          unread,
          Sep 26, 2022, 10:26:35 AM9/26/22
          to Mariano Cano, goph...@pubsubhelper.golang.org, Roland Shoemaker, Ian Lance Taylor, Carlos Alexandro Becker, Alex Scheel, Gopher Robot, Matt Layher, Ivan De Marino, Katie Hockman, Filippo Valsorda, golang-co...@googlegroups.com

          Attention is currently required from: Carlos Alexandro Becker, Mariano Cano, Matt Layher, Roland Shoemaker.

          View Change

          1 comment:

          To view, visit change 218620. To unsubscribe, or for help writing mail filters, visit settings.

          Gerrit-Project: crypto
          Gerrit-Branch: master
          Gerrit-Change-Id: I1a95301f789ce04858e6b147748c6e8b7700384b
          Gerrit-Change-Number: 218620
          Gerrit-PatchSet: 5
          Gerrit-Owner: Mariano Cano <marian...@gmail.com>
          Gerrit-Reviewer: Alex Scheel <alex....@hashicorp.com>
          Gerrit-Reviewer: Filippo Valsorda <fil...@golang.org>
          Gerrit-Reviewer: Gopher Robot <go...@golang.org>
          Gerrit-Reviewer: Ivan De Marino <ivan.d...@hashicorp.com>
          Gerrit-Reviewer: Matt Layher <mdla...@gmail.com>
          Gerrit-Reviewer: Roland Shoemaker <rol...@golang.org>
          Gerrit-CC: Carlos Alexandro Becker <caar...@gmail.com>
          Gerrit-CC: Ian Lance Taylor <ia...@golang.org>
          Gerrit-CC: Katie Hockman <ka...@golang.org>
          Gerrit-Attention: Carlos Alexandro Becker <caar...@gmail.com>
          Gerrit-Attention: Matt Layher <mdla...@gmail.com>
          Gerrit-Attention: Roland Shoemaker <rol...@golang.org>
          Gerrit-Attention: Mariano Cano <marian...@gmail.com>
          Gerrit-Comment-Date: Mon, 26 Sep 2022 14:26:31 +0000
          Gerrit-HasComments: Yes
          Gerrit-Has-Labels: No
          Gerrit-MessageType: comment

          Roland Shoemaker (Gerrit)

          unread,
          Sep 26, 2022, 11:19:34 AM9/26/22
          to Mariano Cano, goph...@pubsubhelper.golang.org, Ian Lance Taylor, Carlos Alexandro Becker, Alex Scheel, Gopher Robot, Matt Layher, Ivan De Marino, Katie Hockman, Filippo Valsorda, golang-co...@googlegroups.com

          Attention is currently required from: Carlos Alexandro Becker, Mariano Cano, Matt Layher.

          View Change

          9 comments:

            • iv := k[32 : 32+aes.BlockSize]

            • 		block, err := aes.NewCipher(k[:32])

            • This and the below slice operation are slightly more readable by doing `key, iv := k[:32], k[32:]`, as is done in `passphraseProtectedOpenSSHKey`.

            • Patch Set #5, Line 1335: const magic = "openssh-key-v1\x00"

              Change this to `privateKeyAuthMagic` and add a comment indicating this is the header prefix indicating a private key block.

            • Patch Set #5, Line 1512:

              var w struct {
              CipherName string
              KdfName string
              KdfOpts string
              NumKeys uint32
              PubKey []byte
              PrivKeyBlock []byte
              }
              var pk1 struct {
              Check1 uint32
              Check2 uint32
              Keytype string
              Rest []byte `ssh:"rest"`
              }

              These structs are also used in parseOpenSSHPrivateKey, might want to just lift them out so that the same types are used in both places.

            • Patch Set #5, Line 1537:

            • // Use a []byte directly on ed25519 keys.

            • 	if k, ok := key.(*ed25519.PrivateKey); ok {

            • 		key = *k
              }

              This can be skipped, and the key can be directly deref'd in the switch below.

            • Patch Set #5, Line 1558:

              struct {
              N *big.Int
              E *big.Int
              D *big.Int
              Iqmp *big.Int
              P *big.Int
              Q *big.Int
              Comment string
              }

              Similarly, through this function we are duplicating types from parseOpenSSHPrivateKey.

            • Patch Set #5, Line 1576: ed25519.PublicKeySize:

              While this is technically correct, it is somewhat confusing to the casual reader since it appears you are reading _past_ the public key (this works because `PrivateKeySize = 2*PublicKeySize`). Might be (weirdly) more readable as just `32`.

            • Patch Set #5, Line 1576:

              copy(pub, k[ed25519.PublicKeySize:])
              copy(priv, k)

              i.e. this can be

                  copy(pub, (*k)[ed25519.PublicKeySize:])
              copy(priv, *k)
            • Patch Set #5, Line 1667:

            • func generateOpenSSHPadding(block []byte, blockSize int) []byte {

            • 	for i, l := 0, len(block); (l+i)%blockSize != 0; i++ {

            • 		block = append(block, byte(i+1))
              }
              return block
              }

              Padding can be generated in one shot as

            •     func generateOpenSSHPadding(block []byte, blockSize int) []byte {
            •       paddingNeeded := blockSize - (len(block) % blockSize)
              if paddingNeeded > 0 {
              block = append(block, make([]byte, paddingNeeded)...)
              }
              return block
              }

          To view, visit change 218620. To unsubscribe, or for help writing mail filters, visit settings.

          Gerrit-Project: crypto
          Gerrit-Branch: master
          Gerrit-Change-Id: I1a95301f789ce04858e6b147748c6e8b7700384b
          Gerrit-Change-Number: 218620
          Gerrit-PatchSet: 5
          Gerrit-Owner: Mariano Cano <marian...@gmail.com>
          Gerrit-Reviewer: Alex Scheel <alex....@hashicorp.com>
          Gerrit-Reviewer: Filippo Valsorda <fil...@golang.org>
          Gerrit-Reviewer: Gopher Robot <go...@golang.org>
          Gerrit-Reviewer: Ivan De Marino <ivan.d...@hashicorp.com>
          Gerrit-Reviewer: Matt Layher <mdla...@gmail.com>
          Gerrit-Reviewer: Roland Shoemaker <rol...@golang.org>
          Gerrit-CC: Carlos Alexandro Becker <caar...@gmail.com>
          Gerrit-CC: Ian Lance Taylor <ia...@golang.org>
          Gerrit-CC: Katie Hockman <ka...@golang.org>
          Gerrit-Attention: Carlos Alexandro Becker <caar...@gmail.com>
          Gerrit-Attention: Matt Layher <mdla...@gmail.com>
          Gerrit-Attention: Mariano Cano <marian...@gmail.com>
          Gerrit-Comment-Date: Mon, 26 Sep 2022 15:19:29 +0000

          Roland Shoemaker (Gerrit)

          unread,
          Sep 26, 2022, 11:23:11 AM9/26/22
          to Mariano Cano, goph...@pubsubhelper.golang.org, Ian Lance Taylor, Carlos Alexandro Becker, Alex Scheel, Gopher Robot, Matt Layher, Ivan De Marino, Katie Hockman, Filippo Valsorda, golang-co...@googlegroups.com

          Attention is currently required from: Carlos Alexandro Becker, Mariano Cano, Matt Layher.

          View Change

          1 comment:

            • func generateOpenSSHPadding(block []byte, blockSize int) []byte {

            • 	for i, l := 0, len(block); (l+i)%blockSize != 0; i++ {

            • 		block = append(block, byte(i+1))
              }
              return block
              }

            • Padding can be generated in one shot as […]

              Oh never mind, I forgot the weird OpenSSH padding scheme 🤦.

          To view, visit change 218620. To unsubscribe, or for help writing mail filters, visit settings.

          Gerrit-Project: crypto
          Gerrit-Branch: master
          Gerrit-Change-Id: I1a95301f789ce04858e6b147748c6e8b7700384b
          Gerrit-Change-Number: 218620
          Gerrit-PatchSet: 5
          Gerrit-Owner: Mariano Cano <marian...@gmail.com>
          Gerrit-Reviewer: Alex Scheel <alex....@hashicorp.com>
          Gerrit-Reviewer: Filippo Valsorda <fil...@golang.org>
          Gerrit-Reviewer: Gopher Robot <go...@golang.org>
          Gerrit-Reviewer: Ivan De Marino <ivan.d...@hashicorp.com>
          Gerrit-Reviewer: Matt Layher <mdla...@gmail.com>
          Gerrit-Reviewer: Roland Shoemaker <rol...@golang.org>
          Gerrit-CC: Carlos Alexandro Becker <caar...@gmail.com>
          Gerrit-CC: Ian Lance Taylor <ia...@golang.org>
          Gerrit-CC: Katie Hockman <ka...@golang.org>
          Gerrit-Attention: Carlos Alexandro Becker <caar...@gmail.com>
          Gerrit-Attention: Matt Layher <mdla...@gmail.com>
          Gerrit-Attention: Mariano Cano <marian...@gmail.com>
          Gerrit-Comment-Date: Mon, 26 Sep 2022 15:23:07 +0000
          Gerrit-HasComments: Yes
          Gerrit-Has-Labels: No
          Comment-In-Reply-To: Roland Shoemaker <rol...@golang.org>
          Gerrit-MessageType: comment

          Mariano Cano (Gerrit)

          unread,
          Sep 27, 2022, 9:09:07 PM9/27/22
          to goph...@pubsubhelper.golang.org, golang-co...@googlegroups.com

          Attention is currently required from: Carlos Alexandro Becker, Mariano Cano.

          Mariano Cano uploaded patch set #6 to this change.

          View Change

          ssh: support for marshaling keys using the OpenSSH format

          This adds methods to marshal private keys, encrypted and unencrypted

          to the OpenSSH format.

          Fixes golang/go#37132

          Change-Id: I1a95301f789ce04858e6b147748c6e8b7700384b
          ---
          M ssh/keys.go
          M ssh/keys_test.go
          2 files changed, 322 insertions(+), 46 deletions(-)

          To view, visit change 218620. To unsubscribe, or for help writing mail filters, visit settings.

          Gerrit-Project: crypto
          Gerrit-Branch: master
          Gerrit-Change-Id: I1a95301f789ce04858e6b147748c6e8b7700384b
          Gerrit-Change-Number: 218620
          Gerrit-PatchSet: 6
          Gerrit-Owner: Mariano Cano <marian...@gmail.com>
          Gerrit-Reviewer: Alex Scheel <alex....@hashicorp.com>
          Gerrit-Reviewer: Filippo Valsorda <fil...@golang.org>
          Gerrit-Reviewer: Gopher Robot <go...@golang.org>
          Gerrit-Reviewer: Ivan De Marino <ivan.d...@hashicorp.com>
          Gerrit-Reviewer: Roland Shoemaker <rol...@golang.org>
          Gerrit-CC: Carlos Alexandro Becker <caar...@gmail.com>
          Gerrit-CC: Ian Lance Taylor <ia...@golang.org>
          Gerrit-CC: Katie Hockman <ka...@golang.org>
          Gerrit-Attention: Carlos Alexandro Becker <caar...@gmail.com>

          Mariano Cano (Gerrit)

          unread,
          Sep 27, 2022, 9:09:50 PM9/27/22
          to goph...@pubsubhelper.golang.org, Roland Shoemaker, Ian Lance Taylor, Carlos Alexandro Becker, Alex Scheel, Gopher Robot, Ivan De Marino, Katie Hockman, Filippo Valsorda, golang-co...@googlegroups.com

          Attention is currently required from: Carlos Alexandro Becker, Roland Shoemaker.

          View Change

          8 comments:

            • nit: this can just be `rand.Read(salt)`.

            • Ack

            • iv := k[32 : 32+aes.BlockSize]


            • block, err := aes.NewCipher(k[:32])

            • This and the below slice operation are slightly more readable by doing `key, iv := k[:32], k[32:]`, […]

              Ack

            • Change this to `privateKeyAuthMagic` and add a comment indicating this is the header prefix indicati […]

              Ack

            • Patch Set #5, Line 1512:

              var w struct {
              CipherName string
              KdfName string
              KdfOpts string
              NumKeys uint32
              PubKey []byte
              PrivKeyBlock []byte
              }
              var pk1 struct {
              Check1 uint32
              Check2 uint32
              Keytype string
              Rest []byte `ssh:"rest"`
              }

            • These structs are also used in parseOpenSSHPrivateKey, might want to just lift them out so that the […]

              Ack

            • // Use a []byte directly on ed25519 keys.


            • if k, ok := key.(*ed25519.PrivateKey); ok {

            • 		key = *k
              }

            • This can be skipped, and the key can be directly deref'd in the switch below.

            • I don't think it is a good idea to repeat the entire `case` statement, and with type switches, you cannot use `fallthrough`.

              This `if` condition is there because some other ssh code in the `ssh` package supports that case, but it's not the general idiom.

            • Patch Set #5, Line 1558:

              struct {
              N *big.Int
              E *big.Int
              D *big.Int
              Iqmp *big.Int
              P *big.Int
              Q *big.Int
              Comment string
              }

              Similarly, through this function we are duplicating types from parseOpenSSHPrivateKey.

            • Ack

            • While this is technically correct, it is somewhat confusing to the casual reader since it appears yo […]

              Ack

            • i.e. this can be […]

              Per before, I don't think is a good idea to add a `case *ed25519.PrivateKey1`

          To view, visit change 218620. To unsubscribe, or for help writing mail filters, visit settings.

          Gerrit-Project: crypto
          Gerrit-Branch: master
          Gerrit-Change-Id: I1a95301f789ce04858e6b147748c6e8b7700384b
          Gerrit-Change-Number: 218620
          Gerrit-PatchSet: 6
          Gerrit-Owner: Mariano Cano <marian...@gmail.com>
          Gerrit-Reviewer: Alex Scheel <alex....@hashicorp.com>
          Gerrit-Reviewer: Filippo Valsorda <fil...@golang.org>
          Gerrit-Reviewer: Gopher Robot <go...@golang.org>
          Gerrit-Reviewer: Ivan De Marino <ivan.d...@hashicorp.com>
          Gerrit-Reviewer: Roland Shoemaker <rol...@golang.org>
          Gerrit-CC: Carlos Alexandro Becker <caar...@gmail.com>
          Gerrit-CC: Ian Lance Taylor <ia...@golang.org>
          Gerrit-CC: Katie Hockman <ka...@golang.org>
          Gerrit-Attention: Carlos Alexandro Becker <caar...@gmail.com>
          Gerrit-Attention: Roland Shoemaker <rol...@golang.org>
          Gerrit-Comment-Date: Wed, 28 Sep 2022 01:09:45 +0000

          Carlos Alexandro Becker (Gerrit)

          unread,
          Dec 7, 2022, 8:38:28 AM12/7/22
          to Mariano Cano, goph...@pubsubhelper.golang.org, Roland Shoemaker, Ian Lance Taylor, Alex Scheel, Gopher Robot, Ivan De Marino, Katie Hockman, Filippo Valsorda, golang-co...@googlegroups.com

          Attention is currently required from: Mariano Cano, Roland Shoemaker.

          View Change

          2 comments:

          To view, visit change 218620. To unsubscribe, or for help writing mail filters, visit settings.

          Gerrit-Project: crypto
          Gerrit-Branch: master
          Gerrit-Change-Id: I1a95301f789ce04858e6b147748c6e8b7700384b
          Gerrit-Change-Number: 218620
          Gerrit-PatchSet: 6
          Gerrit-Owner: Mariano Cano <marian...@gmail.com>
          Gerrit-Reviewer: Alex Scheel <alex....@hashicorp.com>
          Gerrit-Reviewer: Filippo Valsorda <fil...@golang.org>
          Gerrit-Reviewer: Gopher Robot <go...@golang.org>
          Gerrit-Reviewer: Ivan De Marino <ivan.d...@hashicorp.com>
          Gerrit-Reviewer: Roland Shoemaker <rol...@golang.org>
          Gerrit-CC: Carlos Alexandro Becker <caar...@gmail.com>
          Gerrit-CC: Ian Lance Taylor <ia...@golang.org>
          Gerrit-CC: Katie Hockman <ka...@golang.org>
          Gerrit-Attention: Roland Shoemaker <rol...@golang.org>
          Gerrit-Attention: Mariano Cano <marian...@gmail.com>
          Gerrit-Comment-Date: Wed, 07 Dec 2022 13:38:23 +0000
          Gerrit-HasComments: Yes
          Gerrit-Has-Labels: No
          Gerrit-MessageType: comment

          Mariano Cano (Gerrit)

          unread,
          Feb 20, 2023, 3:12:22 PM2/20/23
          to goph...@pubsubhelper.golang.org, Roland Shoemaker, Ian Lance Taylor, Carlos Alexandro Becker, Alex Scheel, Gopher Robot, Ivan De Marino, Katie Hockman, Filippo Valsorda, golang-co...@googlegroups.com

          Attention is currently required from: Carlos Alexandro Becker, Roland Shoemaker.

          View Change

          3 comments:

            • // Use a []byte directly on ed25519 keys.
              if k, ok := key.(*ed25519.PrivateKey); ok {
              key = *k
              }

            • I don't think it is a good idea to repeat the entire `case` statement, and with type switches, you c […]

              Done

            • Per before, I don't think is a good idea to add a `case *ed25519. […]

              Done

          • File ssh/keys.go:

            • are those check fields the "64-bit dummy checksum? # a random 32-bit int, repeated" field from the […]

              These checksums are described at https://github.com/openssh/openssh-portable/blob/master/PROTOCOL.key.

              When you encrypt a key, a random value is assigned to both checks, when you decrypt it, if the value is different you know for sure that the password was not the correct one, and you don't need to attempt to unmarshal the rest.

              With an incorrect password, you can also get those checks to represent the right value, but then the probability of properly unmarshal a valid key is negligible.

          To view, visit change 218620. To unsubscribe, or for help writing mail filters, visit settings.

          Gerrit-Project: crypto
          Gerrit-Branch: master
          Gerrit-Change-Id: I1a95301f789ce04858e6b147748c6e8b7700384b
          Gerrit-Change-Number: 218620
          Gerrit-PatchSet: 6
          Gerrit-Owner: Mariano Cano <marian...@gmail.com>
          Gerrit-Reviewer: Alex Scheel <alex....@hashicorp.com>
          Gerrit-Reviewer: Filippo Valsorda <fil...@golang.org>
          Gerrit-Reviewer: Gopher Robot <go...@golang.org>
          Gerrit-Reviewer: Ivan De Marino <ivan.d...@hashicorp.com>
          Gerrit-Reviewer: Roland Shoemaker <rol...@golang.org>
          Gerrit-CC: Carlos Alexandro Becker <caar...@gmail.com>
          Gerrit-CC: Ian Lance Taylor <ia...@golang.org>
          Gerrit-CC: Katie Hockman <ka...@golang.org>
          Gerrit-Attention: Carlos Alexandro Becker <caar...@gmail.com>
          Gerrit-Attention: Roland Shoemaker <rol...@golang.org>
          Gerrit-Comment-Date: Mon, 20 Feb 2023 20:12:18 +0000
          Gerrit-HasComments: Yes
          Gerrit-Has-Labels: No
          Comment-In-Reply-To: Carlos Alexandro Becker <caar...@gmail.com>
          Comment-In-Reply-To: Roland Shoemaker <rol...@golang.org>

          Caleb Spare (Gerrit)

          unread,
          May 25, 2023, 5:07:46 PM5/25/23
          to Mariano Cano, goph...@pubsubhelper.golang.org, Roland Shoemaker, Ian Lance Taylor, Carlos Alexandro Becker, Alex Scheel, Gopher Robot, Ivan De Marino, Katie Hockman, Filippo Valsorda, golang-co...@googlegroups.com

          Attention is currently required from: Carlos Alexandro Becker, Mariano Cano, Roland Shoemaker.

          View Change

          1 comment:

          • Patchset:

            • Patch Set #6:

              Ping. Isn't this waiting on another round of review?

          To view, visit change 218620. To unsubscribe, or for help writing mail filters, visit settings.

          Gerrit-MessageType: comment
          Gerrit-Project: crypto
          Gerrit-Branch: master
          Gerrit-Change-Id: I1a95301f789ce04858e6b147748c6e8b7700384b
          Gerrit-Change-Number: 218620
          Gerrit-PatchSet: 6
          Gerrit-Owner: Mariano Cano <marian...@gmail.com>
          Gerrit-Reviewer: Alex Scheel <alex....@hashicorp.com>
          Gerrit-Reviewer: Filippo Valsorda <fil...@golang.org>
          Gerrit-Reviewer: Gopher Robot <go...@golang.org>
          Gerrit-Reviewer: Ivan De Marino <ivan.d...@hashicorp.com>
          Gerrit-Reviewer: Roland Shoemaker <rol...@golang.org>
          Gerrit-CC: Caleb Spare <ces...@gmail.com>
          Gerrit-CC: Carlos Alexandro Becker <caar...@gmail.com>
          Gerrit-CC: Ian Lance Taylor <ia...@golang.org>
          Gerrit-CC: Katie Hockman <ka...@golang.org>
          Gerrit-Attention: Carlos Alexandro Becker <caar...@gmail.com>
          Gerrit-Attention: Roland Shoemaker <rol...@golang.org>
          Gerrit-Attention: Mariano Cano <marian...@gmail.com>
          Gerrit-Comment-Date: Thu, 25 May 2023 21:07:42 +0000
          Gerrit-HasComments: Yes
          Gerrit-Has-Labels: No

          Caleb Spare (Gerrit)

          unread,
          Sep 3, 2023, 5:03:45 PM9/3/23
          to Mariano Cano, goph...@pubsubhelper.golang.org, Plumer CC, ALI “Iceman” MAZLOUM, Caleb Spare, Roland Shoemaker, Ian Lance Taylor, Carlos Alexandro Becker, Alex Scheel, Gopher Robot, Ivan De Marino, Katie Hockman, Filippo Valsorda, golang-co...@googlegroups.com

          Attention is currently required from: Carlos Alexandro Becker, Mariano Cano, Roland Shoemaker.

          View Change

          1 comment:

          • Patchset:

            • Patch Set #6:

              Hi Roland et al, could we please get this reviewed? The author has been waiting 3.5 years now and more than 6 months since the most recent replies.

          To view, visit change 218620. To unsubscribe, or for help writing mail filters, visit settings.

          Gerrit-MessageType: comment
          Gerrit-Project: crypto
          Gerrit-Branch: master
          Gerrit-Change-Id: I1a95301f789ce04858e6b147748c6e8b7700384b
          Gerrit-Change-Number: 218620
          Gerrit-PatchSet: 6
          Gerrit-Owner: Mariano Cano <marian...@gmail.com>
          Gerrit-Reviewer: Alex Scheel <alex....@hashicorp.com>
          Gerrit-Reviewer: Filippo Valsorda <fil...@golang.org>
          Gerrit-Reviewer: Gopher Robot <go...@golang.org>
          Gerrit-Reviewer: Ivan De Marino <ivan.d...@hashicorp.com>
          Gerrit-Reviewer: Roland Shoemaker <rol...@golang.org>
          Gerrit-CC: ALI “Iceman” MAZLOUM <iam.ali...@gmail.com>
          Gerrit-CC: Caleb Spare <ca...@liftoff.io>
          Gerrit-CC: Caleb Spare <ces...@gmail.com>
          Gerrit-CC: Carlos Alexandro Becker <caar...@gmail.com>
          Gerrit-CC: Ian Lance Taylor <ia...@golang.org>
          Gerrit-CC: Katie Hockman <ka...@golang.org>
          Gerrit-CC: Plumer CC <cz1881...@gmail.com>
          Gerrit-Attention: Carlos Alexandro Becker <caar...@gmail.com>
          Gerrit-Attention: Roland Shoemaker <rol...@golang.org>
          Gerrit-Attention: Mariano Cano <marian...@gmail.com>
          Gerrit-Comment-Date: Sun, 03 Sep 2023 21:03:37 +0000
          Gerrit-HasComments: Yes
          Gerrit-Has-Labels: No

          Roland Shoemaker (Gerrit)

          unread,
          Sep 5, 2023, 2:52:21 PM9/5/23
          to Mariano Cano, goph...@pubsubhelper.golang.org, Caleb Spare, Plumer CC, ALI “Iceman” MAZLOUM, Caleb Spare, Ian Lance Taylor, Carlos Alexandro Becker, Alex Scheel, Gopher Robot, Ivan De Marino, Katie Hockman, Filippo Valsorda, golang-co...@googlegroups.com

          Attention is currently required from: Carlos Alexandro Becker, Mariano Cano.

          Patch set 6:Run-TryBot +1Code-Review +2Commit-Queue +1

          View Change

          1 comment:

          • Patchset:

            • Patch Set #6:

              Sorry for the (extremely) long wait on this, sometimes CLs get buried in my dashboard and it can be quite hard for them to resurface.

          To view, visit change 218620. To unsubscribe, or for help writing mail filters, visit settings.

          Gerrit-MessageType: comment
          Gerrit-Project: crypto
          Gerrit-Branch: master
          Gerrit-Change-Id: I1a95301f789ce04858e6b147748c6e8b7700384b
          Gerrit-Change-Number: 218620
          Gerrit-PatchSet: 6
          Gerrit-Owner: Mariano Cano <marian...@gmail.com>
          Gerrit-Reviewer: Alex Scheel <alex....@hashicorp.com>
          Gerrit-Reviewer: Filippo Valsorda <fil...@golang.org>
          Gerrit-Reviewer: Gopher Robot <go...@golang.org>
          Gerrit-Reviewer: Ivan De Marino <ivan.d...@hashicorp.com>
          Gerrit-Reviewer: Roland Shoemaker <rol...@golang.org>
          Gerrit-CC: ALI “Iceman” MAZLOUM <iam.ali...@gmail.com>
          Gerrit-CC: Caleb Spare <ca...@liftoff.io>
          Gerrit-CC: Caleb Spare <ces...@gmail.com>
          Gerrit-CC: Carlos Alexandro Becker <caar...@gmail.com>
          Gerrit-CC: Ian Lance Taylor <ia...@golang.org>
          Gerrit-CC: Katie Hockman <ka...@golang.org>
          Gerrit-CC: Plumer CC <cz1881...@gmail.com>
          Gerrit-Attention: Carlos Alexandro Becker <caar...@gmail.com>
          Gerrit-Attention: Mariano Cano <marian...@gmail.com>
          Gerrit-Comment-Date: Tue, 05 Sep 2023 18:52:14 +0000
          Gerrit-HasComments: Yes
          Gerrit-Has-Labels: Yes

          Roland Shoemaker (Gerrit)

          unread,
          Sep 5, 2023, 2:58:07 PM9/5/23
          to Mariano Cano, goph...@pubsubhelper.golang.org, Gopher Robot, Go LUCI, Caleb Spare, Plumer CC, ALI “Iceman” MAZLOUM, Caleb Spare, Ian Lance Taylor, Carlos Alexandro Becker, Alex Scheel, Ivan De Marino, Katie Hockman, Filippo Valsorda, golang-co...@googlegroups.com

          Attention is currently required from: Carlos Alexandro Becker, Mariano Cano.

          Patch set 7:Run-TryBot +1Code-Review +2Commit-Queue +1

          View Change

            To view, visit change 218620. To unsubscribe, or for help writing mail filters, visit settings.

            Gerrit-MessageType: comment
            Gerrit-Project: crypto
            Gerrit-Branch: master
            Gerrit-Change-Id: I1a95301f789ce04858e6b147748c6e8b7700384b
            Gerrit-Change-Number: 218620
            Gerrit-PatchSet: 7
            Gerrit-Owner: Mariano Cano <marian...@gmail.com>
            Gerrit-Reviewer: Alex Scheel <alex....@hashicorp.com>
            Gerrit-Reviewer: Filippo Valsorda <fil...@golang.org>
            Gerrit-Reviewer: Gopher Robot <go...@golang.org>
            Gerrit-Reviewer: Ivan De Marino <ivan.d...@hashicorp.com>
            Gerrit-Reviewer: Roland Shoemaker <rol...@golang.org>
            Gerrit-CC: ALI “Iceman” MAZLOUM <iam.ali...@gmail.com>
            Gerrit-CC: Caleb Spare <ca...@liftoff.io>
            Gerrit-CC: Caleb Spare <ces...@gmail.com>
            Gerrit-CC: Carlos Alexandro Becker <caar...@gmail.com>
            Gerrit-CC: Ian Lance Taylor <ia...@golang.org>
            Gerrit-CC: Katie Hockman <ka...@golang.org>
            Gerrit-CC: Plumer CC <cz1881...@gmail.com>
            Gerrit-Attention: Carlos Alexandro Becker <caar...@gmail.com>
            Gerrit-Attention: Mariano Cano <marian...@gmail.com>
            Gerrit-Comment-Date: Tue, 05 Sep 2023 18:58:02 +0000
            Gerrit-HasComments: No
            Gerrit-Has-Labels: Yes

            Roland Shoemaker (Gerrit)

            unread,
            Sep 5, 2023, 3:06:04 PM9/5/23
            to Mariano Cano, goph...@pubsubhelper.golang.org, Go LUCI, Gopher Robot, Caleb Spare, Plumer CC, ALI “Iceman” MAZLOUM, Caleb Spare, Ian Lance Taylor, Carlos Alexandro Becker, Alex Scheel, Ivan De Marino, Katie Hockman, Filippo Valsorda, golang-co...@googlegroups.com

            Attention is currently required from: Carlos Alexandro Becker, Mariano Cano.

            Patch set 7:Auto-Submit +1

            View Change

              To view, visit change 218620. To unsubscribe, or for help writing mail filters, visit settings.

              Gerrit-MessageType: comment
              Gerrit-Project: crypto
              Gerrit-Branch: master
              Gerrit-Change-Id: I1a95301f789ce04858e6b147748c6e8b7700384b
              Gerrit-Change-Number: 218620
              Gerrit-PatchSet: 7
              Gerrit-Owner: Mariano Cano <marian...@gmail.com>
              Gerrit-Reviewer: Alex Scheel <alex....@hashicorp.com>
              Gerrit-Reviewer: Filippo Valsorda <fil...@golang.org>
              Gerrit-Reviewer: Gopher Robot <go...@golang.org>
              Gerrit-Reviewer: Ivan De Marino <ivan.d...@hashicorp.com>
              Gerrit-Reviewer: Roland Shoemaker <rol...@golang.org>
              Gerrit-CC: ALI “Iceman” MAZLOUM <iam.ali...@gmail.com>
              Gerrit-CC: Caleb Spare <ca...@liftoff.io>
              Gerrit-CC: Caleb Spare <ces...@gmail.com>
              Gerrit-CC: Carlos Alexandro Becker <caar...@gmail.com>
              Gerrit-CC: Ian Lance Taylor <ia...@golang.org>
              Gerrit-CC: Katie Hockman <ka...@golang.org>
              Gerrit-CC: Plumer CC <cz1881...@gmail.com>
              Gerrit-Attention: Carlos Alexandro Becker <caar...@gmail.com>
              Gerrit-Attention: Mariano Cano <marian...@gmail.com>
              Gerrit-Comment-Date: Tue, 05 Sep 2023 19:05:58 +0000
              Gerrit-HasComments: No
              Gerrit-Has-Labels: Yes

              Cherry Mui (Gerrit)

              unread,
              Sep 5, 2023, 4:25:37 PM9/5/23
              to Mariano Cano, goph...@pubsubhelper.golang.org, Roland Shoemaker, Go LUCI, Gopher Robot, Caleb Spare, Plumer CC, ALI “Iceman” MAZLOUM, Caleb Spare, Ian Lance Taylor, Carlos Alexandro Becker, Alex Scheel, Ivan De Marino, Katie Hockman, Filippo Valsorda, golang-co...@googlegroups.com

              Attention is currently required from: Carlos Alexandro Becker, Mariano Cano.

              Patch set 7:Code-Review +1

              View Change

                To view, visit change 218620. To unsubscribe, or for help writing mail filters, visit settings.

                Gerrit-MessageType: comment
                Gerrit-Project: crypto
                Gerrit-Branch: master
                Gerrit-Change-Id: I1a95301f789ce04858e6b147748c6e8b7700384b
                Gerrit-Change-Number: 218620
                Gerrit-PatchSet: 7
                Gerrit-Owner: Mariano Cano <marian...@gmail.com>
                Gerrit-Reviewer: Alex Scheel <alex....@hashicorp.com>
                Gerrit-Reviewer: Cherry Mui <cher...@google.com>
                Gerrit-Reviewer: Filippo Valsorda <fil...@golang.org>
                Gerrit-Reviewer: Gopher Robot <go...@golang.org>
                Gerrit-Reviewer: Ivan De Marino <ivan.d...@hashicorp.com>
                Gerrit-Reviewer: Roland Shoemaker <rol...@golang.org>
                Gerrit-CC: ALI “Iceman” MAZLOUM <iam.ali...@gmail.com>
                Gerrit-CC: Caleb Spare <ca...@liftoff.io>
                Gerrit-CC: Caleb Spare <ces...@gmail.com>
                Gerrit-CC: Carlos Alexandro Becker <caar...@gmail.com>
                Gerrit-CC: Ian Lance Taylor <ia...@golang.org>
                Gerrit-CC: Katie Hockman <ka...@golang.org>
                Gerrit-CC: Plumer CC <cz1881...@gmail.com>
                Gerrit-Attention: Carlos Alexandro Becker <caar...@gmail.com>
                Gerrit-Attention: Mariano Cano <marian...@gmail.com>
                Gerrit-Comment-Date: Tue, 05 Sep 2023 20:25:31 +0000
                Gerrit-HasComments: No
                Gerrit-Has-Labels: Yes

                Gopher Robot (Gerrit)

                unread,
                Sep 5, 2023, 4:25:53 PM9/5/23
                to Mariano Cano, goph...@pubsubhelper.golang.org, golang-...@googlegroups.com, Cherry Mui, Roland Shoemaker, Go LUCI, Caleb Spare, Plumer CC, ALI “Iceman” MAZLOUM, Caleb Spare, Ian Lance Taylor, Carlos Alexandro Becker, Alex Scheel, Ivan De Marino, Katie Hockman, Filippo Valsorda, golang-co...@googlegroups.com

                Gopher Robot submitted this change.

                View Change

                Approvals: Cherry Mui: Looks good to me, but someone else must approve Go LUCI: TryBots succeeded Roland Shoemaker: Looks good to me, approved; Run legacy TryBots; Automatically submit change Gopher Robot: TryBots succeeded
                ssh: support for marshaling keys using the OpenSSH format

                This adds methods to marshal private keys, encrypted and unencrypted
                to the OpenSSH format.

                Fixes golang/go#37132

                Change-Id: I1a95301f789ce04858e6b147748c6e8b7700384b
                Reviewed-on: https://go-review.googlesource.com/c/crypto/+/218620
                Run-TryBot: Roland Shoemaker <rol...@golang.org>
                TryBot-Result: Gopher Robot <go...@golang.org>
                LUCI-TryBot-Result: Go LUCI <golang...@luci-project-accounts.iam.gserviceaccount.com>
                Reviewed-by: Cherry Mui <cher...@google.com>
                Reviewed-by: Roland Shoemaker <rol...@golang.org>
                Auto-Submit: Roland Shoemaker <rol...@golang.org>

                ---
                M ssh/keys.go
                M ssh/keys_test.go
                2 files changed, 308 insertions(+), 46 deletions(-)

                diff --git a/ssh/keys.go b/ssh/keys.go
                index dac8ee7..1bf28d8 100644
                --- a/ssh/keys.go
                +++ b/ssh/keys.go
                @@ -13,11 +13,13 @@
                "crypto/ecdsa"
                "crypto/elliptic"
                "crypto/md5"
                + "crypto/rand"
                "crypto/rsa"
                "crypto/sha256"
                "crypto/x509"
                "encoding/asn1"
                "encoding/base64"
                + "encoding/binary"
                "encoding/hex"
                "encoding/pem"
                "errors"
                @@ -295,6 +297,18 @@
                return b.Bytes()
                }

                +// MarshalPrivateKey returns a PEM block with the private key serialized in the
                +// OpenSSH format.
                +func MarshalPrivateKey(key crypto.PrivateKey, comment string) (*pem.Block, error) {
                + return marshalOpenSSHPrivateKey(key, comment, unencryptedOpenSSHMarshaler)
                +}
                +
                +// MarshalPrivateKeyWithPassphrase returns a PEM block holding the encrypted
                +// private key serialized in the OpenSSH format.
                +func MarshalPrivateKeyWithPassphrase(key crypto.PrivateKey, comment string, passphrase []byte) (*pem.Block, error) {
                + return marshalOpenSSHPrivateKey(key, comment, passphraseProtectedOpenSSHMarshaler(passphrase))
                +}
                +
                // PublicKey represents a public key using an unspecified algorithm.
                //
                // Some PublicKeys provided by this package also implement CryptoPublicKey.
                @@ -1241,28 +1255,106 @@
                }
                }

                +func unencryptedOpenSSHMarshaler(privKeyBlock []byte) ([]byte, string, string, string, error) {
                + key := generateOpenSSHPadding(privKeyBlock, 8)
                + return key, "none", "none", "", nil
                +}
                +
                +func passphraseProtectedOpenSSHMarshaler(passphrase []byte) openSSHEncryptFunc {
                + return func(privKeyBlock []byte) ([]byte, string, string, string, error) {
                + salt := make([]byte, 16)
                + if _, err := rand.Read(salt); err != nil {
                + return nil, "", "", "", err
                + }
                +
                + opts := struct {
                + Salt []byte
                + Rounds uint32
                + }{salt, 16}
                +
                + // Derive key to encrypt the private key block.
                + k, err := bcrypt_pbkdf.Key(passphrase, salt, int(opts.Rounds), 32+aes.BlockSize)
                + if err != nil {
                + return nil, "", "", "", err
                + }
                +
                + // Add padding matching the block size of AES.
                + keyBlock := generateOpenSSHPadding(privKeyBlock, aes.BlockSize)
                +
                + // Encrypt the private key using the derived secret.
                +
                + dst := make([]byte, len(keyBlock))
                + key, iv := k[:32], k[32:]
                + block, err := aes.NewCipher(key)
                + if err != nil {
                + return nil, "", "", "", err
                + }
                +
                + stream := cipher.NewCTR(block, iv)
                + stream.XORKeyStream(dst, keyBlock)
                +
                + return dst, "aes256-ctr", "bcrypt", string(Marshal(opts)), nil
                + }
                +}
                +
                +const privateKeyAuthMagic = "openssh-key-v1\x00"
                +
                type openSSHDecryptFunc func(CipherName, KdfName, KdfOpts string, PrivKeyBlock []byte) ([]byte, error)
                +type openSSHEncryptFunc func(PrivKeyBlock []byte) (ProtectedKeyBlock []byte, cipherName, kdfName, kdfOptions string, err error)
                +
                +type openSSHEncryptedPrivateKey struct {
                + CipherName string
                + KdfName string
                + KdfOpts string
                + NumKeys uint32
                + PubKey []byte
                + PrivKeyBlock []byte
                +}
                +
                +type openSSHPrivateKey struct {
                + Check1 uint32
                + Check2 uint32
                + Keytype string
                + Rest []byte `ssh:"rest"`
                +}
                +
                +type openSSHRSAPrivateKey struct {
                + N *big.Int
                + E *big.Int
                + D *big.Int
                + Iqmp *big.Int
                + P *big.Int
                + Q *big.Int
                + Comment string
                + Pad []byte `ssh:"rest"`
                +}
                +
                +type openSSHEd25519PrivateKey struct {
                + Pub []byte
                + Priv []byte
                + Comment string
                + Pad []byte `ssh:"rest"`
                +}
                +
                +type openSSHECDSAPrivateKey struct {
                + Curve string
                + Pub []byte
                + D *big.Int
                + Comment string
                + Pad []byte `ssh:"rest"`
                +}

                // parseOpenSSHPrivateKey parses an OpenSSH private key, using the decrypt
                // function to unwrap the encrypted portion. unencryptedOpenSSHKey can be used
                // as the decrypt function to parse an unencrypted private key. See
                // https://github.com/openssh/openssh-portable/blob/master/PROTOCOL.key.
                func parseOpenSSHPrivateKey(key []byte, decrypt openSSHDecryptFunc) (crypto.PrivateKey, error) {
                - const magic = "openssh-key-v1\x00"
                - if len(key) < len(magic) || string(key[:len(magic)]) != magic {
                + if len(key) < len(privateKeyAuthMagic) || string(key[:len(privateKeyAuthMagic)]) != privateKeyAuthMagic {
                return nil, errors.New("ssh: invalid openssh private key format")
                }
                - remaining := key[len(magic):]
                + remaining := key[len(privateKeyAuthMagic):]

                - var w struct {
                - CipherName string
                - KdfName string
                - KdfOpts string
                - NumKeys uint32
                - PubKey []byte
                - PrivKeyBlock []byte
                - }
                -
                + var w openSSHEncryptedPrivateKey
                if err := Unmarshal(remaining, &w); err != nil {
                return nil, err
                }
                @@ -1284,13 +1376,7 @@
                return nil, err
                }

                - pk1 := struct {
                - Check1 uint32
                - Check2 uint32
                - Keytype string
                - Rest []byte `ssh:"rest"`
                - }{}
                -
                + var pk1 openSSHPrivateKey
                if err := Unmarshal(privKeyBlock, &pk1); err != nil || pk1.Check1 != pk1.Check2 {
                if w.CipherName != "none" {
                return nil, x509.IncorrectPasswordError
                @@ -1300,18 +1386,7 @@

                switch pk1.Keytype {
                case KeyAlgoRSA:
                - // https://github.com/openssh/openssh-portable/blob/master/sshkey.c#L2760-L2773
                - key := struct {
                - N *big.Int
                - E *big.Int
                - D *big.Int
                - Iqmp *big.Int
                - P *big.Int
                - Q *big.Int
                - Comment string
                - Pad []byte `ssh:"rest"`
                - }{}
                -
                + var key openSSHRSAPrivateKey
                if err := Unmarshal(pk1.Rest, &key); err != nil {
                return nil, err
                }
                @@ -1337,13 +1412,7 @@

                return pk, nil
                case KeyAlgoED25519:
                - key := struct {
                - Pub []byte
                - Priv []byte
                - Comment string
                - Pad []byte `ssh:"rest"`
                - }{}
                -
                + var key openSSHEd25519PrivateKey
                if err := Unmarshal(pk1.Rest, &key); err != nil {
                return nil, err
                }
                @@ -1360,14 +1429,7 @@
                copy(pk, key.Priv)
                return &pk, nil
                case KeyAlgoECDSA256, KeyAlgoECDSA384, KeyAlgoECDSA521:
                - key := struct {
                - Curve string
                - Pub []byte
                - D *big.Int
                - Comment string
                - Pad []byte `ssh:"rest"`
                - }{}
                -
                + var key openSSHECDSAPrivateKey
                if err := Unmarshal(pk1.Rest, &key); err != nil {
                return nil, err
                }
                @@ -1415,6 +1477,131 @@
                }
                }

                +func marshalOpenSSHPrivateKey(key crypto.PrivateKey, comment string, encrypt openSSHEncryptFunc) (*pem.Block, error) {
                + var w openSSHEncryptedPrivateKey
                + var pk1 openSSHPrivateKey
                +
                + // Random check bytes.
                + var check uint32
                + if err := binary.Read(rand.Reader, binary.BigEndian, &check); err != nil {
                + return nil, err
                + }
                +
                + pk1.Check1 = check
                + pk1.Check2 = check
                + w.NumKeys = 1
                +
                + // Use a []byte directly on ed25519 keys.
                + if k, ok := key.(*ed25519.PrivateKey); ok {
                + key = *k
                + }
                +
                + switch k := key.(type) {
                + case *rsa.PrivateKey:
                + E := new(big.Int).SetInt64(int64(k.PublicKey.E))
                + // Marshal public key:
                + // E and N are in reversed order in the public and private key.
                + pubKey := struct {
                + KeyType string
                + E *big.Int
                + N *big.Int
                + }{
                + KeyAlgoRSA,
                + E, k.PublicKey.N,
                + }
                + w.PubKey = Marshal(pubKey)
                +
                + // Marshal private key.
                + key := openSSHRSAPrivateKey{
                + N: k.PublicKey.N,
                + E: E,
                + D: k.D,
                + Iqmp: k.Precomputed.Qinv,
                + P: k.Primes[0],
                + Q: k.Primes[1],
                + Comment: comment,
                + }
                + pk1.Keytype = KeyAlgoRSA
                + pk1.Rest = Marshal(key)
                + case ed25519.PrivateKey:
                + pub := make([]byte, ed25519.PublicKeySize)
                + priv := make([]byte, ed25519.PrivateKeySize)
                + copy(pub, k[32:])
                + copy(priv, k)
                +
                + // Marshal public key.
                + pubKey := struct {
                + KeyType string
                + Pub []byte
                + }{
                + KeyAlgoED25519, pub,
                + }
                + w.PubKey = Marshal(pubKey)
                +
                + // Marshal private key.
                + key := openSSHEd25519PrivateKey{
                + Pub: pub,
                + Priv: priv,
                + Comment: comment,
                + }
                + pk1.Keytype = KeyAlgoED25519
                + pk1.Rest = Marshal(key)
                + case *ecdsa.PrivateKey:
                + var curve, keyType string
                + switch name := k.Curve.Params().Name; name {
                + case "P-256":
                + curve = "nistp256"
                + keyType = KeyAlgoECDSA256
                + case "P-384":
                + curve = "nistp384"
                + keyType = KeyAlgoECDSA384
                + case "P-521":
                + curve = "nistp521"
                + keyType = KeyAlgoECDSA521
                + default:
                + return nil, errors.New("ssh: unhandled elliptic curve " + name)
                + }
                +
                + pub := elliptic.Marshal(k.Curve, k.PublicKey.X, k.PublicKey.Y)
                +
                + // Marshal public key.
                + pubKey := struct {
                + KeyType string
                + Curve string
                + Pub []byte
                + }{
                + keyType, curve, pub,
                + }
                + w.PubKey = Marshal(pubKey)
                +
                + // Marshal private key.
                + key := openSSHECDSAPrivateKey{
                + Curve: curve,
                + Pub: pub,
                + D: k.D,
                + Comment: comment,
                + }
                + pk1.Keytype = keyType
                + pk1.Rest = Marshal(key)
                + default:
                + return nil, fmt.Errorf("ssh: unsupported key type %T", k)
                + }
                +
                + var err error
                + // Add padding and encrypt the key if necessary.
                + w.PrivKeyBlock, w.CipherName, w.KdfName, w.KdfOpts, err = encrypt(Marshal(pk1))
                + if err != nil {
                + return nil, err
                + }
                +
                + b := Marshal(w)
                + block := &pem.Block{
                + Type: "OPENSSH PRIVATE KEY",
                + Bytes: append([]byte(privateKeyAuthMagic), b...),
                + }
                + return block, nil
                +}
                +
                func checkOpenSSHKeyPadding(pad []byte) error {
                for i, b := range pad {
                if int(b) != i+1 {
                @@ -1424,6 +1611,13 @@
                return nil
                }

                +func generateOpenSSHPadding(block []byte, blockSize int) []byte {
                + for i, l := 0, len(block); (l+i)%blockSize != 0; i++ {
                + block = append(block, byte(i+1))
                + }
                + return block
                +}
                +
                // FingerprintLegacyMD5 returns the user presentation of the key's
                // fingerprint as described by RFC 4716 section 4.
                func FingerprintLegacyMD5(pubKey PublicKey) string {
                diff --git a/ssh/keys_test.go b/ssh/keys_test.go
                index 334ef74..a8e216e 100644
                --- a/ssh/keys_test.go
                +++ b/ssh/keys_test.go
                @@ -281,6 +281,74 @@
                }
                }

                +func TestMarshalPrivateKey(t *testing.T) {
                + tests := []struct {
                + name string
                + }{
                + {"rsa-openssh-format"},
                + {"ed25519"},
                + {"p256-openssh-format"},
                + {"p384-openssh-format"},
                + {"p521-openssh-format"},
                + }
                + for _, tt := range tests {
                + t.Run(tt.name, func(t *testing.T) {
                + expected, ok := testPrivateKeys[tt.name]
                + if !ok {
                + t.Fatalf("cannot find key %s", tt.name)
                + }
                +
                + block, err := MarshalPrivateKey(expected, "te...@golang.org")
                + if err != nil {
                + t.Fatalf("cannot marshal %s: %v", tt.name, err)
                + }
                +
                + key, err := ParseRawPrivateKey(pem.EncodeToMemory(block))
                + if err != nil {
                + t.Fatalf("cannot parse %s: %v", tt.name, err)
                + }
                +
                + if !reflect.DeepEqual(expected, key) {
                + t.Errorf("unexpected marshaled key %s", tt.name)
                + }
                + })
                + }
                +}
                +
                +func TestMarshalPrivateKeyWithPassphrase(t *testing.T) {
                + tests := []struct {
                + name string
                + }{
                + {"rsa-openssh-format"},
                + {"ed25519"},
                + {"p256-openssh-format"},
                + {"p384-openssh-format"},
                + {"p521-openssh-format"},
                + }
                + for _, tt := range tests {
                + t.Run(tt.name, func(t *testing.T) {
                + expected, ok := testPrivateKeys[tt.name]
                + if !ok {
                + t.Fatalf("cannot find key %s", tt.name)
                + }
                +
                + block, err := MarshalPrivateKeyWithPassphrase(expected, "te...@golang.org", []byte("test-passphrase"))
                + if err != nil {
                + t.Fatalf("cannot marshal %s: %v", tt.name, err)
                + }
                +
                + key, err := ParseRawPrivateKeyWithPassphrase(pem.EncodeToMemory(block), []byte("test-passphrase"))
                + if err != nil {
                + t.Fatalf("cannot parse %s: %v", tt.name, err)
                + }
                +
                + if !reflect.DeepEqual(expected, key) {
                + t.Errorf("unexpected marshaled key %s", tt.name)
                + }
                + })
                + }
                +}
                +
                type testAuthResult struct {
                pubKey PublicKey
                options []string

                To view, visit change 218620. To unsubscribe, or for help writing mail filters, visit settings.

                Gerrit-MessageType: merged
                Gerrit-Project: crypto
                Gerrit-Branch: master
                Gerrit-Change-Id: I1a95301f789ce04858e6b147748c6e8b7700384b
                Gerrit-Change-Number: 218620
                Gerrit-PatchSet: 8
                Reply all
                Reply to author
                Forward
                0 new messages