summaryrefslogtreecommitdiff
path: root/crypto/curve25519.go
blob: a8637399d732c65945567b5df0a9b8ef5e804d1a (plain)
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
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
// Copyright (C) 2019-2023 Algorand, Inc.
// This file is part of go-algorand
//
// go-algorand is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// go-algorand is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with go-algorand.  If not, see <https://www.gnu.org/licenses/>.

package crypto

// #cgo CFLAGS: -Wall -std=c99
// #cgo darwin,amd64 CFLAGS: -I${SRCDIR}/libs/darwin/amd64/include
// #cgo darwin,amd64 LDFLAGS: ${SRCDIR}/libs/darwin/amd64/lib/libsodium.a
// #cgo darwin,arm64 CFLAGS: -I${SRCDIR}/libs/darwin/arm64/include
// #cgo darwin,arm64 LDFLAGS: ${SRCDIR}/libs/darwin/arm64/lib/libsodium.a
// #cgo linux,amd64 CFLAGS: -I${SRCDIR}/libs/linux/amd64/include
// #cgo linux,amd64 LDFLAGS: ${SRCDIR}/libs/linux/amd64/lib/libsodium.a
// #cgo linux,arm64 CFLAGS: -I${SRCDIR}/libs/linux/arm64/include
// #cgo linux,arm64 LDFLAGS: ${SRCDIR}/libs/linux/arm64/lib/libsodium.a
// #cgo linux,arm CFLAGS: -I${SRCDIR}/libs/linux/arm/include
// #cgo linux,arm LDFLAGS: ${SRCDIR}/libs/linux/arm/lib/libsodium.a
// #cgo windows,amd64 CFLAGS: -I${SRCDIR}/libs/windows/amd64/include
// #cgo windows,amd64 LDFLAGS: ${SRCDIR}/libs/windows/amd64/lib/libsodium.a
// #include <stdint.h>
// #include "sodium.h"
import "C"

import (
	"fmt"
	"unsafe"

	"github.com/algorand/go-algorand/logging"
	"github.com/algorand/go-algorand/util/metrics"
)

// TODO: Remove metrics from crypto package
var cryptoVRFGenerateTotal = metrics.MakeCounter(metrics.CryptoVRFGenerateTotal)
var cryptoVRFProveTotal = metrics.MakeCounter(metrics.CryptoVRFProveTotal)
var cryptoVRFHashTotal = metrics.MakeCounter(metrics.CryptoVRFHashTotal)
var cryptoVRFVerifyTotal = metrics.MakeCounter(metrics.CryptoVRFVerifyTotal)
var cryptoGenSigSecretsTotal = metrics.MakeCounter(metrics.CryptoGenSigSecretsTotal)
var cryptoSigSecretsSignTotal = metrics.MakeCounter(metrics.CryptoSigSecretsSignTotal)
var cryptoSigSecretsSignBytesTotal = metrics.MakeCounter(metrics.CryptoSigSecretsSignBytesTotal)
var cryptoSigSecretsVerifyTotal = metrics.MakeCounter(metrics.CryptoSigSecretsVerifyTotal)
var cryptoSigSecretsVerifyBytesTotal = metrics.MakeCounter(metrics.CryptoSigSecretsVerifyBytesTotal)

const masterDerivationKeyLenBytes = 32

