On 7/13/23 12:27 PM, Martin Balao wrote:
On 7/13/23 12:06, Sean Mullan wrote:
One other comment that I thought of - is that from a practical
standpoint, I think it will be hard to unilaterally disable an algorithm
at the JCE layer unless it is so broken that almost no code ever uses
it, say MD2 or RC2. There may be cases where a weak algorithm is
acceptable, for example using MD5 for a checksum. (For a real example,
UUID.nameUUIDFromBytes uses MD5 to generate a UUID).

If you have a single case in your application where a weak algorithm is
ok to use, you won't be able to disable it across the board.

Yes, I agree with this observation. In fact, our original motivation was
not to disable an algorithm across the board but a specific
implementation of it —i.e. blocking the implementation from provider X,
because we want the one from provider Y or prefer the algorithm not to
be available. What we also have in mind is using this enhancement in
combination with security profiles that can enforce policies of allowed
algorithms, at the risk of requiring changes in an application to be
compliant.


At the risk of complicating your syntax and implementation, it may be
worth exploring adding the name of a class to the syntax for cases like
this. But my comment is more about thinking about this a bit more first.

Or perhaps adding some extensibility into the format would be a good
idea in case we want to add something like this down the line.


I have a couple of questions regarding this idea:

1) Isn't the class name an implementation detail? My concern is not much
on how to extend the syntax but on binding the filter value to internal
names.

It is an application-specific detail. I would expect this to be applied by applications that are most familiar with their usage, and not as part of a global configuration.

2) Why wouldn't a combination of Security Provider + Service Type +
Algorithm be enough to identify a specific implementation?

Because that doesn't tell you who is calling the specific provider/service and whether that use case is acceptable or not. Also most code that calls JCE APIs doesn't specify a specific provider.

Again, I think this needs more thought, and I am not suggesting this is the best course of action, but one thought is a syntax something like this:

jdk.security.providers.filter=java.util.UUID.nameUUIDFromBytes:MessageDigest.MD5;!*.MessageDigest.MD5;*

But, that would probably mean extending the implementation to do a stack walk to check if the specified class was one of the callers, which I am very wary about doing something like this. But the overall issue still remains for me. Maybe we should not be providing a way to unilaterally disable algorithms unless it can be used more effectively in practice. Otherwise I don't like the idea of telling a user they have to re-enable the algorithm even if they only have a single case where it is acceptable.

--Sean

Thanks,
Martin.-


--Sean


On 7/13/23 11:44 AM, Martin Balao wrote:
Hi Sean,

Thanks for your feedback.

Just to give some visibility, we have implemented most of the
functionality and are now working on final adjustments, more tests
coverage, documentation and internal reviews. The implementation is
pretty much aligned to what we previously discussed, with the exception
of algorithm's alias that turned up to have more complexity than
anticipated —particularly in the legacy mode of registering Services—.

We will send a PR for public discussion in the coming weeks.

Martin.-


On 6/14/23 12:40, Sean Mullan wrote:
This proposal looks pretty good, although I think I would like to see
more examples and a prototype if you have it.

I think this would work well in conjunction with Sean Coffey's
enhancement to add a security category to the java -XshowSettings option
[1]. This would help debug issues with the syntax. The provider
suboption could be enhanced (perhaps by default, perhaps with an
additional suboption) to show the services that are disabled, ex, with
the property set to

jdk.security.providers.filter=!*.MessageDigest.MD5;
!*.MessageDigest.MD2; *:

it would show something like:

           Provider name: SUN
           Provider information: ...
           Provider services: (type : algorithm)
               ...
               MessageDigest : MD2 (disabled)
               MessageDigest : MD5 (disabled)
               ...

I would even add that as a debugging tip in the documenting of the
syntax.

--Sean

[1] https://github.com/openjdk/jdk/pull/14394

On 5/24/23 5:03 PM, Martin Balao wrote:
Hi,

Thanks Anthony for your feedback.

We've been exploring the syntax and semantics for this new property
further, with the goal of making it more consistent and simple while
retaining expressiveness power. We understand the importance of clarity
to minimize the risk of security providers, service types or algorithms
being unexpectedly enabled.

In this new iteration of the proposal, we explore a filter that has
similarities to the serialization filter (jdk.serialFilter). We think
that it could be beneficial to leverage on a specification to which the
user is familiar already.


General structure
====================

jdk.security.providers.filter=pattern-1; pattern-2; ...; pattern-n

