1// Copyright 2017 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5//go:build boringcrypto
6
7package ecdsa
8
9import (
10	"crypto/internal/boring"
11	"crypto/internal/boring/bbig"
12	"crypto/internal/boring/bcache"
13	"math/big"
14)
15
16// Cached conversions from Go PublicKey/PrivateKey to BoringCrypto.
17//
18// The first operation on a PublicKey or PrivateKey makes a parallel
19// BoringCrypto key and saves it in pubCache or privCache.
20//
21// We could just assume that once used in a Sign or Verify operation,
22// a particular key is never again modified, but that has not been a
23// stated assumption before. Just in case there is any existing code that
24// does modify the key between operations, we save the original values
25// alongside the cached BoringCrypto key and check that the real key
26// still matches before using the cached key. The theory is that the real
27// operations are significantly more expensive than the comparison.
28
29var pubCache bcache.Cache[PublicKey, boringPub]
30var privCache bcache.Cache[PrivateKey, boringPriv]
31
32func init() {
33	pubCache.Register()
34	privCache.Register()
35}
36
37type boringPub struct {
38	key  *boring.PublicKeyECDSA
39	orig PublicKey
40}
41
42func boringPublicKey(pub *PublicKey) (*boring.PublicKeyECDSA, error) {
43	b := pubCache.Get(pub)
44	if b != nil && publicKeyEqual(&b.orig, pub) {
45		return b.key, nil
46	}
47
48	b = new(boringPub)
49	b.orig = copyPublicKey(pub)
50	key, err := boring.NewPublicKeyECDSA(b.orig.Curve.Params().Name, bbig.Enc(b.orig.X), bbig.Enc(b.orig.Y))
51	if err != nil {
52		return nil, err
53	}
54	b.key = key
55	pubCache.Put(pub, b)
56	return key, nil
57}
58
59type boringPriv struct {
60	key  *boring.PrivateKeyECDSA
61	orig PrivateKey
62}
63
64func boringPrivateKey(priv *PrivateKey) (*boring.PrivateKeyECDSA, error) {
65	b := privCache.Get(priv)
66	if b != nil && privateKeyEqual(&b.orig, priv) {
67		return b.key, nil
68	}
69
70	b = new(boringPriv)
71	b.orig = copyPrivateKey(priv)
72	key, err := boring.NewPrivateKeyECDSA(b.orig.Curve.Params().Name, bbig.Enc(b.orig.X), bbig.Enc(b.orig.Y), bbig.Enc(b.orig.D))
73	if err != nil {
74		return nil, err
75	}
76	b.key = key
77	privCache.Put(priv, b)
78	return key, nil
79}
80
81func publicKeyEqual(k1, k2 *PublicKey) bool {
82	return k1.X != nil &&
83		k1.Curve.Params() == k2.Curve.Params() &&
84		k1.X.Cmp(k2.X) == 0 &&
85		k1.Y.Cmp(k2.Y) == 0
86}
87
88func privateKeyEqual(k1, k2 *PrivateKey) bool {
89	return publicKeyEqual(&k1.PublicKey, &k2.PublicKey) &&
90		k1.D.Cmp(k2.D) == 0
91}
92
93func copyPublicKey(k *PublicKey) PublicKey {
94	return PublicKey{
95		Curve: k.Curve,
96		X:     new(big.Int).Set(k.X),
97		Y:     new(big.Int).Set(k.Y),
98	}
99}
100
101func copyPrivateKey(k *PrivateKey) PrivateKey {
102	return PrivateKey{
103		PublicKey: copyPublicKey(&k.PublicKey),
104		D:         new(big.Int).Set(k.D),
105	}
106}
107