RFD: Security Providers Filter (JEP)
Martin Balao
mbalao at redhat.com
Fri Oct 18 18:55:05 UTC 2024
Hello Sean,
On 9/19/24 12:00, Sean Mullan wrote:
> Hi Martin,
>
> Our team at Oracle has done another review of the proposed feature and
> we have some questions that we would like to discuss below.
>
> This is a significant effort that would add some valuable features to
> the Security Provider mechanism. However, this is also modifying an
> important core function used by all JCA/JCE APIs to locate
> implementations, so we want to make sure we spend the time to review all
> aspects of the proposal thoroughly.
>
Thanks for reviewing our proposal. We totally understand and are happy
to have such a thorough review. It is in the best interest of the whole
community and us to get this right.
> - The current implementation introduces behavior changes in the Provider
> API which we believe would need to be specified in the specification.
> For example, if an application registers a service that is restricted by
> the filter using the Provider API, then that service won't be returned
> by methods such as Provider.getServices(). This is a behavior change in
> the API. I don't think it could be specified as an implementation note,
> since it changes the specified behavior of these methods.
>
> Adding specification changes will make any backport much more difficult,
> if that was one of your goals.
We agree that our proposal affects the behavior of
Provider::getService(s) APIs and that a specification change is
required. We are aware of this extra complexity but, in the interest of
having the best solution possible for the long term, are willing to have
the discussion. Our expectation is to have the Filter included in JDK 25
and have no plans for backports to earlier releases. With that said, if
it takes longer to integrate the Filter into JDK main line, we will
still be interested in a backport to JDK 25.
>
> - Did you consider implementing the filtering mechanism at a higher
> layer, in the getInstance methods? This could possibly avoid the
> specification concerns above, as the behavior could potentially be added
> as implementation notes in the getInstance methods, similar to the
> implementation note for the jdk.security.provider.preferred Security
> property.
Yes, we considered that option. We just noticed that it is not in the
Alternatives segment of the JEP. We will add it in the coming days.
Being ::getInstance a public API and the Filter altering its behavior in
terms of what is returned, we'd rather handle this alternative as a
specification change as well. In any case, the main reason why we opted
for filtering at the Service level is to handle any service type in a
uniform and consistent way, including service types defined by 3rd-party
applications/libraries/providers. Having taken the ::getInstance path,
we would have had to either not support 3rd-party service types or
create a new public API in the Filter to query whether a service is
allowed or denied. This approach would have opened the door for flaws or
inconsistencies in how 3rd-party implementations handle Filter
decisions. Besides this, we thought that obtaining a service instance
with Provider::getService(s) that cannot be obtained and used by
::getInstance could introduce some confusion. This would have had an
impact not only programmatically but in -XshowSettings:security:providers.
>
> - The syntax doesn't currently support key size constraints, or
> algorithm constraints that are not specified in algorithm names, such as
> the parameters used with the RSASSA-PSS algorithm. How would you add
> support for these in the future?
>
We reserved the colon (:) and comma (,) characters in the syntax for
this purpose. As these characters must be escaped, we can extend the
syntax in the future to allow further constraints (e.g. key size,
algorithm constraints) in a backward-compatible way.
We added the following reference in the JEP:
"In addition, identifying services with a granularity finer than an
algorithm name or alias (e.g. based on key size or other algorithm
parameters) is not under the scope at this time. With that said,
extensions to this proposal may be explored in the future and characters
will be reserved in the filter syntax for this purpose."
We also mentioned the full list of reserved characters:
"The following characters, when part of one of the listed levels, must
be escaped by prepending a '\' character: '!', '*', ' ' (white space),
'.', ';', '\', ':' and ','."
We did not connect the two references by matching the concept of finer
granularity with the reserved characters for that purpose. We can add a
loose connection. However, we prefer not to prescribe the exact syntax
now as it is a non-goal and would require further analysis and to
explore implementations.
One thing to consider here is that the use of one of the reserved
characters (e.g. ':') can open the door for an extension of the syntax
with new reserved tokens and characters. For example: SunJCE.Cipher.AES
: keySize > 128. In this case, '>' does not need to be reserved for the
provider name, service type or algorithm. For the algorithm constraint,
'keySize' and '>' are assigned a specific meaning.
> - Does the filtering work for providers that are not registered, and
> just passed into the getInstance methods?
>
Yes, it does. For most providers, filtering occurs when they invoke
Provider::putService/put APIs. In very rare cases, a Provider may not
invoke Provider::putService/put APIs and still return a Service
overriding Provider::getService(s). In that situation, we filter when
Service::newInstance is invoked. In theory, it is possible that a
Service subclass overrides Service::newInstance. In such extreme
circumstances, we filter at the ::getInstance level. The last case will
not apply to 3rd-party service types, but should be extremely unlikely.
> - Did you consider implementing this as a Provider.configure option?
> There are pros/cons to doing that, but given that the configure method
> is designed to configure providers, we think it should be
> considered and described in the Alternatives section.
>
We did not consider Provider::configure. We will add it to the
Alternatives section if you deem it important. I personally see more
cons than pros but if someone has a different opinion and wants to
advocate for it, we're happy to have a discussion.
> - Would early initialization of the filter inadvertently triggered by a
> cryptographic operation (for example, when verifying a signed JAR on the
> classpath) cause subsequent settings of the filter by an application at
> runtime to be ignored? If so, this is something that could easily go
> undetected, as the filter could be silently ignored and thus
> restrictions not enforced as expected - it may be worth considering
> throwing an exception if you can detect that the property has already
> been set and processed. The logging and debugging mechanisms are helpful
> but I view them as being more useful when testing the filter or later
> debugging of issues.
Our understanding is that in such an event the Filter would be
initialized when the ClassLoader does JAR verification. If this is the
case, programmatically defining a Filter in the signed JAR should not
work. @Francisco will create a proof-of-concept to confirm this
hypothesis in the coming days.
We agree that throwing an exception in such a case would be ideal.
However, this type of change —as we see it— involves a specification
change in System::setProperty/Security::setProperty as that's the only
interface to set a Filter value programmatically. We are open to having
this discussion.
Another alternative would be to reserve the property in
System::setProperty/Security::setProperty and throw an exception
irrespective of the Filter status. We think that setting the Filter
programmatically should not be recommended for general use. However,
this would prevent advanced users that know how things are initialized
from benefiting.
>
> - Does the additional searching for Cipher transformations cause
> significant performance issues?
>
We don't think so. When providers register the full "alg/mode/padding"
as algorithm name, transformation queries against the filter are done at
registration time (Provider::putService/put invocation) and cached in
the Service instance. It's true that queries may be done at run time to
explore different transformation combinations (e.g. ::getInstance of
"alg/mode/padding" may explore services that registered "alg/mode",
"alg//padding" and "alg" as algorithm name). However, these
transformation queries are cached per Service instance after the Filter
decision is made. In the worst case —extremely unlikely—, a provider may
register 4 services relevant for the transformation (e.g.
alg/mode/padding, alg/mode, alg//padding and alg) and the Filter blocks
the transformation —if the Filter allows the transformation, the first
service will be used and the others won't be queried because it is a
lazy mechanism—. This case would require 3 queries against the filter
—the "alg/mode/padding" decision is read from the Service's cache— when
they are not in the transformations cache. The reason why the
transformation cache is per service and not per provider is because we
include service aliases as part of the query.
Regards,
Martin and Francisco.-
More information about the security-dev
mailing list