1package keycard
2
3import (
4	"crypto/rand"
5	"crypto/sha256"
6	"encoding/base64"
7	"fmt"
8	"math/big"
9
10	"github.com/status-im/keycard-go/crypto"
11	"golang.org/x/crypto/pbkdf2"
12	"golang.org/x/text/unicode/norm"
13)
14
15const (
16	maxPukNumber = int64(999999999999)
17	maxPinNumber = int64(999999)
18)
19
20// Secrets contains the secret data needed to pair a client with a card.
21type Secrets struct {
22	pin          string
23	puk          string
24	pairingPass  string
25	pairingToken []byte
26}
27
28func NewSecrets(pin, puk, pairingPass string) *Secrets {
29	return &Secrets{
30		pin:         pin,
31		puk:         puk,
32		pairingPass: pairingPass,
33	}
34}
35
36// GenerateSecrets generate a new Secrets with  random puk and pairing password.
37func GenerateSecrets() (*Secrets, error) {
38	pairingPass, err := generatePairingPass()
39	if err != nil {
40		return nil, err
41	}
42
43	puk, err := rand.Int(rand.Reader, big.NewInt(maxPukNumber))
44	if err != nil {
45		return nil, err
46	}
47
48	pin, err := rand.Int(rand.Reader, big.NewInt(maxPinNumber))
49	if err != nil {
50		return nil, err
51	}
52
53	return &Secrets{
54		pin:          fmt.Sprintf("%06d", pin.Int64()),
55		puk:          fmt.Sprintf("%012d", puk.Int64()),
56		pairingPass:  pairingPass,
57		pairingToken: generatePairingToken(pairingPass),
58	}, nil
59}
60
61// Pin returns the pin string.
62func (s *Secrets) Pin() string {
63	return s.pin
64}
65
66// Puk returns the puk string.
67func (s *Secrets) Puk() string {
68	return s.puk
69}
70
71// PairingPass returns the pairing password string.
72func (s *Secrets) PairingPass() string {
73	return s.pairingPass
74}
75
76// PairingToken returns the pairing token generated from the random pairing password.
77func (s *Secrets) PairingToken() []byte {
78	return s.pairingToken
79}
80
81func generatePairingPass() (string, error) {
82	r := make([]byte, 12)
83	_, err := rand.Read(r)
84	if err != nil {
85		return "", err
86	}
87
88	return base64.URLEncoding.EncodeToString(r), nil
89}
90
91func generatePairingToken(pass string) []byte {
92	return pbkdf2.Key(norm.NFKD.Bytes([]byte(pass)), norm.NFKD.Bytes([]byte(crypto.PairingTokenSalt)), 50000, 32, sha256.New)
93}
94