8262316: Reducing locks in RSA Blinding

Reviewed-by: jnimeh
This commit is contained in:
Anthony Scarpino 2021-04-07 17:29:01 +00:00
parent d3fdd7399d
commit 7a99a9874b

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2003, 2020, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2003, 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -25,20 +25,25 @@
package sun.security.rsa; package sun.security.rsa;
import java.math.BigInteger; import sun.security.jca.JCAUtil;
import java.util.*;
import java.security.SecureRandom;
import java.security.interfaces.*;
import javax.crypto.BadPaddingException; import javax.crypto.BadPaddingException;
import java.math.BigInteger;
import sun.security.jca.JCAUtil; import java.security.SecureRandom;
import java.security.interfaces.RSAKey;
import java.security.interfaces.RSAPrivateCrtKey;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.util.Arrays;
import java.util.Map;
import java.util.WeakHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.locks.ReentrantLock;
/** /**
* Core of the RSA implementation. Has code to perform public and private key * Core of the RSA implementation. Has code to perform public and private key
* RSA operations (with and without CRT for private key ops). Private CRT ops * RSA operations (with and without CRT for private key ops). Private CRT ops
* also support blinding to twart timing attacks. * also support blinding to thwart timing attacks.
* *
* The code in this class only does the core RSA operation. Padding and * The code in this class only does the core RSA operation. Padding and
* unpadding must be done externally. * unpadding must be done externally.
@ -53,11 +58,14 @@ public final class RSACore {
// globally enable/disable use of blinding // globally enable/disable use of blinding
private static final boolean ENABLE_BLINDING = true; private static final boolean ENABLE_BLINDING = true;
// cache for blinding parameters. Map<BigInteger, BlindingParameters> // cache for blinding parameters. Map<BigInteger,
// use a weak hashmap so that cached values are automatically cleared // ConcurrentLinkedQueue<BlindingParameters>> use a weak hashmap so that,
// when the modulus is GC'ed // cached values are automatically cleared when the modulus is GC'ed.
private static final Map<BigInteger, BlindingParameters> // Multiple BlindingParameters can be queued during times of heavy load,
// like performance testing.
private static final Map<BigInteger, ConcurrentLinkedQueue<BlindingParameters>>
blindingCache = new WeakHashMap<>(); blindingCache = new WeakHashMap<>();
private static final ReentrantLock lock = new ReentrantLock();
private RSACore() { private RSACore() {
// empty // empty
@ -402,56 +410,68 @@ public final class RSACore {
if ((this.e != null && this.e.equals(e)) || if ((this.e != null && this.e.equals(e)) ||
(this.d != null && this.d.equals(d))) { (this.d != null && this.d.equals(d))) {
BlindingRandomPair brp = null; BlindingRandomPair brp = new BlindingRandomPair(u, v);
synchronized (this) { if (u.compareTo(BigInteger.ONE) <= 0 ||
if (!u.equals(BigInteger.ZERO) && v.compareTo(BigInteger.ONE) <= 0) {
!v.equals(BigInteger.ZERO)) { // Reset so the parameters will be not queued later
u = BigInteger.ZERO;
brp = new BlindingRandomPair(u, v); v = BigInteger.ZERO;
if (u.compareTo(BigInteger.ONE) <= 0 || } else {
v.compareTo(BigInteger.ONE) <= 0) { u = u.modPow(BIG_TWO, n);
v = v.modPow(BIG_TWO, n);
// need to reset the random pair next time
u = BigInteger.ZERO;
v = BigInteger.ZERO;
} else {
u = u.modPow(BIG_TWO, n);
v = v.modPow(BIG_TWO, n);
}
} // Otherwise, need to reset the random pair.
} }
return brp; return brp;
} }
return null; return null;
} }
// Check if reusable, return true if both u & v are not zero.
boolean isReusable() {
return !u.equals(BigInteger.ZERO) && !v.equals(BigInteger.ZERO);
}
} }
private static BlindingRandomPair getBlindingRandomPair( private static BlindingRandomPair getBlindingRandomPair(
BigInteger e, BigInteger d, BigInteger n) { BigInteger e, BigInteger d, BigInteger n) {
BlindingParameters bps = null; ConcurrentLinkedQueue<BlindingParameters> queue;
synchronized (blindingCache) {
bps = blindingCache.get(n); // Get queue from map, if there is none then create one
lock.lock();
try {
queue = blindingCache.computeIfAbsent(n,
ignored -> new ConcurrentLinkedQueue<>());
} finally {
lock.unlock();
} }
BlindingParameters bps = queue.poll();
if (bps == null) { if (bps == null) {
bps = new BlindingParameters(e, d, n); bps = new BlindingParameters(e, d, n);
synchronized (blindingCache) {
blindingCache.putIfAbsent(n, bps);
}
} }
BlindingRandomPair brp = bps.getBlindingRandomPair(e, d, n); BlindingRandomPair brp = null;
if (brp == null) {
// need to reset the blinding parameters // Loops to get a valid pair, going through the queue or create a new
bps = new BlindingParameters(e, d, n); // parameters if needed.
synchronized (blindingCache) { while (brp == null) {
blindingCache.replace(n, bps);
}
brp = bps.getBlindingRandomPair(e, d, n); brp = bps.getBlindingRandomPair(e, d, n);
if (brp == null) {
// need to reset the blinding parameters, first check for
// another in the queue.
bps = queue.poll();
if (bps == null) {
bps = new BlindingParameters(e, d, n);
}
}
} }
// If this parameters are still usable, put them back into the queue.
if (bps.isReusable()) {
queue.add(bps);
}
return brp; return brp;
} }