main
Raw Download raw file
  1// Package ecc implements a generic interface for ECDH, ECDSA, and EdDSA.
  2package ecc
  3
  4import (
  5	"bytes"
  6	"crypto/subtle"
  7	"io"
  8
  9	"github.com/ProtonMail/go-crypto/openpgp/errors"
 10	ed448lib "github.com/cloudflare/circl/sign/ed448"
 11)
 12
 13type ed448 struct{}
 14
 15func NewEd448() *ed448 {
 16	return &ed448{}
 17}
 18
 19func (c *ed448) GetCurveName() string {
 20	return "ed448"
 21}
 22
 23// MarshalBytePoint encodes the public point from native format, adding the prefix.
 24// See https://datatracker.ietf.org/doc/html/draft-ietf-openpgp-crypto-refresh-06#section-5.5.5.5
 25func (c *ed448) MarshalBytePoint(x []byte) []byte {
 26	// Return prefixed
 27	return append([]byte{0x40}, x...)
 28}
 29
 30// UnmarshalBytePoint decodes a point from prefixed format to native.
 31// See https://datatracker.ietf.org/doc/html/draft-ietf-openpgp-crypto-refresh-06#section-5.5.5.5
 32func (c *ed448) UnmarshalBytePoint(point []byte) (x []byte) {
 33	if len(point) != ed448lib.PublicKeySize+1 {
 34		return nil
 35	}
 36
 37	// Strip prefix
 38	return point[1:]
 39}
 40
 41// MarshalByteSecret encoded a scalar from native format to prefixed.
 42// See https://datatracker.ietf.org/doc/html/draft-ietf-openpgp-crypto-refresh-06#section-5.5.5.5
 43func (c *ed448) MarshalByteSecret(d []byte) []byte {
 44	// Return prefixed
 45	return append([]byte{0x40}, d...)
 46}
 47
 48// UnmarshalByteSecret decodes a scalar from prefixed format to native.
 49// See https://datatracker.ietf.org/doc/html/draft-ietf-openpgp-crypto-refresh-06#section-5.5.5.5
 50func (c *ed448) UnmarshalByteSecret(s []byte) (d []byte) {
 51	// Check prefixed size
 52	if len(s) != ed448lib.SeedSize+1 {
 53		return nil
 54	}
 55
 56	// Strip prefix
 57	return s[1:]
 58}
 59
 60// MarshalSignature splits a signature in R and S, where R is in prefixed native format and
 61// S is an MPI with value zero.
 62// See https://datatracker.ietf.org/doc/html/draft-ietf-openpgp-crypto-refresh-06#section-5.2.3.3.2
 63func (c *ed448) MarshalSignature(sig []byte) (r, s []byte) {
 64	return append([]byte{0x40}, sig...), []byte{}
 65}
 66
 67// UnmarshalSignature decodes R and S in the native format. Only R is used, in prefixed native format.
 68// See https://datatracker.ietf.org/doc/html/draft-ietf-openpgp-crypto-refresh-06#section-5.2.3.3.2
 69func (c *ed448) UnmarshalSignature(r, s []byte) (sig []byte) {
 70	if len(r) != ed448lib.SignatureSize+1 {
 71		return nil
 72	}
 73
 74	return r[1:]
 75}
 76
 77func (c *ed448) GenerateEdDSA(rand io.Reader) (pub, priv []byte, err error) {
 78	pk, sk, err := ed448lib.GenerateKey(rand)
 79
 80	if err != nil {
 81		return nil, nil, err
 82	}
 83
 84	return pk, sk[:ed448lib.SeedSize], nil
 85}
 86
 87func getEd448Sk(publicKey, privateKey []byte) ed448lib.PrivateKey {
 88	privateKeyCap, privateKeyLen, publicKeyLen := cap(privateKey), len(privateKey), len(publicKey)
 89
 90	if privateKeyCap >= privateKeyLen+publicKeyLen &&
 91		bytes.Equal(privateKey[privateKeyLen:privateKeyLen+publicKeyLen], publicKey) {
 92		return privateKey[:privateKeyLen+publicKeyLen]
 93	}
 94
 95	return append(privateKey[:privateKeyLen:privateKeyLen], publicKey...)
 96}
 97
 98func (c *ed448) Sign(publicKey, privateKey, message []byte) (sig []byte, err error) {
 99	// Ed448 is used with the empty string as a context string.
100	// See https://datatracker.ietf.org/doc/html/draft-ietf-openpgp-crypto-refresh-06#section-13.7
101	sig = ed448lib.Sign(getEd448Sk(publicKey, privateKey), message, "")
102
103	return sig, nil
104}
105
106func (c *ed448) Verify(publicKey, message, sig []byte) bool {
107	// Ed448 is used with the empty string as a context string.
108	// See https://datatracker.ietf.org/doc/html/draft-ietf-openpgp-crypto-refresh-06#section-13.7
109	return ed448lib.Verify(publicKey, message, sig, "")
110}
111
112func (c *ed448) ValidateEdDSA(publicKey, privateKey []byte) (err error) {
113	priv := getEd448Sk(publicKey, privateKey)
114	expectedPriv := ed448lib.NewKeyFromSeed(priv.Seed())
115	if subtle.ConstantTimeCompare(priv, expectedPriv) == 0 {
116		return errors.KeyInvalidError("ecc: invalid ed448 secret")
117	}
118	return nil
119}