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 rsa
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/verify/encrypt/decrypt 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
29type boringPub struct {
30	key  *boring.PublicKeyRSA
31	orig PublicKey
32}
33
34var pubCache bcache.Cache[PublicKey, boringPub]
35var privCache bcache.Cache[PrivateKey, boringPriv]
36
37func init() {
38	pubCache.Register()
39	privCache.Register()
40}
41
42func boringPublicKey(pub *PublicKey) (*boring.PublicKeyRSA, 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.NewPublicKeyRSA(bbig.Enc(b.orig.N), bbig.Enc(big.NewInt(int64(b.orig.E))))
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.PrivateKeyRSA
61	orig PrivateKey
62}
63
64func boringPrivateKey(priv *PrivateKey) (*boring.PrivateKeyRSA, 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
73	var N, E, D, P, Q, Dp, Dq, Qinv *big.Int
74	N = b.orig.N
75	E = big.NewInt(int64(b.orig.E))
76	D = b.orig.D
77	if len(b.orig.Primes) == 2 {
78		P = b.orig.Primes[0]
79		Q = b.orig.Primes[1]
80		Dp = b.orig.Precomputed.Dp
81		Dq = b.orig.Precomputed.Dq
82		Qinv = b.orig.Precomputed.Qinv
83	}
84	key, err := boring.NewPrivateKeyRSA(bbig.Enc(N), bbig.Enc(E), bbig.Enc(D), bbig.Enc(P), bbig.Enc(Q), bbig.Enc(Dp), bbig.Enc(Dq), bbig.Enc(Qinv))
85	if err != nil {
86		return nil, err
87	}
88	b.key = key
89	privCache.Put(priv, b)
90	return key, nil
91}
92
93func publicKeyEqual(k1, k2 *PublicKey) bool {
94	return k1.N != nil &&
95		k1.N.Cmp(k2.N) == 0 &&
96		k1.E == k2.E
97}
98
99func copyPublicKey(k *PublicKey) PublicKey {
100	return PublicKey{
101		N: new(big.Int).Set(k.N),
102		E: k.E,
103	}
104}
105
106func privateKeyEqual(k1, k2 *PrivateKey) bool {
107	return publicKeyEqual(&k1.PublicKey, &k2.PublicKey) &&
108		k1.D.Cmp(k2.D) == 0
109}
110
111func copyPrivateKey(k *PrivateKey) PrivateKey {
112	dst := PrivateKey{
113		PublicKey: copyPublicKey(&k.PublicKey),
114		D:         new(big.Int).Set(k.D),
115	}
116	dst.Primes = make([]*big.Int, len(k.Primes))
117	for i, p := range k.Primes {
118		dst.Primes[i] = new(big.Int).Set(p)
119	}
120	if x := k.Precomputed.Dp; x != nil {
121		dst.Precomputed.Dp = new(big.Int).Set(x)
122	}
123	if x := k.Precomputed.Dq; x != nil {
124		dst.Precomputed.Dq = new(big.Int).Set(x)
125	}
126	if x := k.Precomputed.Qinv; x != nil {
127		dst.Precomputed.Qinv = new(big.Int).Set(x)
128	}
129	return dst
130}
131