main
1// Package ecc implements a generic interface for ECDH, ECDSA, and EdDSA.
2package ecc
3
4import (
5 "crypto/subtle"
6 "io"
7
8 "github.com/ProtonMail/go-crypto/openpgp/errors"
9 x448lib "github.com/cloudflare/circl/dh/x448"
10)
11
12type x448 struct{}
13
14func NewX448() *x448 {
15 return &x448{}
16}
17
18func (c *x448) GetCurveName() string {
19 return "x448"
20}
21
22// MarshalBytePoint encodes the public point from native format, adding the prefix.
23// See https://datatracker.ietf.org/doc/html/draft-ietf-openpgp-crypto-refresh-06#section-5.5.5.6
24func (c *x448) MarshalBytePoint(point []byte) []byte {
25 return append([]byte{0x40}, point...)
26}
27
28// UnmarshalBytePoint decodes a point from prefixed format to native.
29// See https://datatracker.ietf.org/doc/html/draft-ietf-openpgp-crypto-refresh-06#section-5.5.5.6
30func (c *x448) UnmarshalBytePoint(point []byte) []byte {
31 if len(point) != x448lib.Size+1 {
32 return nil
33 }
34
35 return point[1:]
36}
37
38// MarshalByteSecret encoded a scalar from native format to prefixed.
39// See https://datatracker.ietf.org/doc/html/draft-ietf-openpgp-crypto-refresh-06#section-5.5.5.6.1.2
40func (c *x448) MarshalByteSecret(d []byte) []byte {
41 return append([]byte{0x40}, d...)
42}
43
44// UnmarshalByteSecret decodes a scalar from prefixed format to native.
45// See https://datatracker.ietf.org/doc/html/draft-ietf-openpgp-crypto-refresh-06#section-5.5.5.6.1.2
46func (c *x448) UnmarshalByteSecret(d []byte) []byte {
47 if len(d) != x448lib.Size+1 {
48 return nil
49 }
50
51 // Store without prefix
52 return d[1:]
53}
54
55func (c *x448) generateKeyPairBytes(rand io.Reader) (sk, pk x448lib.Key, err error) {
56 if _, err = rand.Read(sk[:]); err != nil {
57 return
58 }
59
60 x448lib.KeyGen(&pk, &sk)
61 return
62}
63
64func (c *x448) GenerateECDH(rand io.Reader) (point []byte, secret []byte, err error) {
65 priv, pub, err := c.generateKeyPairBytes(rand)
66 if err != nil {
67 return
68 }
69
70 return pub[:], priv[:], nil
71}
72
73func (c *x448) Encaps(rand io.Reader, point []byte) (ephemeral, sharedSecret []byte, err error) {
74 var pk, ss x448lib.Key
75 seed, e, err := c.generateKeyPairBytes(rand)
76 if err != nil {
77 return nil, nil, err
78 }
79 copy(pk[:], point)
80 x448lib.Shared(&ss, &seed, &pk)
81
82 return e[:], ss[:], nil
83}
84
85func (c *x448) Decaps(ephemeral, secret []byte) (sharedSecret []byte, err error) {
86 var ss, sk, e x448lib.Key
87
88 copy(sk[:], secret)
89 copy(e[:], ephemeral)
90 x448lib.Shared(&ss, &sk, &e)
91
92 return ss[:], nil
93}
94
95func (c *x448) ValidateECDH(point []byte, secret []byte) error {
96 var sk, pk, expectedPk x448lib.Key
97
98 copy(pk[:], point)
99 copy(sk[:], secret)
100 x448lib.KeyGen(&expectedPk, &sk)
101
102 if subtle.ConstantTimeCompare(expectedPk[:], pk[:]) == 0 {
103 return errors.KeyInvalidError("ecc: invalid curve25519 public point")
104 }
105
106 return nil
107}