The property jdk.security.providers.filter is an overrideable Security
property. Thus, a System property with the same name exists and, when
specified, overrides any value in its Security counterpart. When not
specified (value is null), filtering capabilities are completely
disabled: all installed security providers, service types and
algorithms
are allowed. If any of these properties are set during run time, the
filter could be initialized already and the new value may not take
effect.

When filtering capabilities are enabled, each service is checked
against
the filter before registration. Notice that this affects both the
initial list of security providers as well as those dynamically
installed during run time. Once a service is registered, instances
of it
can be obtained and used without any other checks that could affect
performance.

The registration of a service involves a combination of a security
provider, service type and algorithm. Each combination is evaluated
against the filter patterns, from left to right. When a pattern matches
—or, in other words, the rule concerns the service to be registered—, a
decision is made: the service will be allowed or denied. When a
decision
is made, remaining patterns are not checked for the service under
consideration. When all patterns are checked and a decision is not
made,
the default behavior is to deny the service registration.

Contrary to the serialization filter, white spaces between patterns do
not have any significance.


Pattern matching
=====================================================

pattern := ! security-provider.service-type.algorithm

pattern := security-provider.service-type.algorithm

A canonical pattern consists of 3 hierarchical levels separated by ".".
    From left to right in lexicographic order, these levels denote a
security provider, a service type and an algorithm. If a pattern starts
with "!", the decision made upon matching is to deny the service
registration. Otherwise, the service registration is allowed. White
spaces between "!" and the rest of the pattern do not have any
significance.

For a match to be successful, the security provider name, the service
type and the algorithm have to match the pattern exactly (case
insensitive). If the service type of a security provider interprets the
algorithm as a transformation composed of different parts, the full
transformation has to be specified in the pattern: the filter takes a
conservative approach and does not make any assumptions of what an
algorithm name means. For example, "AES" as the algorithm of a
canonical
filter pattern will not match an "AES/ECB/PKCS5Padding" transformation.

If an algorithm alias is specified in the filter pattern, a service
registering the alias will be matched.

For convenience, it's possible to specify patterns in non-canonical
forms:

1) At any level, the security provider, the service type or the
algorithm name can contain wildcards ("*") to represent zero or more
repetitions of any character;

2) The .algorithm part can be omitted to imply all algorithms under the
security provider and service type;

3) The .service-type.algorithm part can be omitted to imply all service
types and algorithms under the security provider; and,

4) The non-canonical form #1 can be combined with either #2 or #3.


Security provider, service type and algorithm names escaping
=================================================================

If the security provider, service type or algorithm name contains
any of
the characters "\", ".", ";" or "*", they have to be escaped by
prepending the character "\". If the character "\" is found not
escaping
a character, it's silently discarded.

White spaces are discarded at the beginning and end of names.

It's worth mentioning that the described escaping rules apply to the
jdk.security.providers.filter property value as read in
java.lang.System::getProperty or java.security.Security::getProperty.
Additional escaping might be needed depending on how the property is
passed. For example, Security properties require "\" characters to be
escaped. Thus, to match a provider name whose name is "\.", a filter
would require the "jdk.security.providers.filter=\\\\\\." entry in the
java.security file. See more about this in java.util.Properties::load
[1].


Examples (correct)
====================

--

Enable all security providers, service types and algorithms:

jdk.security.providers.filter=

or

jdk.security.providers.filter=*

or

jdk.security.providers.filter=*.*

or

jdk.security.providers.filter=*.*.*

--

Enable everything except for the MD5 algorithm in MessageDigest
services
when implemented by the SUN security provider:

jdk.security.providers.filter=!SUN.MessageDigest.MD5; *

--

Enable everything except for the MD5 algorithm in MessageDigest
services, irrespective of the security provider:

jdk.security.providers.filter=!*.MessageDigest.MD5; *

--

Enable everything except for algorithms using MD5, irrespective of the
security provider and the service type:

jdk.security.providers.filter=!*.*.*MD5*; *

Notice that in this case there are wildcards at the beginning and
end of
the algorithm name. The reason is to match MD5 uses in algorithms such
as HmacMD5, MD5withRSA, PBEWithMD5AndDES, etc.

--

Enable everything except for the RC4 algorithm in Cipher services when
implemented by the SunJCE security provider:

jdk.security.providers.filter=!SunJCE.Cipher.ARCFOUR; *

or

jdk.security.providers.filter=!SunJCE.Cipher.RC4; *

or

jdk.security.providers.filter=!SunJCE.Cipher.1\.2\.840\.113549\.3\.4; *

--

Enable the SUN security provider only, with all its service types and
algorithms. Other security providers must be disabled.

jdk.security.providers.filter=SUN

