2022-08-26 22:26:53 +02:00
|
|
|
// Package ecc implements a generic interface for ECDH, ECDSA, and EdDSA.
|
|
|
|
package ecc
|
|
|
|
|
|
|
|
import (
|
|
|
|
"crypto/subtle"
|
|
|
|
"io"
|
|
|
|
|
|
|
|
"github.com/ProtonMail/go-crypto/openpgp/errors"
|
|
|
|
x448lib "github.com/cloudflare/circl/dh/x448"
|
|
|
|
)
|
|
|
|
|
2023-12-13 14:29:02 +01:00
|
|
|
type x448 struct{}
|
2022-08-26 22:26:53 +02:00
|
|
|
|
|
|
|
func NewX448() *x448 {
|
|
|
|
return &x448{}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *x448) GetCurveName() string {
|
|
|
|
return "x448"
|
|
|
|
}
|
|
|
|
|
|
|
|
// MarshalBytePoint encodes the public point from native format, adding the prefix.
|
|
|
|
// See https://datatracker.ietf.org/doc/html/draft-ietf-openpgp-crypto-refresh-06#section-5.5.5.6
|
|
|
|
func (c *x448) MarshalBytePoint(point []byte) []byte {
|
|
|
|
return append([]byte{0x40}, point...)
|
|
|
|
}
|
|
|
|
|
|
|
|
// UnmarshalBytePoint decodes a point from prefixed format to native.
|
|
|
|
// See https://datatracker.ietf.org/doc/html/draft-ietf-openpgp-crypto-refresh-06#section-5.5.5.6
|
|
|
|
func (c *x448) UnmarshalBytePoint(point []byte) []byte {
|
2023-12-13 14:29:02 +01:00
|
|
|
if len(point) != x448lib.Size+1 {
|
2022-08-26 22:26:53 +02:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
return point[1:]
|
|
|
|
}
|
|
|
|
|
|
|
|
// MarshalByteSecret encoded a scalar from native format to prefixed.
|
|
|
|
// See https://datatracker.ietf.org/doc/html/draft-ietf-openpgp-crypto-refresh-06#section-5.5.5.6.1.2
|
|
|
|
func (c *x448) MarshalByteSecret(d []byte) []byte {
|
|
|
|
return append([]byte{0x40}, d...)
|
|
|
|
}
|
|
|
|
|
|
|
|
// UnmarshalByteSecret decodes a scalar from prefixed format to native.
|
|
|
|
// See https://datatracker.ietf.org/doc/html/draft-ietf-openpgp-crypto-refresh-06#section-5.5.5.6.1.2
|
|
|
|
func (c *x448) UnmarshalByteSecret(d []byte) []byte {
|
2023-12-13 14:29:02 +01:00
|
|
|
if len(d) != x448lib.Size+1 {
|
2022-08-26 22:26:53 +02:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Store without prefix
|
|
|
|
return d[1:]
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *x448) generateKeyPairBytes(rand io.Reader) (sk, pk x448lib.Key, err error) {
|
|
|
|
if _, err = rand.Read(sk[:]); err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
x448lib.KeyGen(&pk, &sk)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *x448) GenerateECDH(rand io.Reader) (point []byte, secret []byte, err error) {
|
|
|
|
priv, pub, err := c.generateKeyPairBytes(rand)
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
return pub[:], priv[:], nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *x448) Encaps(rand io.Reader, point []byte) (ephemeral, sharedSecret []byte, err error) {
|
|
|
|
var pk, ss x448lib.Key
|
|
|
|
seed, e, err := c.generateKeyPairBytes(rand)
|
2023-12-13 14:29:02 +01:00
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
2022-08-26 22:26:53 +02:00
|
|
|
copy(pk[:], point)
|
|
|
|
x448lib.Shared(&ss, &seed, &pk)
|
|
|
|
|
|
|
|
return e[:], ss[:], nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *x448) Decaps(ephemeral, secret []byte) (sharedSecret []byte, err error) {
|
|
|
|
var ss, sk, e x448lib.Key
|
|
|
|
|
|
|
|
copy(sk[:], secret)
|
|
|
|
copy(e[:], ephemeral)
|
|
|
|
x448lib.Shared(&ss, &sk, &e)
|
|
|
|
|
|
|
|
return ss[:], nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *x448) ValidateECDH(point []byte, secret []byte) error {
|
|
|
|
var sk, pk, expectedPk x448lib.Key
|
|
|
|
|
|
|
|
copy(pk[:], point)
|
|
|
|
copy(sk[:], secret)
|
|
|
|
x448lib.KeyGen(&expectedPk, &sk)
|
|
|
|
|
|
|
|
if subtle.ConstantTimeCompare(expectedPk[:], pk[:]) == 0 {
|
|
|
|
return errors.KeyInvalidError("ecc: invalid curve25519 public point")
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|