Go implementation

peoplecoin

Standard library only. Primitives, the state machine, and the nine operations — no networking, no video, no wallet yet.

sig/sig.go

121 lines

1// Package sig verifies Ed25519 signatures under the protocol's strictness
2// rules: s < L, and R and public keys canonically encoded (RFC 8032).
3//
4// The standard library rejects s ≥ L but, like most implementations,
5// accepts non-canonical point encodings (a y-coordinate ≥ p, or x = 0 with
6// the sign bit set). Those are rejected here before verification, so that
7// every node accepts exactly the same signatures.
8package sig
9
10import (
11 "crypto/ed25519"
12 "crypto/rand"
13 "math/big"
14)
15
16// PublicKey is a 32-byte Ed25519 public key; it is also the trie key of
17// address-keyed tries.
18type PublicKey [32]byte
19
20// Signature is a 64-byte Ed25519 signature, R ‖ s.
21type Signature [64]byte
22
23var (
24 // p = 2^255 − 19, the field prime.
25 fieldP = func() *big.Int {
26 p := new(big.Int).Lsh(big.NewInt(1), 255)
27 return p.Sub(p, big.NewInt(19))
28 }()
29 // pMinus1 = p − 1 (the y-coordinate of the other point with x = 0).
30 pMinus1 = new(big.Int).Sub(fieldP, big.NewInt(1))
31 // L = 2^252 + 27742317777372353535851937790883648493, the group order.
32 groupL = func() *big.Int {
33 l, _ := new(big.Int).SetString("7237005577332262213973186563042994240857116359379907606001950938285454250989", 10)
34 return l
35 }()
36)
37
38// leToInt interprets 32 little-endian bytes as an integer.
39func leToInt(b [32]byte) *big.Int {
40 var be [32]byte
41 for i := range b {
42 be[31-i] = b[i]
43 }
44 return new(big.Int).SetBytes(be[:])
45}
46
47// PointCanonical reports whether enc is a canonical point encoding: the
48// y-coordinate is reduced (y < p), and the sign bit is clear when x = 0
49// (y = 1 or y = p − 1). It does not check that the point is on the curve;
50// verification does.
51func PointCanonical(enc [32]byte) bool {
52 sign := enc[31]&0x80 != 0
53 enc[31] &= 0x7f
54 y := leToInt(enc)
55 if y.Cmp(fieldP) >= 0 {
56 return false
57 }
58 if sign && (y.Cmp(big.NewInt(1)) == 0 || y.Cmp(pMinus1) == 0) {
59 return false
60 }
61 return true
62}
63
64// ScalarCanonical reports whether s < L.
65func ScalarCanonical(s [32]byte) bool {
66 return leToInt(s).Cmp(groupL) < 0
67}
68
69// Valid reports whether sig is well-formed under the strictness rules,
70// without checking it against a message: R canonical, s < L.
71func (sig *Signature) Valid() bool {
72 var r, s [32]byte
73 copy(r[:], sig[:32])
74 copy(s[:], sig[32:])
75 return PointCanonical(r) && ScalarCanonical(s)
76}
77
78// Verify reports whether sig is a valid, canonical signature by pub over
79// msg. A non-canonical signature or public key never verifies.
80func Verify(pub PublicKey, msg []byte, sig Signature) bool {
81 if !PointCanonical(pub) || !sig.Valid() {
82 return false
83 }
84 return ed25519.Verify(ed25519.PublicKey(pub[:]), msg, sig[:])
85}
86
87// Keypair holds a signing key, for wallets and tests.
88type Keypair struct {
89 Public PublicKey
90 private ed25519.PrivateKey
91}
92
93// Generate creates a fresh random keypair.
94func Generate() (*Keypair, error) {
95 pub, priv, err := ed25519.GenerateKey(rand.Reader)
96 if err != nil {
97 return nil, err
98 }
99 var kp Keypair
100 copy(kp.Public[:], pub)
101 kp.private = priv
102 return &kp, nil
103}
104
105// FromSeed derives a keypair from a 32-byte seed (RFC 8032 private key).
106func FromSeed(seed [32]byte) *Keypair {
107 priv := ed25519.NewKeyFromSeed(seed[:])
108 var kp Keypair
109 copy(kp.Public[:], priv.Public().(ed25519.PublicKey))
110 kp.private = priv
111 return &kp
112}
113
114// Sign signs msg. Signatures produced by the standard library are always
115// canonical.
116func (k *Keypair) Sign(msg []byte) Signature {
117 var s Signature
118 copy(s[:], ed25519.Sign(k.private, msg))
119 return s
120}