--

Enable the SUN security provider only, with all its service types and
algorithms except for MessageDigest. Other security providers must be
disabled.

jdk.security.providers.filter=!SUN.MessageDigest; SUN

--


Examples (mistakes)
====================

--

Enable everything except for the MD5 algorithm, irrespective of the
security provider and the service type:

jdk.security.providers.filter=*; !*.*.MD5

This is wrong because the pattern "*" is matched first and a decision
allowing MD5 will be made immediately after. The pattern "!*.*.MD5"
will
never be checked.

--

Enable all SUN service types except for MessageDigest. Disable other
security providers.

jdk.security.providers.filter=!SUN.MessageDigest

While non-SUN security providers are effectively disabled, this is
wrong
because SUN services other than MessageDigest will not match any
pattern
and, by default, the decision is to deny registration.

--

Enable the SunPKCS11 security provider only.

jdk.security.providers.filter=SunPKCS11

This is wrong because the SunPKCS11 provider has to be identified by
its
name instead of its class. A possible name would have the form of
SunPKCS11-NAME. In a filter, this can be matched either by
"SunPKCS11-NAME" or "SunPKCS11-*".

--


Look forward to your thoughts.

Thanks.-


--
[1] -
https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/Properties.html#load%28java.io.Reader%29

(†) - Thanks to Francisco Ferrari (@fferrari) for his contributions to
this proposal.




On 2/24/23 14:49, Anthony Scarpino wrote:
Hi Martin,

Interesting proposal.  I think Alternative 1 is a better direction to
explore from a code structure standpoint.  If I remember correctly,
Preferred Provider is accessed when getting a service or instance
of the
algorithm.  That happens on a per-operation basis.  What you
describe is
something that would reshape contents of the ProviderList where
algorithms or services would not be in the list at all.  That is
were I
think #2 gets too complex in trying to handle both in the same
property.
     #2 may end up putting all checks in a per-operation check,
hindering
performance every time as the list grows.

I agree this is mostly used in the FIPS situation or where someone
wants
to disable an algorithm completely, say MD5.  In those cases it's best
to just prevent the algorithm from ever being available.

On the smaller details side that you list.  I think the name
".enabled"
doesn't fit, particularly as the first thing in the example
disables all
Ciphers :).  I don't have any suggestions at this time.

As far as the syntax.  I think it maybe a bit difficult to parse in
code
and mental to disable all Ciphers, then enable just for SunJCE and
SUN.
The SUN '*" confused me until I realized you were enabling Ciphers.
Seems too easy to get wrong.  I know you weren't making a formal spec,
but we have to start somewhere.

thanks

Tony


On 2/17/23 10:52 AM, Martin Balao wrote:
Hi,

We would like to discuss a limitation in the current configuration
capabilities for security providers and possible solutions that we
are
exploring (†).

As you know, current configuration capabilities in java.security
allow
users to install security providers, decide their priority in a list
(security.provider.<n> properties) and even circumvent this priority
for specific algorithms (jdk.security.provider.preferred property).
However, there is no granularity in terms of what service types and
algorithms are enabled once a security provider is installed: it's an
all or nothing scheme. It is worth noting that security providers can
bring with them a diverse range of service types. As an example, the
SUN security provider comes with the following service types:
SecureRandom, Signature, KeyPairGenerator,
AlgorithmParameterGenerator, AlgorithmParameters, KeyFactory,
MessageDigest, CertificateFactory, KeyStore, CertStore, Policy,
Configuration, CertPathBuilder and CertPathValidator [1].

In some cases, the user may need to enforce that all cryptographic
primitives come from a specific security provider. This could happen,
for example, when operating in a FIPS-compliant environment or under
strict security policies. To better illustrate, let's say that the
user requires that all cryptographic operations are performed in a
Hardware Security Module (HSM). On the OpenJDK side, this means that
the implementation for Cipher, Signature, Mac and other cryptographic
services must be the one in the SunPKCS11 security provider. Let's
also suppose that other non-cryptographic services such as those for
certificates validation and TLS are required, and their
implementation
is in the SUN and SunJSSE security providers respectively. Setting
SunPKCS11 at the highest priority of the list is not a strong
guarantee to ensure that all cryptographic operations come from it:
it's possible that an algorithm for Signature is not implemented in
SunPKCS11 or in its underlying token but in the SUN security
provider.
Disabling the SUN security provider wouldn't be an option in this
case
because we need its certificates validation service.

