File: ed448.go

package info (click to toggle)
golang-github-protonmail-go-crypto 1.3.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 1,932 kB
  • sloc: makefile: 10
file content (119 lines) | stat: -rw-r--r-- 3,823 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
// Package ecc implements a generic interface for ECDH, ECDSA, and EdDSA.
package ecc

import (
	"bytes"
	"crypto/subtle"
	"io"

	"github.com/ProtonMail/go-crypto/openpgp/errors"
	ed448lib "github.com/cloudflare/circl/sign/ed448"
)

type ed448 struct{}

func NewEd448() *ed448 {
	return &ed448{}
}

func (c *ed448) GetCurveName() string {
	return "ed448"
}

// 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.5
func (c *ed448) MarshalBytePoint(x []byte) []byte {
	// Return prefixed
	return append([]byte{0x40}, x...)
}

// 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.5
func (c *ed448) UnmarshalBytePoint(point []byte) (x []byte) {
	if len(point) != ed448lib.PublicKeySize+1 {
		return nil
	}

	// Strip prefix
	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.5
func (c *ed448) MarshalByteSecret(d []byte) []byte {
	// Return prefixed
	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.5
func (c *ed448) UnmarshalByteSecret(s []byte) (d []byte) {
	// Check prefixed size
	if len(s) != ed448lib.SeedSize+1 {
		return nil
	}

	// Strip prefix
	return s[1:]
}

// MarshalSignature splits a signature in R and S, where R is in prefixed native format and
// S is an MPI with value zero.
// See https://datatracker.ietf.org/doc/html/draft-ietf-openpgp-crypto-refresh-06#section-5.2.3.3.2
func (c *ed448) MarshalSignature(sig []byte) (r, s []byte) {
	return append([]byte{0x40}, sig...), []byte{}
}

// UnmarshalSignature decodes R and S in the native format. Only R is used, in prefixed native format.
// See https://datatracker.ietf.org/doc/html/draft-ietf-openpgp-crypto-refresh-06#section-5.2.3.3.2
func (c *ed448) UnmarshalSignature(r, s []byte) (sig []byte) {
	if len(r) != ed448lib.SignatureSize+1 {
		return nil
	}

	return r[1:]
}

func (c *ed448) GenerateEdDSA(rand io.Reader) (pub, priv []byte, err error) {
	pk, sk, err := ed448lib.GenerateKey(rand)

	if err != nil {
		return nil, nil, err
	}

	return pk, sk[:ed448lib.SeedSize], nil
}

func getEd448Sk(publicKey, privateKey []byte) ed448lib.PrivateKey {
	privateKeyCap, privateKeyLen, publicKeyLen := cap(privateKey), len(privateKey), len(publicKey)

	if privateKeyCap >= privateKeyLen+publicKeyLen &&
		bytes.Equal(privateKey[privateKeyLen:privateKeyLen+publicKeyLen], publicKey) {
		return privateKey[:privateKeyLen+publicKeyLen]
	}

	return append(privateKey[:privateKeyLen:privateKeyLen], publicKey...)
}

func (c *ed448) Sign(publicKey, privateKey, message []byte) (sig []byte, err error) {
	// Ed448 is used with the empty string as a context string.
	// See https://datatracker.ietf.org/doc/html/draft-ietf-openpgp-crypto-refresh-06#section-13.7
	sig = ed448lib.Sign(getEd448Sk(publicKey, privateKey), message, "")

	return sig, nil
}

func (c *ed448) Verify(publicKey, message, sig []byte) bool {
	// Ed448 is used with the empty string as a context string.
	// See https://datatracker.ietf.org/doc/html/draft-ietf-openpgp-crypto-refresh-06#section-13.7
	return ed448lib.Verify(publicKey, message, sig, "")
}

func (c *ed448) ValidateEdDSA(publicKey, privateKey []byte) (err error) {
	priv := getEd448Sk(publicKey, privateKey)
	expectedPriv := ed448lib.NewKeyFromSeed(priv.Seed())
	if subtle.ConstantTimeCompare(priv, expectedPriv) == 0 {
		return errors.KeyInvalidError("ecc: invalid ed448 secret")
	}
	return nil
}