func init() {
	if C.sodium_init() < 0 {
		logging.Init()
		logging.Base().Fatal("failed to initialize libsodium!")
	}

	// Check sizes of structs
	_ = [C.crypto_sign_ed25519_BYTES]byte(ed25519Signature{})
	_ = [C.crypto_sign_ed25519_PUBLICKEYBYTES]byte(ed25519PublicKey{})
	_ = [C.crypto_sign_ed25519_SECRETKEYBYTES]byte(ed25519PrivateKey{})
	_ = [C.crypto_sign_ed25519_SEEDBYTES]byte(ed25519Seed{})

	// Check that this platform makes slices []Signature and []SignatureVerifier that use a backing
	// array of contiguously allocated 64- and 32-byte segments, respectively, with no padding.
	// These slice's backing arrays are passed to C.ed25519_batch_wrapper. In practice, this check
	// should always succeed, but to be careful we can double-check, since the Go specification does
	// not explicitly define platform-specific alignment sizes and slice allocation behavior.
	length := 1024
	sigs := make([]Signature, length)        // same as [][64]byte
	pks := make([]SignatureVerifier, length) // same as [][32]byte

	for i := 1; i < length; i++ {
		if uintptr(unsafe.Pointer(&sigs[i]))-uintptr(unsafe.Pointer(&sigs[0])) != uintptr(i)*C.crypto_sign_ed25519_BYTES {
			panic("Unexpected alignment for a slice of signatures")
		}
		if uintptr(unsafe.Pointer(&pks[i]))-uintptr(unsafe.Pointer(&pks[0])) != uintptr(i)*C.crypto_sign_ed25519_PUBLICKEYBYTES {
			panic("Unexpected alignment for a slice of public keys")
		}
	}
	if uintptr(unsafe.Pointer(&sigs[length-1]))-uintptr(unsafe.Pointer(&sigs[0])) != uintptr(length-1)*C.crypto_sign_ed25519_BYTES {
		panic("Unexpected total size for a backing array of signatures")
	}
	if uintptr(unsafe.Pointer(&pks[length-1]))-uintptr(unsafe.Pointer(&pks[0])) != uintptr(length-1)*C.crypto_sign_ed25519_PUBLICKEYBYTES {
		panic("Unexpected total size for a backing array of public keys")
	}
}

// A Seed holds the entropy needed to generate cryptographic keys.
type Seed ed25519Seed

/* Classical signatures */
type ed25519Signature [64]byte
type ed25519PublicKey [32]byte
type ed25519PrivateKey [64]byte
type ed25519Seed [32]byte

// MasterDerivationKey is used to derive ed25519 keys for use in wallets
type MasterDerivationKey [masterDerivationKeyLenBytes]byte

// PrivateKey is an exported ed25519PrivateKey
type PrivateKey ed25519PrivateKey

// PublicKey is an exported ed25519PublicKey
type PublicKey ed25519PublicKey

func ed25519GenerateKey() (public ed25519PublicKey, secret ed25519PrivateKey) {
	var seed ed25519Seed
	RandBytes(seed[:])
	return ed25519GenerateKeySeed(seed)
}

func ed25519GenerateKeyRNG(rng RNG) (public ed25519PublicKey, secret ed25519PrivateKey) {
	var seed ed25519Seed
	rng.RandBytes(seed[:])
	return ed25519GenerateKeySeed(seed)
}

func ed25519GenerateKeySeed(seed ed25519Seed) (public ed25519PublicKey, secret ed25519PrivateKey) {
	C.crypto_sign_ed25519_seed_keypair((*C.uchar)(&public[0]), (*C.uchar)(&secret[0]), (*C.uchar)(&seed[0]))
	return
}

func ed25519Sign(secret ed25519PrivateKey, data []byte) (sig ed25519Signature) {
	// &data[0] will make Go panic if msg is zero length
	d := (*C.uchar)(C.NULL)
	if len(data) != 0 {
		d = (*C.uchar)(&data[0])
	}
	// https://download.libsodium.org/doc/public-key_cryptography/public-key_signatures#detached-mode
	C.crypto_sign_ed25519_detached((*C.uchar)(&sig[0]), (*C.ulonglong)(C.NULL), d, C.ulonglong(len(data)), (*C.uchar)(&secret[0]))
	return
}

func ed25519Verify(public ed25519PublicKey, data []byte, sig ed25519Signature) bool {
	// &data[0] will make Go panic if msg is zero length
	d := (*C.uchar)(C.NULL)
	if len(data) != 0 {
		d = (*C.uchar)(&data[0])
	}
	// https://download.libsodium.org/doc/public-key_cryptography/public-key_signatures#detached-mode
	result := C.crypto_sign_ed25519_bv_compatible_verify_detached((*C.uchar)(&sig[0]), d, C.ulonglong(len(data)), (*C.uchar)(&public[0]))
	return result == 0
}

