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 }