This problem goes beyond OpenJDK default security providers. Even if
we come up with a new layout for service types, algorithms and
providers —putting backward compatibility issues aside—, there is
always the possibility that a 3rd party security provider does not
follow any services grouping convention. It might also be the case
that we need to disable a specific algorithm only —i.e. for
cryptographic policy reasons— and TLS or JAR signing properties fall
short.

In our view, it would be beneficial to add more configuration
flexibility and control to the existing API in which any security
provider can be plugged in, in the form of deciding which service
types and algorithms are enabled for each installed provider.

There are 2 alternatives that we are exploring to tackle this
problem.

Alternative #1
===========================

Introduce a new security property to decide which service types and
algorithms are enabled for each security provider. The default value
for this property would be empty, which keeps this feature disabled
and all services from installed security providers available.

As for the new property's syntax and semantics, we've been
considering
an allow-list along the lines of:

jdk.security.provider.enabled = security-provider-1 {
service-type-1 :
alg-1, ... ; ... } , ...

Note: we need a formal syntax specification, this is for illustration
only.

As part of the syntax we are considering the use of wildcards (*) to
match multiple security providers, service types and algorithms, and
minus signs (-) to remove service types. When a service type is
removed, the action applies to all algorithms and any attempt to
specify them explicitly would be an error. The minus sign cannot be
used at the algorithm level. We are also thinking that in case of a
partial or total contradiction between conditions, the right-most
value applies on top of the others. If a security provider, service
type or algorithm does not exist, we can simply write a debug warning
and ignore it. As for the name of the algorithms, we can also include
Ciphers transformations.

Example:

jdk.security.provider.enabled = * { -Cipher }, SunJCE { Cipher :
AES/GCM/NoPadding, DES ; Signature }, SUN { * ; -Signature }

This would be interpreted as:

     * Irrespective of the provider (*), Cipher services should be
removed (-). This rule would be superfluous in this case because the
property itself is an allow-list and there is nothing to the left
that
enables Cipher service types for any provider.
     * From the SunJCE security provider, Cipher services with
AES/GCM/NoPadding and DES transformations are allowed, and Signature
services with any algorithm are allowed. Notice that there is a
shortcut here: the algorithm list that follows the service name, "':
alg-1, ..." is optional. When omitted all the service's algorithms
are
enabled.
     * From the SUN security provider, every service type is allowed
except Signature (recall that a minus sign can only apply to a
service, removing all associated algorithms).

It's not the goal of this proposal to invalidate property values that
lead to inconsistent internal states, such as "the Cipher service of
SunJCE depends on AlgorithmParameters from SUN". This is because the
combinations for a check are virtually infinite: there can be 3rd
party security providers with their own semantics and
dependencies. In
the same way, we cannot determine at start time any application
dependencies. It's up to the user to analyze all types of
dependencies
before setting a value.


Alternative #2
===========================

Introduce a boolean security property to turn the value of the
existing jdk.security.provider.preferred property into the only
combinations of algorithm, service and provider that are allowed:

jdk.security.provider.preferredOnly = true

The default value for the new property would be "false", keeping the
current "preferred" behavior in which all algorithms and services
from
installed security providers are available.

Contrary to Alternative #1, the user has to explicitly list the
algorithms and cannot rely on wildcards to express wide categories
such as "all Cipher algorithms from SunJCE" or "all algorithms from
SunJCE". The use of minus signs to remove service types or algorithms
wouldn't be available either.

In order to mitigate the burden on users we can consider extending
jdk.security.provider.preferred syntax as long as we keep
backward-compatibility and stay within the boundaries of a
"preferred"
semantics. For example, we can accept a value of
"jdk.security.provider.preferred=SunJCE" to mean that any service and
any algorithm from SunJCE is either preferred or allowed,
depending on
the value of jdk.security.provider.preferredOnly. This case would
be a
service type and algorithm wildcard. We can also define an
algorithms-only wildcard, such as Cipher.*:SunJCE.

Alternative #2 has the advantage of reusing most or all of the
existing syntax. However, it's worth noticing that it implies an
overloaded semantic that can turn confusing or inconvenient in some
cases. As an example, a user that relies on the prioritized security
providers list for most of the algorithms and has only a few
preferred
exceptions, would need to express preferences by extension upon
turning on this feature. Alternative #1 keeps preferences and
availability as two separate concepts, in a more clear way.


Thanks,
Martin.-

--
[1] -
https://docs.oracle.com/en/java/javase/17/security/oracle-providers.html#GUID-3A80CC46-91E1-4E47-AC51-CB7B782CEA7D
(†) - Thanks to @fferrari for his contributions to this proposal.








Reply via email to