Skip to content
Snippets Groups Projects
Select Git revision
3 results Searching

ask.go

Blame
  • edkey.go 2.22 KiB
    package edkey
    
    import (
    	"math/rand"
    
    	"golang.org/x/crypto/ed25519"
    	"golang.org/x/crypto/ssh"
    )
    
    /* Writes ed25519 private keys into the new OpenSSH private key format.
    I have no idea why this isn't implemented anywhere yet, you can do seemingly
    everything except write it to disk in the OpenSSH private key format. */
    func MarshalED25519PrivateKey(key ed25519.PrivateKey) []byte {
    	// Add our key header (followed by a null byte)
    	magic := append([]byte("openssh-key-v1"), 0)
    
    	var w struct {
    		CipherName   string
    		KdfName      string
    		KdfOpts      string
    		NumKeys      uint32
    		PubKey       []byte
    		PrivKeyBlock []byte
    	}
    
    	// Fill out the private key fields
    	pk1 := struct {
    		Check1  uint32
    		Check2  uint32
    		Keytype string
    		Pub     []byte
    		Priv    []byte
    		Comment string
    		Pad     []byte `ssh:"rest"`
    	}{}
    
    	// Set our check ints
    	ci := rand.Uint32()
    	pk1.Check1 = ci
    	pk1.Check2 = ci
    
    	// Set our key type
    	pk1.Keytype = ssh.KeyAlgoED25519
    
    	// Add the pubkey to the optionally-encrypted block
    	pk, ok := key.Public().(ed25519.PublicKey)
    	if !ok {
    		//fmt.Fprintln(os.Stderr, "ed25519.PublicKey type assertion failed on an ed25519 public key. This should never ever happen.")
    		return nil
    	}
    	pubKey := []byte(pk)
    	pk1.Pub = pubKey
    
    	// Add our private key
    	pk1.Priv = []byte(key)
    
    	// Might be useful to put something in here at some point
    	pk1.Comment = ""
    
    	// Add some padding to match the encryption block size within PrivKeyBlock (without Pad field)
    	// 8 doesn't match the documentation, but that's what ssh-keygen uses for unencrypted keys. *shrug*
    	bs := 8
    	blockLen := len(ssh.Marshal(pk1))
    	padLen := (bs - (blockLen % bs)) % bs
    	pk1.Pad = make([]byte, padLen)
    
    	// Padding is a sequence of bytes like: 1, 2, 3...
    	for i := 0; i < padLen; i++ {
    		pk1.Pad[i] = byte(i + 1)
    	}
    
    	// Generate the pubkey prefix "\0\0\0\nssh-ed25519\0\0\0 "
    	prefix := []byte{0x0, 0x0, 0x0, 0x0b}
    	prefix = append(prefix, []byte(ssh.KeyAlgoED25519)...)
    	prefix = append(prefix, []byte{0x0, 0x0, 0x0, 0x20}...)
    
    	// Only going to support unencrypted keys for now
    	w.CipherName = "none"
    	w.KdfName = "none"
    	w.KdfOpts = ""
    	w.NumKeys = 1
    	w.PubKey = append(prefix, pubKey...)
    	w.PrivKeyBlock = ssh.Marshal(pk1)
    
    	magic = append(magic, ssh.Marshal(w)...)
    
    	return magic
    }