main
1// Package ed448 implements the ed448 signature algorithm for OpenPGP
2// as defined in the Open PGP crypto refresh.
3package ed448
4
5import (
6 "crypto/subtle"
7 "io"
8
9 "github.com/ProtonMail/go-crypto/openpgp/errors"
10 ed448lib "github.com/cloudflare/circl/sign/ed448"
11)
12
13const (
14 // PublicKeySize is the size, in bytes, of public keys in this package.
15 PublicKeySize = ed448lib.PublicKeySize
16 // SeedSize is the size, in bytes, of private key seeds.
17 // The private key representation used by RFC 8032.
18 SeedSize = ed448lib.SeedSize
19 // SignatureSize is the size, in bytes, of signatures generated and verified by this package.
20 SignatureSize = ed448lib.SignatureSize
21)
22
23type PublicKey struct {
24 // Point represents the elliptic curve point of the public key.
25 Point []byte
26}
27
28type PrivateKey struct {
29 PublicKey
30 // Key the private key representation by RFC 8032,
31 // encoded as seed | public key point.
32 Key []byte
33}
34
35// NewPublicKey creates a new empty ed448 public key.
36func NewPublicKey() *PublicKey {
37 return &PublicKey{}
38}
39
40// NewPrivateKey creates a new empty private key referencing the public key.
41func NewPrivateKey(key PublicKey) *PrivateKey {
42 return &PrivateKey{
43 PublicKey: key,
44 }
45}
46
47// Seed returns the ed448 private key secret seed.
48// The private key representation by RFC 8032.
49func (pk *PrivateKey) Seed() []byte {
50 return pk.Key[:SeedSize]
51}
52
53// MarshalByteSecret returns the underlying seed of the private key.
54func (pk *PrivateKey) MarshalByteSecret() []byte {
55 return pk.Seed()
56}
57
58// UnmarshalByteSecret computes the private key from the secret seed
59// and stores it in the private key object.
60func (sk *PrivateKey) UnmarshalByteSecret(seed []byte) error {
61 sk.Key = ed448lib.NewKeyFromSeed(seed)
62 return nil
63}
64
65// GenerateKey generates a fresh private key with the provided randomness source.
66func GenerateKey(rand io.Reader) (*PrivateKey, error) {
67 publicKey, privateKey, err := ed448lib.GenerateKey(rand)
68 if err != nil {
69 return nil, err
70 }
71 privateKeyOut := new(PrivateKey)
72 privateKeyOut.PublicKey.Point = publicKey[:]
73 privateKeyOut.Key = privateKey[:]
74 return privateKeyOut, nil
75}
76
77// Sign signs a message with the ed448 algorithm.
78// priv MUST be a valid key! Check this with Validate() before use.
79func Sign(priv *PrivateKey, message []byte) ([]byte, error) {
80 // Ed448 is used with the empty string as a context string.
81 // See https://datatracker.ietf.org/doc/html/draft-ietf-openpgp-crypto-refresh-08#section-13.7
82 return ed448lib.Sign(priv.Key, message, ""), nil
83}
84
85// Verify verifies a ed448 signature
86func Verify(pub *PublicKey, message []byte, signature []byte) bool {
87 // Ed448 is used with the empty string as a context string.
88 // See https://datatracker.ietf.org/doc/html/draft-ietf-openpgp-crypto-refresh-08#section-13.7
89 return ed448lib.Verify(pub.Point, message, signature, "")
90}
91
92// Validate checks if the ed448 private key is valid
93func Validate(priv *PrivateKey) error {
94 expectedPrivateKey := ed448lib.NewKeyFromSeed(priv.Seed())
95 if subtle.ConstantTimeCompare(priv.Key, expectedPrivateKey) == 0 {
96 return errors.KeyInvalidError("ed448: invalid ed448 secret")
97 }
98 if subtle.ConstantTimeCompare(priv.PublicKey.Point, expectedPrivateKey[SeedSize:]) == 0 {
99 return errors.KeyInvalidError("ed448: invalid ed448 public key")
100 }
101 return nil
102}
103
104// ENCODING/DECODING signature:
105
106// WriteSignature encodes and writes an ed448 signature to writer.
107func WriteSignature(writer io.Writer, signature []byte) error {
108 _, err := writer.Write(signature)
109 return err
110}
111
112// ReadSignature decodes an ed448 signature from a reader.
113func ReadSignature(reader io.Reader) ([]byte, error) {
114 signature := make([]byte, SignatureSize)
115 if _, err := io.ReadFull(reader, signature); err != nil {
116 return nil, err
117 }
118 return signature, nil
119}