go.mondoo.com/cnquery@v0.0.0-20231005093811-59568235f6ea/providers/os/connection/ssh/keypair/edkey.go (about)

     1  // Copyright (c) Mondoo, Inc.
     2  // SPDX-License-Identifier: BUSL-1.1
     3  
     4  package keypair
     5  
     6  import (
     7  	"crypto/ed25519"
     8  	"math/rand"
     9  
    10  	"golang.org/x/crypto/ssh"
    11  )
    12  
    13  // MarshalED25519PrivateKey writes ed25519 private keys into the new OpenSSH private key format.
    14  // This code is derived from https://github.com/mikesmitty/edkey which is MIT-Licensed
    15  func MarshalED25519PrivateKey(key ed25519.PrivateKey) []byte {
    16  	// Add our key header (followed by a null byte)
    17  	magic := append([]byte("openssh-key-v1"), 0)
    18  
    19  	var w struct {
    20  		CipherName   string
    21  		KdfName      string
    22  		KdfOpts      string
    23  		NumKeys      uint32
    24  		PubKey       []byte
    25  		PrivKeyBlock []byte
    26  	}
    27  
    28  	// Fill out the private key fields
    29  	pk1 := struct {
    30  		Check1  uint32
    31  		Check2  uint32
    32  		Keytype string
    33  		Pub     []byte
    34  		Priv    []byte
    35  		Comment string
    36  		Pad     []byte `ssh:"rest"`
    37  	}{}
    38  
    39  	// Set our check ints
    40  	ci := rand.Uint32()
    41  	pk1.Check1 = ci
    42  	pk1.Check2 = ci
    43  
    44  	// Set our key type
    45  	pk1.Keytype = ssh.KeyAlgoED25519
    46  
    47  	// Add the pubkey to the optionally-encrypted block
    48  	pk, ok := key.Public().(ed25519.PublicKey)
    49  	if !ok {
    50  		// fmt.Fprintln(os.Stderr, "ed25519.PublicKey type assertion failed on an ed25519 public key. This should never ever happen.")
    51  		return nil
    52  	}
    53  	pubKey := []byte(pk)
    54  	pk1.Pub = pubKey
    55  
    56  	// Add our private key
    57  	pk1.Priv = []byte(key)
    58  
    59  	// Might be useful to put something in here at some point
    60  	pk1.Comment = ""
    61  
    62  	// Add some padding to match the encryption block size within PrivKeyBlock (without Pad field)
    63  	// 8 doesn't match the documentation, but that's what ssh-keygen uses for unencrypted keys. *shrug*
    64  	bs := 8
    65  	blockLen := len(ssh.Marshal(pk1))
    66  	padLen := (bs - (blockLen % bs)) % bs
    67  	pk1.Pad = make([]byte, padLen)
    68  
    69  	// Padding is a sequence of bytes like: 1, 2, 3...
    70  	for i := 0; i < padLen; i++ {
    71  		pk1.Pad[i] = byte(i + 1)
    72  	}
    73  
    74  	// Generate the pubkey prefix "\0\0\0\nssh-ed25519\0\0\0 "
    75  	prefix := []byte{0x0, 0x0, 0x0, 0x0b}
    76  	prefix = append(prefix, []byte(ssh.KeyAlgoED25519)...)
    77  	prefix = append(prefix, []byte{0x0, 0x0, 0x0, 0x20}...)
    78  
    79  	// Only going to support unencrypted keys for now
    80  	w.CipherName = "none"
    81  	w.KdfName = "none"
    82  	w.KdfOpts = ""
    83  	w.NumKeys = 1
    84  	w.PubKey = append(prefix, pubKey...)
    85  	w.PrivKeyBlock = ssh.Marshal(pk1)
    86  
    87  	magic = append(magic, ssh.Marshal(w)...)
    88  
    89  	return magic
    90  }