(key ed25519.PrivateKey)
| 44 | ) |
| 45 | |
| 46 | func MarshalED25519PrivateKey(key ed25519.PrivateKey) ([]byte, error) { |
| 47 | // Add our key header (followed by a null byte) |
| 48 | magic := append([]byte("openssh-key-v1"), 0) |
| 49 | |
| 50 | var msg struct { |
| 51 | CipherName string |
| 52 | KdfName string |
| 53 | KdfOpts string |
| 54 | NumKeys uint32 |
| 55 | PubKey []byte |
| 56 | PrivKeyBlock []byte |
| 57 | } |
| 58 | |
| 59 | // Fill out the private key fields |
| 60 | pk1 := struct { |
| 61 | Check1 uint32 |
| 62 | Check2 uint32 |
| 63 | Keytype string |
| 64 | Pub []byte |
| 65 | Priv []byte |
| 66 | Comment string |
| 67 | Pad []byte `ssh:"rest"` |
| 68 | }{} |
| 69 | |
| 70 | // Random check bytes |
| 71 | var check uint32 |
| 72 | if err := binary.Read(rand.Reader, binary.BigEndian, &check); err != nil { |
| 73 | return nil, xerrors.Errorf("generate random bytes: %w", err) |
| 74 | } |
| 75 | |
| 76 | pk1.Check1 = check |
| 77 | pk1.Check2 = check |
| 78 | |
| 79 | // Set our key type |
| 80 | pk1.Keytype = ssh.KeyAlgoED25519 |
| 81 | |
| 82 | // Add the pubkey to the optionally-encrypted block |
| 83 | pk, ok := key.Public().(ed25519.PublicKey) |
| 84 | if !ok { |
| 85 | return nil, xerrors.Errorf("ed25519.PublicKey type assertion failed on an ed25519 public key") |
| 86 | } |
| 87 | pubKey := []byte(pk) |
| 88 | pk1.Pub = pubKey |
| 89 | |
| 90 | // Add our private key |
| 91 | pk1.Priv = []byte(key) |
| 92 | |
| 93 | // Might be useful to put something in here at some point |
| 94 | pk1.Comment = "" |
| 95 | |
| 96 | // Add some padding to match the encryption block size within PrivKeyBlock (without Pad field) |
| 97 | // 8 doesn't match the documentation, but that's what ssh-keygen uses for unencrypted keys. *shrug* |
| 98 | bs := 8 |
| 99 | blockLen := len(ssh.Marshal(pk1)) |
| 100 | padLen := (bs - (blockLen % bs)) % bs |
| 101 | pk1.Pad = make([]byte, padLen) |
| 102 | |
| 103 | // Padding is a sequence of bytes like: 1, 2, 3... |
no test coverage detected