Closed hko-s closed 3 years ago
We have seen several differences between Javacard implementations of P-521.
Could you try to run your test with the following patch applied ? https://github.com/ANSSI-FR/SmartPGP/blob/dcd0d42b7abf2809881eb727ce68f9f5e68c63b7/patches/fix_p521_528_bits.patch
jcardsim doesn't seem to support that approach:
javacard.security.CryptoException
at javacard.security.CryptoException.throwIt(Unknown Source)
at com.licel.jcardsim.crypto.ECKeyImpl.getDefaultsDomainParameters(ECKeyImpl.java:300)
at com.licel.jcardsim.crypto.ECKeyImpl.<init>(ECKeyImpl.java:67)
at com.licel.jcardsim.crypto.ECPrivateKeyImpl.<init>(ECPrivateKeyImpl.java:41)
at javacard.security.KeyBuilder.buildKey(Unknown Source)
at fr.anssi.smartpgp.PGPKey.importECKey(PGPKey.java:384)
at fr.anssi.smartpgp.PGPKey.importKey(PGPKey.java:517)
at fr.anssi.smartpgp.SmartPGPApplet.processPutData(SmartPGPApplet.java:913)
at fr.anssi.smartpgp.SmartPGPApplet.process(SmartPGPApplet.java:1635)
at com.licel.jcardsim.base.SimulatorRuntime.transmitCommand(SimulatorRuntime.java:303)
at com.licel.jcardsim.base.Simulator.transmitCommand(Simulator.java:260)
at com.licel.jcardsim.base.CardManager.dispatchApduImpl(CardManager.java:66)
at com.licel.jcardsim.base.CardManager.dispatchApdu(CardManager.java:36)
at com.licel.jcardsim.remote.VSmartCard$IOThread.run(VSmartCard.java:154)
The exception gets thrown because 528 is not handled in this switch statement:
Ok, this patch is a workaround for some smartcards.
The leading zero is here to have all related parameters the same length in bytes only. It is also present in recommended parameters values (see section 2.9.1 in https://www.secg.org/SEC2-Ver-1.0.pdf)
Something that seems really odd is that in jcardsim ECKeyImpl the parameters are "automatically" set according to the key length, and are assumed to be either sect
or secp
curves. What about brainpool curves with exact same key sizes (256, 384) ?
From what we see in ECKeyIml it seems brainpoolP512 cannot work at all. Could you test this one ?
Unfortunately I can't easily test brainpool.
However, just by looking at the jcardsim code, it seems to me that brainpool is not supported in the getDefaultsDomainParameters() function we're looking at.
(I also can't find any references to brainpool with jcardsim on search engines, for whatever that's worth. Maybe noone has used jcardsim with brainpool?)
Ok.
For P521 the problem is in jcardsim for two reasons. The first reason is that the buildKey is called with 521 as key size, which means that the underlying implementation must allocate 66 bytes for all parameters (65 bytes is not enough). The second reason is that jcardsim makes a wrong assumption by automatically/implicitly choosing a sect or secp curve, and by automatically settings the parameters. This is not the behavior expected by the JavaCard API.
It seems JCAlgtest reports the same problem of index out of bound with jCardSim-3.0.5-SNAPSHOT (previous versions seem to not support ECC).
See line ALG_EC_FP LENGTH_EC_FP_521 at column c53: https://www.fi.muni.cz/~xsvenda/jcalgtest/table.html
Thank you for your very helpful assessment of the problem!
I think the root mistake in jCardSim is https://github.com/licel/jcardsim/blob/master/src/main/java/com/licel/jcardsim/crypto/ECKeyImpl.java#L63, which seems to directly contradict the javadoc comment for this constructor. However, a number of the integration tests of jCardSim apparently rely on this behavior, so I decided against attempting to change this.
Instead, I have opted for a simple workaround for the specific case that actually fails for me, namely setting a larger byte vector in a ByteContainer: https://github.com/hko-s/jcardsim/commit/ff1b4d934b24e635cb56322ec47ec7a19bebb38e - while this hack isn't entirely satisfying, I don't see how it makes anything in jCardSim worse than it was before.
And with this patch, I can successfully run my testsuite (including for NIST P-521) against SmartPGP running on jCardSim.
(FYI, in case anyone is interested, I have made container images to run CI against SmartPGP with my OpenPGP card client code: https://gitlab.com/hkos/openpgp-card-images)
When running SmartPGP on jcardsim, both import and key generation fail for NIST P-521.
I have explored and will outline why this happens, but I don't understand the codebases well enough to suggest how the issue should be resolved.
The problem, when it happens, looks like this:
Why do we get an ArrayIndexOutOfBoundsException?
The problem seems to be that a
com.licel.jcardsim.crypto.ECPublicKeyImpl
object gets initialized first by jcardsim. Later, new values (one of them with a larger size) are written into this object from ECPublicKeyImpl. The second attempt at setting the valueb
in the object fails.The first initalization for key generation is performed at https://github.com/ANSSI-FR/SmartPGP/blob/dcd0d42b7abf2809881eb727ce68f9f5e68c63b7/src/fr/anssi/smartpgp/PGPKey.java#L249 and analogously for import, here: https://github.com/ANSSI-FR/SmartPGP/blob/dcd0d42b7abf2809881eb727ce68f9f5e68c63b7/src/fr/anssi/smartpgp/PGPKey.java#L387-L389
In this first initialization, the value
b
gets set to51953eb9618e1c9a1f929a21a0b68540eea2da725b99b315f3b8b489918ef109e156193951ec7e937b1652c0bd3bb1bf073573df883d2c34f1ef451fd46b503f00
, for P-521 (note, this is 65 bytes of data, without a leading zero byte).Later, SmartPGP attempts to set the value of
b
again, in the same ECPublicKeyImpl object, from https://github.com/ANSSI-FR/SmartPGP/blob/dcd0d42b7abf2809881eb727ce68f9f5e68c63b7/src/fr/anssi/smartpgp/ECParams.java#L60However, here the value that gets set is
0051953eb9618e1c9a1f929a21a0b68540eea2da725b99b315f3b8b489918ef109e156193951ec7e937b1652c0bd3bb1bf073573df883d2c34f1ef451fd46b503f00
(note: this is 66 bytes, with a leading zero byte, this value comes from SmartPGP inECConstants.java:239
. This constantansix9p521r1_b
happens to be the only one inECConstants.java
that has a leading zero byte).This second attempt to set
b
fails, because the underlying jcardsim ByteContainer forb
has been sized to 65 bytes when it was first set. The new value is thus too large by one byte (causing the ArrayIndexOutOfBoundsException from above).Workaround
I have tested that commenting out ECParams.java:60 leads to SmartPGP on jcardsim working for NIST P-521.
I don't know how this should be properly resolved
It's unclear to me if re-setting
b
at ECParams.java:60 is actually useful in any scenario. Here, it seems to be redundant.Also, it's unclear to me if and when the leading zero byte of
b
(for NIST P-521) should be stored.I don't know enough about how EC cryptography works, but I wonder how leading zero bytes should be treated generally. Should they always be stripped?
Finally: Could there be other related bugs/problems here, when other EC values have leading zero byte(s)? (In particular: the value of the user's public or private key)