add sshkey.Generate function

This commit is contained in:
Adrien Delorme 2020-10-12 18:01:05 +02:00
parent 01ed398756
commit 0cf7af4247
2 changed files with 233 additions and 0 deletions

View File

@ -0,0 +1,52 @@
// Code generated by "enumer -type Algorithm -transform snake"; DO NOT EDIT.
//
package communicator
import (
"fmt"
)
const _AlgorithmName = "rsadsaecdsaed25519"
var _AlgorithmIndex = [...]uint8{0, 3, 6, 11, 18}
func (i Algorithm) String() string {
if i < 0 || i >= Algorithm(len(_AlgorithmIndex)-1) {
return fmt.Sprintf("Algorithm(%d)", i)
}
return _AlgorithmName[_AlgorithmIndex[i]:_AlgorithmIndex[i+1]]
}
var _AlgorithmValues = []Algorithm{0, 1, 2, 3}
var _AlgorithmNameToValueMap = map[string]Algorithm{
_AlgorithmName[0:3]: 0,
_AlgorithmName[3:6]: 1,
_AlgorithmName[6:11]: 2,
_AlgorithmName[11:18]: 3,
}
// AlgorithmString retrieves an enum value from the enum constants string name.
// Throws an error if the param is not part of the enum.
func AlgorithmString(s string) (Algorithm, error) {
if val, ok := _AlgorithmNameToValueMap[s]; ok {
return val, nil
}
return 0, fmt.Errorf("%s does not belong to Algorithm values", s)
}
// AlgorithmValues returns all values of the enum
func AlgorithmValues() []Algorithm {
return _AlgorithmValues
}
// IsAAlgorithm returns "true" if the value is listed in the enum definition. "false" otherwise
func (i Algorithm) IsAAlgorithm() bool {
for _, v := range _AlgorithmValues {
if i == v {
return true
}
}
return false
}

View File

@ -0,0 +1,181 @@
package communicator
import (
"crypto/dsa"
"crypto/ecdsa"
"crypto/ed25519"
"crypto/elliptic"
cryptorand "crypto/rand"
"crypto/rsa"
"crypto/x509"
"encoding/asn1"
"encoding/pem"
"fmt"
"io"
"math/big"
"golang.org/x/crypto/ssh"
)
type Algorithm int
//go:generate enumer -type Algorithm -transform snake
const (
RSA Algorithm = iota
DSA
ECDSA
ED25519
)
var (
ErrUnknownAlgorithm = fmt.Errorf("sshkey: unknown private key algorithm")
ErrInvalidRSAKeySize = fmt.Errorf("sshkey: invalid private key rsa size: must be more than 1024")
ErrInvalidECDSAKeySize = fmt.Errorf("sshkey: invalid private key ecdsa size, must be one of 256, 384 or 521")
ErrInvalidDSAKeySize = fmt.Errorf("sshkey: invalid private key dsa size, must be one of 1024, 2048 or 3072")
)
// Pair represents an ssh key pair, as in
type Pair struct {
Private []byte
Public []byte
}
func NewPair(public, private interface{}) (*Pair, error) {
kb, err := x509.MarshalPKCS8PrivateKey(private)
if err != nil {
return nil, err
}
privBlk := &pem.Block{
Type: "",
Headers: nil,
Bytes: kb,
}
publicKey, err := ssh.NewPublicKey(public)
if err != nil {
return nil, err
}
return &Pair{
Private: pem.EncodeToMemory(privBlk),
Public: ssh.MarshalAuthorizedKey(publicKey),
}, nil
}
func PairFromDSA(key *dsa.PrivateKey) (*Pair, error) {
// see https://github.com/golang/crypto/blob/7f63de1d35b0f77fa2b9faea3e7deb402a2383c8/ssh/keys.go#L1186-L1195
// and https://linux.die.net/man/1/dsa
k := struct {
Version int
P *big.Int
Q *big.Int
G *big.Int
Pub *big.Int
Priv *big.Int
}{
Version: 0,
P: key.P,
Q: key.Q,
G: key.G,
Pub: key.Y,
Priv: key.X,
}
kb, err := asn1.Marshal(k)
privBlk := &pem.Block{
Type: "DSA PRIVATE KEY",
Headers: nil,
Bytes: kb,
}
publicKey, err := ssh.NewPublicKey(key.PublicKey)
if err != nil {
return nil, err
}
return &Pair{
Private: pem.EncodeToMemory(privBlk),
Public: ssh.MarshalAuthorizedKey(publicKey),
}, nil
}
// GeneratePair generates a Private/Public key pair using algorithm t.
//
// When rand is nil "crypto/rand".Reader will be used.
//
// bits specifies the number of bits in the key to create. For RSA keys, the
// minimum size is 1024 bits and the default is 3072 bits. Generally, 3072 bits
// is considered sufficient. DSA keys must be exactly 1024 bits - or 2 or 3
// times that - as specified by FIPS 186-2. For ECDSA keys, bits determines the
// key length by selecting from one of three elliptic curve sizes: 256, 384 or
// 521 bits. Attempting to use bit lengths other than these three values for
// ECDSA keys will fail. Ed25519 keys have a fixed length and the bits will
// be ignored.
func GeneratePair(t Algorithm, rand io.Reader, bits int) (*Pair, error) {
if rand == nil {
rand = cryptorand.Reader
}
switch t {
case DSA:
var sizes dsa.ParameterSizes
switch bits {
case 1024:
sizes = dsa.L1024N160
case 2048:
sizes = dsa.L2048N256
case 3072:
sizes = dsa.L3072N256
default:
return nil, ErrInvalidDSAKeySize
}
params := dsa.Parameters{}
if err := dsa.GenerateParameters(&params, rand, sizes); err != nil {
return nil, err
}
dsakey := &dsa.PrivateKey{
PublicKey: dsa.PublicKey{
Parameters: params,
},
}
if err := dsa.GenerateKey(dsakey, rand); err != nil {
return nil, err
}
return PairFromDSA(dsakey)
case ECDSA:
var ecdsakey *ecdsa.PrivateKey
var err error
switch bits {
case 256:
ecdsakey, err = ecdsa.GenerateKey(elliptic.P256(), rand)
case 384:
ecdsakey, err = ecdsa.GenerateKey(elliptic.P384(), rand)
case 521:
ecdsakey, err = ecdsa.GenerateKey(elliptic.P521(), rand)
default:
ecdsakey, err = nil, ErrInvalidECDSAKeySize
}
if err != nil {
return nil, err
}
return NewPair(ecdsakey.PublicKey, ecdsakey)
case ED25519:
publicKey, privateKey, err := ed25519.GenerateKey(rand)
if err != nil {
return nil, err
}
return NewPair(publicKey, privateKey)
case RSA:
if bits == 0 {
bits = 3072
}
if bits < 1024 {
return nil, ErrInvalidRSAKeySize
}
rsakey, err := rsa.GenerateKey(rand, bits)
if err != nil {
return nil, err
}
return NewPair(rsakey.PublicKey, rsakey)
default:
return nil, ErrUnknownAlgorithm
}
}