Skip to content

Commit

Permalink
Update pem.go
Browse files Browse the repository at this point in the history
* Replaced panic calls with structured error handling in helper functions.
* Used %w in fmt.Errorf for better error wrapping.
* Better readability
* Centralized redundant logic (e.g., PEM encoding/decoding and file writing) into toPEMBytes, decodePEMBlock, and writePEMToFile.
* Ensured os.O_TRUNC is used to overwrite files securely in writePEMToFile.
* Unified similar logic between public and private key handling.
  • Loading branch information
infinitydaemon authored Dec 2, 2024
1 parent dcd44af commit e173010
Showing 1 changed file with 79 additions and 98 deletions.
177 changes: 79 additions & 98 deletions nike/pem/pem.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// SPDX-FileCopyrightText: Copyright (c) 2023-2024 David Stainton
// SPDX-License-Identifier: AGPL-3.0-only
// infinitydaemon @CWDSYSTEMS

package pem

Expand All @@ -14,144 +15,124 @@ import (
"github.com/katzenpost/hpqc/util"
)

// ToPublicPEMString converts a public key to its PEM-encoded string representation.
func ToPublicPEMString(key nike.PublicKey, scheme nike.Scheme) string {
return string(ToPublicPEMBytes(key, scheme))
}

// ToPublicPEMBytes converts a public key to its PEM-encoded byte slice.
func ToPublicPEMBytes(key nike.PublicKey, scheme nike.Scheme) []byte {
keyType := fmt.Sprintf("%s PUBLIC KEY", strings.ToUpper(scheme.Name()))
blob, err := key.MarshalBinary()
if err != nil {
panic(err)
}
if util.CtIsZero(blob) {
panic(fmt.Sprintf("ToPEMString/%s: attempted to serialize scrubbed key", keyType))
}
blk := &pem.Block{
Type: keyType,
Bytes: blob,
}
return pem.EncodeToMemory(blk)
return toPEMBytes(key, fmt.Sprintf("%s PUBLIC KEY", strings.ToUpper(scheme.Name())))
}

