github.com/mvdan/u-root-coreutils@v0.0.0-20230122170626-c2eef2898555/pkg/crypto/ed25519.go (about)

     1  // Copyright 2017-2019 the u-root Authors. All rights reserved
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package crypto
     6  
     7  import (
     8  	"crypto/rand"
     9  	"crypto/x509"
    10  	"encoding/pem"
    11  	"errors"
    12  	"os"
    13  
    14  	"golang.org/x/crypto/ed25519"
    15  )
    16  
    17  var (
    18  	// PubKeyIdentifier is the PEM public key identifier
    19  	PubKeyIdentifier = "PUBLIC KEY"
    20  	// PrivKeyIdentifier is the PEM private key identifier
    21  	PrivKeyIdentifier = "PRIVATE KEY"
    22  	// PEMCipher is the PEM encryption algorithm
    23  	PEMCipher = x509.PEMCipherAES256
    24  	// PubKeyFilePermissions are the public key file perms
    25  	PubKeyFilePermissions os.FileMode = 0o644
    26  	// PrivKeyFilePermissions are the private key file perms
    27  	PrivKeyFilePermissions os.FileMode = 0o600
    28  )
    29  
    30  // LoadPublicKeyFromFile loads PEM formatted ED25519 public key from file.
    31  func LoadPublicKeyFromFile(publicKeyPath string) ([]byte, error) {
    32  	x509PEM, err := os.ReadFile(publicKeyPath)
    33  	if err != nil {
    34  		return nil, err
    35  	}
    36  
    37  	// Parse x509 PEM file
    38  	var block *pem.Block
    39  	for {
    40  		block, x509PEM = pem.Decode(x509PEM)
    41  		if block == nil {
    42  			return nil, errors.New("can't decode PEM file")
    43  		}
    44  		if block.Type == PubKeyIdentifier {
    45  			break
    46  		}
    47  	}
    48  
    49  	return block.Bytes, nil
    50  }
    51  
    52  // LoadPrivateKeyFromFile loads PEM formatted ED25519 private key from file.
    53  func LoadPrivateKeyFromFile(privateKeyPath string, password []byte) ([]byte, error) {
    54  	x509PEM, err := os.ReadFile(privateKeyPath)
    55  	if err != nil {
    56  		return nil, err
    57  	}
    58  
    59  	// Parse x509 PEM file
    60  	var block *pem.Block
    61  	for {
    62  		block, x509PEM = pem.Decode(x509PEM)
    63  		if block == nil {
    64  			return nil, errors.New("can't decode PEM file")
    65  		}
    66  		if block.Type == PrivKeyIdentifier {
    67  			break
    68  		}
    69  	}
    70  
    71  	// Check for encrypted PEM format
    72  	if x509.IsEncryptedPEMBlock(block) {
    73  		decryptedKey, err := x509.DecryptPEMBlock(block, password)
    74  		if err != nil {
    75  			return nil, err
    76  		}
    77  		return decryptedKey, nil
    78  	}
    79  
    80  	return block.Bytes, nil
    81  }
    82  
    83  // GeneratED25519Key generates a ED25519 keypair
    84  func GeneratED25519Key(password []byte, privateKeyFilePath string, publicKeyFilePath string) error {
    85  	pubKey, privKey, err := ed25519.GenerateKey(rand.Reader)
    86  	if err != nil {
    87  		return err
    88  	}
    89  
    90  	privBlock := &pem.Block{
    91  		Type:  "PRIVATE KEY",
    92  		Bytes: privKey,
    93  	}
    94  
    95  	pubBlock := &pem.Block{
    96  		Type:  "PUBLIC KEY",
    97  		Bytes: pubKey,
    98  	}
    99  
   100  	var privateKey []byte
   101  	if len(password) < 1 {
   102  		encrypted, err := x509.EncryptPEMBlock(rand.Reader, privBlock.Type, privBlock.Bytes, password, PEMCipher)
   103  		if err != nil {
   104  			return err
   105  		}
   106  		privateKey = pem.EncodeToMemory(encrypted)
   107  	} else {
   108  		privateKey = pem.EncodeToMemory(privBlock)
   109  	}
   110  
   111  	if err := os.WriteFile(privateKeyFilePath, privateKey, PrivKeyFilePermissions); err != nil {
   112  		return err
   113  	}
   114  
   115  	return os.WriteFile(publicKeyFilePath, pem.EncodeToMemory(pubBlock), PubKeyFilePermissions)
   116  }