OSDN Git Service

new repo
[bytom/vapor.git] / crypto / ed25519 / chainkd / expanded_key.go
1 // Package chainkd This is an extension to ed25519.Sign that is compatible with NaCl `crypto_sign`
2 // function taking 64-byte expanded private key (where the left part is a pre-multiplied
3 // scalar and the right part is "prefix" used for generating a nonce).
4 //
5 // Invariants:
6 // 1) Expanded(PrivateKey).Sign() == PrivateKey.Sign()
7 // 2) InnerSign(Expanded(PrivateKey)) == Sign(PrivateKey)
8 package chainkd
9
10 import (
11         "crypto"
12         "crypto/sha512"
13         "errors"
14         "io"
15         "strconv"
16
17         "github.com/vapor/crypto/ed25519"
18         "github.com/vapor/crypto/ed25519/internal/edwards25519"
19 )
20
21 const (
22         // ExpandedPrivateKeySize is the size, in bytes, of a "secret key" as defined in NaCl.
23         ExpandedPrivateKeySize = 64
24 )
25
26 // ExpandedPrivateKey is the type of NaCl secret keys. It implements crypto.Signer.
27 type ExpandedPrivateKey []byte
28
29 // Public returns the PublicKey corresponding to secret key.
30 func (priv ExpandedPrivateKey) Public() crypto.PublicKey {
31         var A edwards25519.ExtendedGroupElement
32         var scalar [32]byte
33         copy(scalar[:], priv[:32])
34         edwards25519.GeScalarMultBase(&A, &scalar)
35         var publicKeyBytes [32]byte
36         A.ToBytes(&publicKeyBytes)
37         return ed25519.PublicKey(publicKeyBytes[:])
38 }
39
40 func expandEd25519PrivateKey(priv ed25519.PrivateKey) ExpandedPrivateKey {
41         digest := sha512.Sum512(priv[:32])
42         digest[0] &= 248
43         digest[31] &= 127
44         digest[31] |= 64
45         return ExpandedPrivateKey(digest[:])
46 }
47
48 // Sign signs the given message with expanded private key.
49 // Ed25519 performs two passes over messages to be signed and therefore cannot
50 // handle pre-hashed messages. Thus opts.HashFunc() must return zero to
51 // indicate the message hasn't been hashed. This can be achieved by passing
52 // crypto.Hash(0) as the value for opts.
53 func (priv ExpandedPrivateKey) Sign(rand io.Reader, message []byte, opts crypto.SignerOpts) (signature []byte, err error) {
54         if opts.HashFunc() != crypto.Hash(0) {
55                 return nil, errors.New("ed25519: cannot sign hashed message")
56         }
57
58         return Ed25519InnerSign(priv, message), nil
59 }
60
61 // Ed25519InnerSign signs the message with expanded private key and returns a signature.
62 // It will panic if len(privateKey) is not ExpandedPrivateKeySize.
63 func Ed25519InnerSign(privateKey ExpandedPrivateKey, message []byte) []byte {
64         if l := len(privateKey); l != ExpandedPrivateKeySize {
65                 panic("ed25519: bad private key length: " + strconv.Itoa(l))
66         }
67
68         var messageDigest, hramDigest [64]byte
69
70         h := sha512.New()
71         h.Write(privateKey[32:])
72         h.Write(message)
73         h.Sum(messageDigest[:0])
74
75         var messageDigestReduced [32]byte
76         edwards25519.ScReduce(&messageDigestReduced, &messageDigest)
77         var R edwards25519.ExtendedGroupElement
78         edwards25519.GeScalarMultBase(&R, &messageDigestReduced)
79
80         var encodedR [32]byte
81         R.ToBytes(&encodedR)
82
83         publicKey := privateKey.Public().(ed25519.PublicKey)
84         h.Reset()
85         h.Write(encodedR[:])
86         h.Write(publicKey[:])
87         h.Write(message)
88         h.Sum(hramDigest[:0])
89         var hramDigestReduced [32]byte
90         edwards25519.ScReduce(&hramDigestReduced, &hramDigest)
91
92         var sk [32]byte
93         copy(sk[:], privateKey[:32])
94         var s [32]byte
95         edwards25519.ScMulAdd(&s, &hramDigestReduced, &sk, &messageDigestReduced)
96
97         signature := make([]byte, ed25519.SignatureSize)
98         copy(signature[:], encodedR[:])
99         copy(signature[32:], s[:])
100
101         return signature
102 }