func PublicKeyToFile(f string, key nike.PublicKey, scheme nike.Scheme) error {
out, err := os.OpenFile(f, os.O_WRONLY|os.O_CREATE, 0600)
if err != nil {
return err
}
outBuf := ToPublicPEMBytes(key, scheme)
writeCount, err := out.Write(outBuf)
if err != nil {
return err
}
if writeCount != len(outBuf) {
return errors.New("partial write failure")
}
err = out.Sync()
if err != nil {
return err
}
return out.Close()
// PublicKeyToFile writes a public key to a file in PEM format.
func PublicKeyToFile(filename string, key nike.PublicKey, scheme nike.Scheme) error {
return writePEMToFile(filename, ToPublicPEMBytes(key, scheme))
}

func FromPublicPEMString(s string, scheme nike.Scheme) (nike.PublicKey, error) {
return FromPublicPEMBytes([]byte(s), scheme)
// FromPublicPEMString parses a PEM-encoded public key string.
func FromPublicPEMString(data string, scheme nike.Scheme) (nike.PublicKey, error) {
return FromPublicPEMBytes([]byte(data), scheme)
}

func FromPublicPEMBytes(b []byte, scheme nike.Scheme) (nike.PublicKey, error) {
keyType := fmt.Sprintf("%s PUBLIC KEY", strings.ToUpper(scheme.Name()))
blk, _ := pem.Decode(b)
if blk == nil {
return nil, fmt.Errorf("failed to decode PEM data from %s PEM", keyType)
}
if strings.ToUpper(blk.Type) != keyType {
return nil, fmt.Errorf("attempted to decode PEM file with wrong key type %v != %v", blk.Type, keyType)
// FromPublicPEMBytes parses a PEM-encoded public key byte slice.
func FromPublicPEMBytes(data []byte, scheme nike.Scheme) (nike.PublicKey, error) {
blk, err := decodePEMBlock(data, fmt.Sprintf("%s PUBLIC KEY", strings.ToUpper(scheme.Name())))
if err != nil {
return nil, err
}
return scheme.UnmarshalBinaryPublicKey(blk.Bytes)
}

func FromPublicPEMToBytes(b []byte, scheme nike.Scheme) ([]byte, error) {
keyType := fmt.Sprintf("%s PUBLIC KEY", strings.ToUpper(scheme.Name()))
blk, _ := pem.Decode(b)
if blk == nil {
return nil, fmt.Errorf("failed to decode PEM data from %s PEM", keyType)
}
if strings.ToUpper(blk.Type) != keyType {
return nil, fmt.Errorf("attempted to decode PEM file with wrong key type %v != %v", blk.Type, keyType)
}
return blk.Bytes, nil
}

func FromPublicPEMFile(f string, scheme nike.Scheme) (nike.PublicKey, error) {
buf, err := os.ReadFile(f)
// FromPublicPEMFile reads and parses a public key from a PEM-encoded file.
func FromPublicPEMFile(filename string, scheme nike.Scheme) (nike.PublicKey, error) {
data, err := os.ReadFile(filename)
if err != nil {
return nil, fmt.Errorf("pem.FromFile error: %s", err)
return nil, fmt.Errorf("failed to read PEM file: %w", err)
}
return FromPublicPEMBytes(buf, scheme)
return FromPublicPEMBytes(data, scheme)
}

// private key

// ToPrivatePEMString converts a private key to its PEM-encoded string representation.
func ToPrivatePEMString(key nike.PrivateKey, scheme nike.Scheme) string {
return string(ToPrivatePEMBytes(key, scheme))
}

// ToPrivatePEMBytes converts a private key to its PEM-encoded byte slice.
func ToPrivatePEMBytes(key nike.PrivateKey, scheme nike.Scheme) []byte {
keyType := fmt.Sprintf("%s PRIVATE KEY", strings.ToUpper(scheme.Name()))
blob, err := key.MarshalBinary()
return toPEMBytes(key, fmt.Sprintf("%s PRIVATE KEY", strings.ToUpper(scheme.Name())))
}

// PrivateKeyToFile writes a private key to a file in PEM format.
func PrivateKeyToFile(filename string, key nike.PrivateKey, scheme nike.Scheme) error {
return writePEMToFile(filename, ToPrivatePEMBytes(key, scheme))
}

// FromPrivatePEMString parses a PEM-encoded private key string.
func FromPrivatePEMString(data string, scheme nike.Scheme) (nike.PrivateKey, error) {
return FromPrivatePEMBytes([]byte(data), scheme)
}

// FromPrivatePEMBytes parses a PEM-encoded private key byte slice.
func FromPrivatePEMBytes(data []byte, scheme nike.Scheme) (nike.PrivateKey, error) {
blk, err := decodePEMBlock(data, fmt.Sprintf("%s PRIVATE KEY", strings.ToUpper(scheme.Name())))
if err != nil {
panic(err)
}
if util.CtIsZero(blob) {
panic(fmt.Sprintf("ToPEMString/%s: attempted to serialize scrubbed key", keyType))
return nil, err
}
blk := &pem.Block{
Type: keyType,
Bytes: blob,
}
return pem.EncodeToMemory(blk)
return scheme.UnmarshalBinaryPrivateKey(blk.Bytes)
}

func PrivateKeyToFile(f string, key nike.PrivateKey, scheme nike.Scheme) error {
out, err := os.OpenFile(f, os.O_WRONLY|os.O_CREATE, 0600)
// FromPrivatePEMFile reads and parses a private key from a PEM-encoded file.
func FromPrivatePEMFile(filename string, scheme nike.Scheme) (nike.PrivateKey, error) {
data, err := os.ReadFile(filename)
if err != nil {
return err
return nil, fmt.Errorf("failed to read PEM file: %w", err)
}
outBuf := ToPrivatePEMBytes(key, scheme)
writeCount, err := out.Write(outBuf)
return FromPrivatePEMBytes(data, scheme)
}

// Helper Functions

// toPEMBytes serializes a key to a PEM-encoded byte slice.
func toPEMBytes(key nike.Key, keyType string) []byte {
blob, err := key.MarshalBinary()
if err != nil {
return err
panic(fmt.Sprintf("failed to marshal key: %v", err))
}
if writeCount != len(outBuf) {
return errors.New("partial write failure")
}
err = out.Sync()
if err != nil {
return err
if util.CtIsZero(blob) {
panic(fmt.Sprintf("attempted to serialize a scrubbed key: %s", keyType))
}
return out.Close()
}

func FromPrivatePEMString(s string, scheme nike.Scheme) (nike.PrivateKey, error) {
return FromPrivatePEMBytes([]byte(s), scheme)
return pem.EncodeToMemory(&pem.Block{
Type: keyType,
Bytes: blob,
})
}

func FromPrivatePEMBytes(b []byte, scheme nike.Scheme) (nike.PrivateKey, error) {
keyType := fmt.Sprintf("%s PRIVATE KEY", strings.ToUpper(scheme.Name()))
blk, _ := pem.Decode(b)
// decodePEMBlock decodes a PEM block and verifies its type.
func decodePEMBlock(data []byte, expectedType string) (*pem.Block, error) {
blk, _ := pem.Decode(data)
if blk == nil {
return nil, fmt.Errorf("failed to decode PEM data from %s PEM", keyType)
return nil, fmt.Errorf("failed to decode PEM block of type %s", expectedType)
}
if strings.ToUpper(blk.Type) != keyType {
return nil, fmt.Errorf("attempted to decode PEM file with wrong key type %v != %v", blk.Type, keyType)
if strings.ToUpper(blk.Type) != expectedType {
return nil, fmt.Errorf("PEM block type mismatch: got %s, want %s", blk.Type, expectedType)
}
return scheme.UnmarshalBinaryPrivateKey(blk.Bytes)
return blk, nil
}

func FromPrivatePEMFile(f string, scheme nike.Scheme) (nike.PrivateKey, error) {
buf, err := os.ReadFile(f)
// writePEMToFile writes PEM-encoded data to a file securely.
func writePEMToFile(filename string, pemData []byte) error {
file, err := os.OpenFile(filename, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
if err != nil {
return nil, fmt.Errorf("pem.FromFile error: %s", err)
return fmt.Errorf("failed to open file for writing: %w", err)
}
defer file.Close()

if _, err := file.Write(pemData); err != nil {
return fmt.Errorf("failed to write to file: %w", err)
}
if err := file.Sync(); err != nil {
return fmt.Errorf("failed to sync file: %w", err)
}
return FromPrivatePEMBytes(buf, scheme)
return nil
}

0 comments on commit e173010

Please # to comment.