1// Copyright (c) 2019 Storj Labs, Inc.
2// See LICENSE for copying information.
3
4package identity
5
6import (
7	"context"
8	"crypto"
9
10	"storj.io/common/pkcrypto"
11	"storj.io/common/storj"
12)
13
14// GenerateKey generates a private key with a node id with difficulty at least
15// minDifficulty. No parallelism is used.
16func GenerateKey(ctx context.Context, minDifficulty uint16, version storj.IDVersion) (
17	k crypto.PrivateKey, id storj.NodeID, err error) {
18	defer mon.Task()(&ctx)(&err)
19
20	var d uint16
21	for {
22		err = ctx.Err()
23		if err != nil {
24			break
25		}
26		k, err = version.NewPrivateKey()
27		if err != nil {
28			break
29		}
30
31		var pubKey crypto.PublicKey
32		pubKey, err = pkcrypto.PublicKeyFromPrivate(k)
33		if err != nil {
34			break
35		}
36
37		id, err = NodeIDFromKey(pubKey, version)
38		if err != nil {
39			break
40		}
41		d, err = id.Difficulty()
42		if err != nil {
43			break
44		}
45		if d >= minDifficulty {
46			return k, id, nil
47		}
48	}
49	return k, id, storj.ErrNodeID.Wrap(err)
50}
51
52// GenerateCallback indicates that key generation is done when done is true.
53// if err != nil key generation will stop with that error.
54type GenerateCallback func(crypto.PrivateKey, storj.NodeID) (done bool, err error)
55
56// GenerateKeys continues to generate keys until found returns done == false,
57// or the ctx is canceled.
58func GenerateKeys(ctx context.Context, minDifficulty uint16, concurrency int, version storj.IDVersion, found GenerateCallback) (err error) {
59	defer mon.Task()(&ctx)(&err)
60	ctx, cancel := context.WithCancel(ctx)
61	defer cancel()
62	errchan := make(chan error, concurrency)
63
64	for i := 0; i < concurrency; i++ {
65		go func() {
66			for {
67				k, id, err := GenerateKey(ctx, minDifficulty, version)
68				if err != nil {
69					errchan <- err
70					return
71				}
72
73				done, err := found(k, id)
74				if err != nil {
75					errchan <- err
76					return
77				}
78				if done {
79					errchan <- nil
80					return
81				}
82			}
83		}()
84	}
85
86	// we only care about the first error. the rest of the errors will be
87	// context cancellation errors
88	return <-errchan
89}
90