// A Signature is a cryptographic signature. It proves that a message was
// produced by a holder of a cryptographic secret.
type Signature ed25519Signature

// BlankSignature is an empty signature structure, containing nothing but zeroes
var BlankSignature = Signature{}

// Blank tests to see if the given signature contains only zeros
func (s *Signature) Blank() bool {
	return (*s) == BlankSignature
}

// A SignatureVerifier is used to identify the holder of SignatureSecrets
// and verify the authenticity of Signatures.
type SignatureVerifier = PublicKey

// SignatureSecrets are used by an entity to produce unforgeable signatures over
// a message.
type SignatureSecrets struct {
	_struct struct{} `codec:""`

	SignatureVerifier
	SK ed25519PrivateKey
}

// SecretKeyToSignatureSecrets converts a private key into a SignatureSecrets and
// returns a pointer
func SecretKeyToSignatureSecrets(sk PrivateKey) (secrets *SignatureSecrets, err error) {
	pk, err := SecretKeyToPublicKey(sk)
	if err != nil {
		return
	}
	secrets = &SignatureSecrets{
		SignatureVerifier: SignatureVerifier(pk),
		SK:                ed25519PrivateKey(sk),
	}
	return
}

// SecretKeyToPublicKey derives a public key from a secret key. This is very
// efficient since ed25519 private keys literally contain their public key
func SecretKeyToPublicKey(secret PrivateKey) (PublicKey, error) {
	var pk PublicKey
	result := C.crypto_sign_ed25519_sk_to_pk((*C.uchar)(&pk[0]), (*C.uchar)(&secret[0]))
	if result != 0 {
		return pk, fmt.Errorf("failed to extract public key: %d", result)
	}
	return pk, nil
}

// SecretKeyToSeed derives the seed from a secret key. This is very efficient
// since ed25519 private keys literally contain their seed
func SecretKeyToSeed(secret PrivateKey) (Seed, error) {
	var seed Seed
	result := C.crypto_sign_ed25519_sk_to_seed((*C.uchar)(&seed[0]), (*C.uchar)(&secret[0]))
	if result != 0 {
		return seed, fmt.Errorf("failed to extract seed: %d", result)
	}
	return seed, nil
}

// GenerateSignatureSecrets creates SignatureSecrets from a source of entropy.
func GenerateSignatureSecrets(seed Seed) *SignatureSecrets {
	pk0, sk := ed25519GenerateKeySeed(ed25519Seed(seed))
	pk := SignatureVerifier(pk0)
	cryptoGenSigSecretsTotal.Inc(nil)
	return &SignatureSecrets{SignatureVerifier: pk, SK: sk}
}

// Sign produces a cryptographic Signature of a Hashable message, given
// cryptographic secrets.
func (s *SignatureSecrets) Sign(message Hashable) Signature {
	cryptoSigSecretsSignTotal.Inc(nil)
	return s.SignBytes(HashRep(message))
}

// SignBytes signs a message directly, without first hashing.
// Caller is responsible for domain separation.
func (s *SignatureSecrets) SignBytes(message []byte) Signature {
	cryptoSigSecretsSignBytesTotal.Inc(nil)
	return Signature(ed25519Sign(ed25519PrivateKey(s.SK), message))
}

// Verify verifies that some holder of a cryptographic secret authentically
// signed a Hashable message.
//
// It returns true if this is the case; otherwise, it returns false.
func (v SignatureVerifier) Verify(message Hashable, sig Signature) bool {
	cryptoSigSecretsVerifyTotal.Inc(nil)
	return ed25519Verify(ed25519PublicKey(v), HashRep(message), ed25519Signature(sig))
}

// VerifyBytes verifies a signature, where the message is not hashed first.
// Caller is responsible for domain separation.
// If the message is a Hashable, Verify() can be used instead.
func (v SignatureVerifier) VerifyBytes(message []byte, sig Signature) bool {
	cryptoSigSecretsVerifyBytesTotal.Inc(nil)
	return ed25519Verify(ed25519PublicKey(v), message, ed25519Signature(sig))
}