----- Original Message ----- From: "Tommaso Cucinotta" <[EMAIL PROTECTED]>
To: "MUSCLE" <[email protected]>
Sent: Tuesday, March 15, 2005 4:28 PM
Subject: Re: [Muscle] muscle getChallenge, versus GP 2 way authentication



Peter Williams wrote:
Can anyone explain the muscle applet external authentication code's design, to me?

Hi,

the intended usage of the protocol command is straightforward: allow the
other end communicating with the card to prove possession of a
cryptographic key, either providing an encryption of the card-generated
challenge, or providing a signature on the card-generated challenge.
In the first case, the ExtAuth command is invoked with parameters
analogous to an MSCComputeCrypt invokation for decrypting a
cryptogram (by using an on-card symmetric key or asymmetric public key).
In the latter one, it is invoked with parameters
analogous to an MSCComputeCrypt invokation for verifying a signature
(by using an on-card asymmetric public key).

The code is aligned with this description.

In either cases, no data is returned to the application, but only the
result of the test: SUCCESS if it succeeded and the corresponding
identity was logged in, AUTH_FAILED if cryptogram verification failed,
or one of the other possible errors.

Are you claiming the Applet code has a bug/misbehaviour in case (b) ?

Im trying to use the DES-based external authentication, for the first time - using it as programmed. Then, Ill change the security protocol to cooperate with the GP authentication process. But first, make it work correctly, as is!


Actual code Im using (originally from CVS) enclosed, per request. Compare the final few code lines of the CD_DECRYPT case, and the CD_VERIFY case. Only the signature verify case seems to handle the velocity counters, and the registration of the principal as authenticated - in the logged_id bitmask.



#ifdef USE_AUTH
   private void GetChallenge(APDU apdu, byte[] buffer) {
if (buffer[ISO7816.OFFSET_P1] != (byte) 0x00)
    ISOException.throwIt(SW_INCORRECT_P1);

short bytesLeft = Util.makeShort((byte) 0x00,
    buffer[ISO7816.OFFSET_LC]);
if (bytesLeft != apdu.setIncomingAndReceive())
    ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);

if (bytesLeft < 4)
    ISOException.throwIt(SW_INVALID_PARAMETER);

short size = Util.getShort(buffer, ISO7816.OFFSET_CDATA);
short seed_size = Util.getShort(buffer, (short) (ISO7816.OFFSET_CDATA + 2));
if (bytesLeft != (short) (seed_size + 4))
ISOException.throwIt(SW_INVALID_PARAMETER);


byte data_loc = buffer[ISO7816.OFFSET_P2];

if ((data_loc != DL_APDU) && (data_loc != DL_OBJECT))
    ISOException.throwIt(SW_INVALID_PARAMETER);

if (randomData == null)
    randomData = RandomData.getInstance(RandomData.ALG_SECURE_RANDOM);

if (seed_size != (short) 0x0000)
randomData.setSeed(buffer, (short) (ISO7816.OFFSET_CDATA + 4), seed_size);


// Allow size = 0 for only seeding purposes
if (size != (short) 0x0000) {
om.destroyObject(OUT_OBJECT_CLA, OUT_OBJECT_ID, true);
// Automatically throws exception if no memory
#if 0
short base = om.createObject(OUT_OBJECT_CLA, OUT_OBJECT_ID, (short) (size + 2), getRestrictedACL(), (short) 0);
#else
short base = om.createObject(OUT_OBJECT_CLA, OUT_OBJECT_ID, (short) (size + 2), getPublicACL(), (short) 0);
#endif
mem.setShort(base, size);
randomData.generateData(mem.getBuffer(), (short) (base + 2), size);
/* Remember that out object contains getChallenge data (to avoid attacks
* pretending to write the out object before extAuth) */
getChallengeDone = true;
// Actually return data in APDU only if DL_APDU specified.
if (data_loc == DL_APDU) {
sendData(apdu, mem.getBuffer(), base, (short) (size + 2));
/* Don't destroy out object ! Generated data is needed in ExtAuth ! */
/* Not if running without external authentication */
#ifndef WITH_EXT_AUTH
om.destroyObject(OUT_OBJECT_CLA, OUT_OBJECT_ID, true);
#endif
}
}
}


#ifdef WITH_EXT_AUTH
   private void ExternalAuthenticate(APDU apdu, byte[] buffer) {
if (buffer[ISO7816.OFFSET_P2] != (byte) 0x00)
    ISOException.throwIt(SW_INCORRECT_P2);

short bytesLeft = Util.makeShort((byte) 0x00,
      buffer[ISO7816.OFFSET_LC]);
if (bytesLeft != apdu.setIncomingAndReceive())
    ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);

byte key_nb = buffer[ISO7816.OFFSET_P1];

if ((key_nb < 0) || (key_nb >= MAX_NUM_AUTH_KEYS)
    || (keys[key_nb] == null))
    ISOException.throwIt(SW_INCORRECT_P1);

if (bytesLeft < 3)
    ISOException.throwIt(SW_INVALID_PARAMETER);

