Hello there! >> 3. if we need to, transform this APDU into a sequence of chained >> APDUs, or something else; for instance, if I understand correctly, the >> Spanish DNIe doesn't support command chaining, but rather wants this a >> sequence of ENVELOPE APDUs; > > Enveloped APDUs and Chained APDUs are not strictly the same: Enveloped > APDUs can send effectively only one APDU (such as an APDU with a lot of > data), where chained APDUs can perform a certain operation consisting of > one or more APDUs (such as a get challenge chained with a PSO).
Sure. In the DNIe example that I brought, they share the same purpose (breaking up long APDUs), but they are different beasts and can be tamed for usage in different situations. > So your transformation concept is not strictly bound to SM but rather to > smart card operations in general. An other approach would be to hard > code the single steps of transmission rather than to use a more flexible > chain. This is right. > Note that hardcoding is already done in sc_transmit_apdu: An > APDU, that is too long (e.g. extended length) is broken into multiple > pieces and sent. The same is done for the response via get response > commands. So what is the advantage of coding all this with your > approach? Code reuse and streamlining of the architecture. I've seen that different cards do this differently. As en example, let me show how OpenDNIe solved this problem: by 1. adding the "wrap_apdu" operation hook in the card driver 2. adding a global variable of type dnie_private_data_t to store all information needed for SM processing 3. calling wrap_apdu() early in sc_transmit_apdu() 4. wrap_apdu() looks at the private data to check if SM is active; if it is, it calls cw_encode_apdu() to process it 5. wrap_apdu() calls dnie_transmit_apdu(), the custom replacement for sc_transmit_apdu(), which knows how to handle long APDUs for the DNIe card 6. finally, if SM is active, wrap_apdu() calls cw_decode_apdu(), so that it can return an authenticated and/or unencrypted response to the upper layer. This works (for one card), but hard-coding this approach means that each driver has to reinvent the wheel: code duplication, tight coupling, difficult maintenance. With a transformation chain, a driver could pick the bits it needs and at least some of them could be "black boxes" that carry out useful work without forcing the card driver to know about their innards. > Don't fix what is not broken. Your approach is useful and could be > integrated, but if you don't have something in mind that can be done > only or better by your approach, it's hard to justify the work it needs. Yes, I am afraid that I'm over-engineering it. On the other hand, I think that the code for this is rather short and that the other approaches that come to my mind seem to lead to cut-and-paste drivers. > The German identity card uses a very good replacement for basic access > control. It is basically an anonymous key agreement consisting of > something like 6 APDUs. If some other APDU slips into this sequence, > then it breaks the command chaining (this is what I mean with > interfering). The key agreement could be one single operation queued up > with multiple APDUs in one chain. But it could also be superseded by > sending a sequence of subsequent APDUs (one at a time). This would not > require the overhead of a transmit chain. Take for example > sc_set_security_env or sc_pin_cmd. They are interpreted by the card > driver and the driver can also send multiple (hard coded) APDUs instead > of just one (no matter if they are standardized or not). I see what you mean. On one hand, I think that the usefulness of transforming a whole APDU sequence (or an APDU into an APDU sequence) only arises when you recycle the code you may need to break up long APDUs for a given card. In the case that you show, there is certainly no need for this; you can just lock the card and perform the key agreement. The ability to handle an APDU sequence, in my "concept" of a transformation chain, is only useful in the last steps, not in the early ones. As for where this stuff belongs and for whether a transform chain is needed, your example is a perfect fit. Let us imagine that other cards use the same key agreement scheme or a similar one, but must perform different checks to know what algorithms must be used for a given objects (early steps before SM), and have a different way of dealing with long APDUs (last steps after SM and before APDU transmission). In the transform chain, a single function is responsible for *both* ways of the transformation. If there is anything clever in the approach (which I'm not sure of :) ), this is it. A simpler approach would be to just provide a wrap_apdu() hook and deal there with the whole sequence, just like in DNIe. This way we would still achieve code reuse, by simply breaking up the steps in a logical way to get the desired loose coupling, and provide them as functions available to all drivers. So it looks simpler than a transform chain, but I'm afraid it isn't, because then the call stack does not match the transformation stack. This means that: 1. the wrapping function must invoke an encode() function for each step, perform the card transaction, then invoke each decode() step in reverse; 2. all encode() and decode() step must perform all their memory management on the heap with malloc() when encoding and free() when decoding, because they won't be able to leave anything on the stack. This doesn't only apply to memory space for APDUs: state management becomes somewhat trickier too; 3. when debugging, you aren't able to just look at the call stack and see what was is going on. Thanks, -- Emanuele _______________________________________________ opensc-devel mailing list opensc-devel@lists.opensc-project.org http://www.opensc-project.org/mailman/listinfo/opensc-devel