/* Verify that a GetChallenge has been issued */
if (! getChallengeDone)
    ISOException.throwIt(SW_OPERATION_NOT_ALLOWED);
/* Clear getChallengeDone flag */
getChallengeDone = false;
/* Retrieve getChallenge() data position and check it */
short chall_base = om.getBaseAddress(OUT_OBJECT_CLA, OUT_OBJECT_ID);
if (chall_base == MemoryManager.NULL_OFFSET)
    ISOException.throwIt(SW_OPERATION_NOT_ALLOWED);
short obj_size = om.getSizeFromAddress(chall_base);
if (obj_size < 3)
    ISOException.throwIt(SW_INVALID_PARAMETER);
short chall_size = mem.getShort(chall_base);
/* Actually GetChallenge() creates an object of exact size */
if (obj_size != (short) (2 + chall_size))
    ISOException.throwIt(SW_INVALID_PARAMETER);

byte ciph_mode = buffer[ISO7816.OFFSET_CDATA];
byte ciph_dir = buffer[(short) (ISO7816.OFFSET_CDATA + 1)];

byte[] src_buffer; /* The buffer of encrypted data */
short src_offset; /* The offset of encrypted data in src_buffer[] */
short src_avail; /* The available encrypted data (+ size) */
switch (buffer[(short) (ISO7816.OFFSET_CDATA + 2)]) {
case DL_APDU:
    src_buffer = buffer;
    src_offset = (short) (ISO7816.OFFSET_CDATA + 3);
    src_avail = (short) (bytesLeft - 3);
    break;
case DL_OBJECT:
    src_offset = om.getBaseAddress(IN_OBJECT_CLA, IN_OBJECT_ID);
    if (src_offset == mem.NULL_OFFSET)
 ISOException.throwIt(SW_OBJECT_NOT_FOUND);
    src_buffer = mem.getBuffer();
    src_avail = om.getSizeFromAddress(src_offset);
default:
    ISOException.throwIt(SW_INVALID_PARAMETER);
    return; // Suppress compiler warning
}
if (src_avail < 2)
    ISOException.throwIt(SW_INVALID_PARAMETER);
short size = Util.getShort(src_buffer, src_offset);
if (src_avail < (short) (size + 2))
    ISOException.throwIt(SW_INVALID_PARAMETER);

// Null key already checked above
Key key = keys[key_nb];

// Check if identity is actually blocked
if (keyTries[key_nb] == (byte) 0)
    ISOException.throwIt(SW_IDENTITY_BLOCKED);

byte key_type = key.getType();

boolean result = false;

switch (ciph_dir) {
case CD_DECRYPT:
#ifndef WITH_DECRYPT
 ISOException.throwIt(SW_UNSUPPORTED_FEATURE);
 return;
#else
    byte jc_ciph_alg;
    switch (ciph_mode) {
#ifdef WITH_RSA
    case CM_RSA_NOPAD:
 if (key_type != KeyBuilder.TYPE_RSA_PUBLIC)
     ISOException.throwIt(SW_INVALID_PARAMETER);
 jc_ciph_alg = Cipher.ALG_RSA_NOPAD;
 break;
    case CM_RSA_PAD_PKCS1:
 if (key_type != KeyBuilder.TYPE_RSA_PUBLIC)
     ISOException.throwIt(SW_INVALID_PARAMETER);
 jc_ciph_alg = Cipher.ALG_RSA_PKCS1;
 break;
#else
    case CM_RSA_NOPAD:
    case CM_RSA_PAD_PKCS1:
 ISOException.throwIt(SW_UNSUPPORTED_FEATURE);
#endif // ifdef WITH_RSA

#if defined(WITH_DES) || defined(WITH_3DES)
    case CM_DES_CBC_NOPAD:
 if (key_type != KeyBuilder.TYPE_DES)
     ISOException.throwIt(SW_INVALID_PARAMETER);
 jc_ciph_alg = Cipher.ALG_DES_CBC_NOPAD;
 break;
    case CM_DES_ECB_NOPAD:
 if (key_type != KeyBuilder.TYPE_DES)
     ISOException.throwIt(SW_INVALID_PARAMETER);
 jc_ciph_alg = Cipher.ALG_DES_ECB_NOPAD;
 break;
#else
    case CM_DES_CBC_NOPAD:
    case CM_DES_ECB_NOPAD:
 ISOException.throwIt(SW_UNSUPPORTED_FEATURE);
#endif // if defined(WITH_DES) || defined(WITH_3DES)
    default:
 ISOException.throwIt(SW_INVALID_PARAMETER);
 return;  // Suppress compiler warning
    }
    Cipher ciph = getCipher(key_nb, jc_ciph_alg);
    ciph.init(key, Cipher.MODE_DECRYPT);
    // Create temporary buffer
    short temp = mem.alloc(chall_size);
    if (temp == MemoryManager.NULL_OFFSET)
 ISOException.throwIt(SW_NO_MEMORY_LEFT);
    short written_bytes =
 ciph.doFinal(src_buffer, (short) (src_offset + 2), size,
       mem.getBuffer(), temp);

    /* JC specifies that, when decrypting, padding bytes are cut out *
     *   so after a decrypt we should get the same size as the challenge*
     *   and they should be less than provided encrypted data  */

#ifdef APPLET_DEBUG
    /* Just to be sure that I understood JC API in the right way...*/
    if (written_bytes > chall_size)
 ISOException.throwIt(SW_INTERNAL_ERROR);
#endif

    if ((written_bytes == chall_size)
 && (Util.arrayCompare(mem.getBuffer(), temp,
         mem.getBuffer(), (short) (chall_base + 2),
         chall_size) == (byte) 0)
 )
 result = true;

    sendData(apdu, mem.getBuffer(), temp, written_bytes);
    mem.free(temp);
    break;
#endif
case CD_VERIFY:
#ifndef WITH_SIGN
    ISOException.throwIt(SW_UNSUPPORTED_FEATURE);
    return;
#else
    byte jc_sign_alg;
    switch (ciph_mode) {
    case CM_DSA_SHA:
#ifdef WITH_DSA
 if (key_type != KeyBuilder.TYPE_DSA_PUBLIC)
     ISOException.throwIt(SW_INVALID_PARAMETER);
 jc_sign_alg = Signature.ALG_DSA_SHA;
 break;
#else
 ISOException.throwIt(SW_UNSUPPORTED_FEATURE);
 return;
#endif
    case CM_RSA_PAD_PKCS1:
#ifdef WITH_RSA
 if (key_type != KeyBuilder.TYPE_RSA_PUBLIC)
     ISOException.throwIt(SW_INVALID_PARAMETER);
#ifndef FORCE_SHA1
 jc_sign_alg = Signature.ALG_RSA_MD5_PKCS1;
#else
 jc_sign_alg = Signature.ALG_RSA_SHA_PKCS1;
#endif
 break;
#else
 ISOException.throwIt(SW_UNSUPPORTED_FEATURE);
 return;
#endif
    default:
 ISOException.throwIt(SW_INVALID_PARAMETER);
 return;  // Suppress compiler warning
    }
    Signature sign = getSignature(key_nb, jc_sign_alg);
    sign.init(key, Signature.MODE_VERIFY);
    if (sign.verify(mem.getBuffer(), (short) (chall_base + 2), chall_size,
      src_buffer, (short) (src_offset + 2), size))
 result = true;
    break;
#endif
default:
    ISOException.throwIt(SW_INVALID_PARAMETER);
}
if (result) {
    LoginStrongIdentity(key_nb);
    // Reset try counter
    keyTries[key_nb] = MAX_KEY_TRIES;
    om.destroyObject(IN_OBJECT_CLA, IN_OBJECT_ID, true);
    om.destroyObject(OUT_OBJECT_CLA, OUT_OBJECT_ID, true);
} else {
    // Decrease try counter
    keyTries[key_nb]--;
    LogoutIdentity((byte) (key_nb + 8));
    om.destroyObject(IN_OBJECT_CLA, IN_OBJECT_ID, true);
    om.destroyObject(OUT_OBJECT_CLA, OUT_OBJECT_ID, true);
    ISOException.throwIt(SW_AUTH_FAILED);
}
   }
#endif // ifdef WITH_EXT_AUTH
#endif

Please, send me the version you're referring to in your message, as it's
a while I just use to play with an internal version at our lab for
biometrics related stuff.

((maybe a key should be provided with a restriction on the allowed
ExtAuth modes of operation when it is created/injected for increasing
security .... anyway, in the first protocol formulation there were not
so many choices :-) ))

(a) when cipher-dir == CD_VERIFY, the applet will verify an MD5RSA signature (over the Value of challenge object), and perform strongLogon, if the verification suceeds.

(b) when cipher-dir == CD_DECRYPT, the applet will decrypt the incoming ciphertext using TDES_CBC_NOPAD, compare the plaintext for equality with the stored challenge value V, and reflect the plaintext value back to the consumer (over an insecure channel), if there is a match, without performing strongLogon.

What is the context for (b)? How is it to be used? Is there any host sample code? Are (a) and (b) supposed to be used in conjunction, perhaps?

Once upon a time, all of this was tested. If I manage to find the old test programs, I'll be more than happy to send them to you. As an alternative, maybe I have at reach some test code for the JNI bridge of MuscleCard API -- have to check --... it might also be the case that just writing a short program is actually faster than searching this old stuff :-)

If the two cases are strict alternatives, we can make them equivalent in terms of the access control model.


Bye,
Tommaso.

--
,----------------------------------------------------.
|  Tommaso Cucinotta PhD <t.cucinotta *at* sssup.it> |
>----------------------------------------------------<
!       Scuola Superiore di Studi Universitari       !
!              e Perfezionamento S.Anna              !
!    Pisa                                   Italy    !
`----------------------------------------------------'
_______________________________________________
Muscle mailing list
[email protected]
http://lists.drizzle.com/mailman/listinfo/muscle

_______________________________________________
Muscle mailing list
[email protected]
http://lists.drizzle.com/mailman/listinfo/muscle

Reply via email to