Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

transaction authorizations and challenge stringify problem #66

Open
eoln opened this issue Sep 15, 2020 · 27 comments
Open

transaction authorizations and challenge stringify problem #66

eoln opened this issue Sep 15, 2020 · 27 comments
Assignees

Comments

@eoln
Copy link
Contributor

eoln commented Sep 15, 2020

There is a need to clarify some details in the process of transaction authorization agreement phase described here:
https://github.com/mojaloop/pisp/blob/master/docs/out/transfer/api_calls_detailed/PISPTransferDetailedAPI-page2.png

  • in POST /authorizations we are sending quote object to be signed by User
  • we receive signed by user value in authenticationValue.pinValue in PUT /authorization
  • then the DFSP needs to verify pinValue and this task is delegated to auth-service via POST /thirdpartyRequests/transactions/{ID}/authorizaiton endpoint
  • the auth service receives challenge property where stringified quote object is sent because it is needed for verification

THE PROBLEM:

  • there is a lack of description how quote object should be stringified before it will be signed and verified by different parties and they have to use the same approach elsewhere verification will always fail
  • there is a need for canonical stringification of quote object - the JSON.stringify isn't safe: JSON.stringify({a:1, b:2}) !== JSON.stringify({b:2, a:1})
  • there is a RISK that without the knowledge described above parties will have different strings to sign and verify so all authorization will fail

PROPOSED SOLUTION:

  • add to POST /authorization achallenge string field which will be signed by User
  • put the responsibility of generating the challenge on DFSP - with suggestions/requirements that it should contain some info from quote - like quote.ilpPacket for example - or even better salty hashed quote.ilpPacket where salt is only known to DFSP
  • use the same challenge in /thirdpartyRequests/transactions/{ID}/authorizaiton

CONS:

  • the need to rework POST /authorizations endpoint design

PROS:

  • canonical challenge without the risk of always failed authorization

ANOTHER APPROACH:

  • put into the design desired way of stringifing quote object, or its part only, and be sure this algorithm is well implemented in sign/verification steps
@lewisdaly
Copy link
Contributor

lewisdaly commented Sep 16, 2020

Thanks for this @eoln. I know we have dismissed this before, but this is exactly what the ilpPacket is designed for, and using that for our challenge would make life much easier. (see: https://docs.mojaloop.io/mojaloop-specification/documents/API%20Definition%20v1.0.html#45-ilp-packet)

Perhaps our friend @adrianhopebailie can tune in as he will likely have strong feelings about what should be signed as our 'challenge'.

@lewisdaly
Copy link
Contributor

lewisdaly commented Sep 16, 2020

How big is the ilpPacket? If it's not too big, we should use it, otherwise just the condition.

We specifically want something that can be reconstituted from the quote/transfer. So the condition may not be suitable.

@lewisdaly
Copy link
Contributor

@jgeewax 's decision: hash the ilpPacket, and then sign that as the challenge.

@lewisdaly
Copy link
Contributor

Perhaps we need to add a challenge field to POST /authorizations, which contains the hash of the ilpPacket

@mjbrichards
Copy link
Contributor

The ILP packet contains an encoded version of the Transaction object, which contains all the information about the transfer; so it is rich, but potentially quite large. If we want to sign something small, it might be better to use the condition, which is the signed and hashed version of the Transaction object created by the payee DFSP.

@elnyry-sam-k
Copy link
Member

Yes, I think the 'condition' makes sense here..

@jgeewax
Copy link
Contributor

jgeewax commented Sep 16, 2020 via email

@mjbrichards
Copy link
Contributor

The Condition is a signature generated by the Payee DFSP over the Transaction object using its private key. It can't be regenerated by any other party; but it is a reliable representation of the Transaction object which is otherwise accepted by all parties to the transfer, even though none of them could regenerate it independently. So I think that kind of meets your requirements, provided it's OK to substitute "the payer DFSP" for "I" in confirming that the thing signed was in fact a representation of the Transaction object. In addition, the Payee DFSP returns a fulfilment as proof that it has confirmed the transfer. This can be confirmed against the condition by any party who has both values by applying a SHA-256 hash to the fulfilment: the result should be the condition. This is the current form of security between parties in Mojaloop, and I'd like to retain it if we can...

@adrianhopebailie
Copy link

condition = SHA256(fulfillment) - i.e. Always 32 bytes

fulfillment is derived by the Payee DFSP when constructing the quote response. It MUST be unique per transaction and we recommend that it is derived from the Transaction object (fulfillment = HMAC_SHA256(secret, Transaction).

TL:DR; 👍 to using the Condition

@jgeewax
Copy link
Contributor

jgeewax commented Sep 23, 2020 via email

@mjbrichards
Copy link
Contributor

So we have three pieces of information: the Transaction object itself, the condition and the fulfilment. The payee DFSP creates the fulfilment from the Transaction object, and the condition from the fulfilment; and all parties can be confident that it was the Payee DFSP that created the condition because of non-repudiation. This is the basis which the payer DFSP uses to reserve the funds for its customer; and matching the condition with the fulfilment is the basis on which the switch and the payer DFSP will clear funds and record the transfer for settlement. So I think that the condition is a representation of the terms of the transfer which is accepted by all other parties, and should therefore be sufficient for the PISP. True, the condition was produced using a private key which nobody but the payer DFSP knows; but the object itself will be signed by the PISP using the FIDO private key that it holds, and verified by the FIDO server using the matching public key that it holds. So in order to prove that the PISP signed the object, all we need to know is the FIDO keys, since all parties have already accepted the condition as a valid representation of the terms of the transfer... At least, that's what I think

@eoln
Copy link
Contributor Author

eoln commented Sep 25, 2020

I am not sure we are still on track. Correct me if I am wrong: User is signing the challenge with his private key, so everybody who knows User's public key can verify pinValue (the challenge signing), especially the User's Payer DFSP. The User can't deny his signing because of the nature of asymmetric cryptography (Priv/Pub key) and challenge which is well known to all involved parties. The problem of this story is what we select as a challenge - am I right?

@mjbrichards
Copy link
Contributor

Absolutely and completely (I'm tempted to add, of course.) And please excuse my typo in l. 8, which should read "payee DFSP", not "payer DFSP". So I think I'd want to phrase the question as follows: we want to be able to verify that the PISP agreed to the terms of the transfer by signing the challenge (and, in principle, that it displayed those terms to its customer and obtained the customer's consent, but we have no way of verifying that.) Given that this is the case, is it sufficiently reliable for the PISP to sign a value (the condition) which is accepted as a valid representation of the terms of the transfer by all the other parties to the transfer, or do we want to insist that it should sign the actual representation of the terms in the Transaction object? I think that the first is sufficient, but I can certainly see the point of the second...

@eoln
Copy link
Contributor Author

eoln commented Sep 25, 2020

I agree that condition which is accepted by all parties before requesting the authorization from PISP is a good (small enough) candidate, so the execution of cryptographic functions will not be expensive. But @jgeewax pointed out also the problem of how complex could be the transaction verification process. So using the whole Transaction object in the canonically stringified form looks better in this context - but probably it is a little more expensive.

@jgeewax
Copy link
Contributor

jgeewax commented Sep 28, 2020

Right, my focus is on the verification process for a PISP. The thing that the user is signing should be something that they themselves could reconstruct from the information available without having to trust anything asserted by the DFSP.

When I think through the verification process, using the condition for this purpose seems like the PISP will still need to trust the DFSP, whereas in this scenario, the PISP is actively asserting that they DID NOT send the transaction in question. Here's the conversation I expect to happen in that case:

PISP: Hi. You said I sent $500 USD from Acct 1 @ Bank A to Acct 2 @ Bank B. That definitely wasn't me!
Switch/DFSP: Yes it was! You signed Cheque #1234. See? Here's Cheque 1234 with your signature on it.
PISP: Right.. but that doesn't have the source account, the amount, and the destination account.
Switch/DFSP: Well it does, it's right here as a HMAC I generated. What I did was take the details of the transaction and use HMAC with my secret key and I get these bytes. It's right there on the Cheque!
PISP: How do I know that that's what those bytes mean? How can I verify that you're not lying to me...?
Switch/DFSP: I'm not! Trust me! I can't give you the secret I used to get those bytes, but I promise that they're representative of your transaction!

At the end of this interaction, we've gone from "I don't trust you, prove it", to "You have paperwork, but none of that is true proof".

If, instead, we just signed a sha256(transaction details), the conversation would instead be something like the following:

PISP: Hi. You said I sent $500 USD from Acct 1 @ Bank A to Acct 2 @ Bank B. That definitely wasn't me!
Switch/DFSP: Yes it was! You signed Cheque #1234. See? Here's Cheque 1234 with your signature on it.
PISP: Right, but how do I know that signature represents me sending $500 ?
Switch/DFSP: Oh that's easy. See these bytes here you signed? Just take the transaction ($500, Bank A, Acct 1, Bank B, Acct 2, etc etc), SHA-256 them. If we use your public key to decrypt your signature (decrypt(sig, key[pub])) we end up with that exact SHA-256 hash! That's how we know you signed and approved of exactly these terms!
PISP: Oh. Damn. I must've made a mistake somewhere. Sorry about that.


In short, since HMAC (again, correct me if I'm wrong -- I'm a bit rusty on this...) is unidirectional, it's really only verifiable by those with the secret. We need something that can be generated by both sides, signed, and then that signature used to verify that it, indeed, was sent by an authoritative source (e.g., was definitely sent by the end user). I think that's the transaction itself...

@mjbrichards
Copy link
Contributor

I think I'd like to get a bit provocative here, because I think that the emphasis has shifted. In particular, the thing that's being contested here is not whether or not the PISP "sent" the money (because it can't,) but whether or not it warranted to the institution that could send the money (the payer DFSP) that the PISP had displayed the agreed terms of the transfer to its customer, and the customer had confirmed that the transfer should go ahead.... Now, that might not make a difference, but let's see how this plays out...

PISP: Hi. You said I told Bank A that it was OK to send $500 USD from Acct 1 @ Bank A to Acct 2 @ Bank B. That definitely wasn't me!
Switch: Wow, that's pretty strange. So you didn't request a transfer of $500 from Bank A to Bank B, then? Who are the customers involved?
PISP: Err..
Switch: Because I've got a request from you asking Bank A to transfer $100 from Ayesha's account at Bank A to Bhavesh's account at Bank B. The identifier is 8b72782a-1d81-4be5-a94a-64a99be49072. And I know it came from you, of course, because I checked the JWS signature with your public key. Do you recognise that identifier?

@mjbrichards
Copy link
Contributor

PISP: Well, might do. But then those aren't the eventual terms of the transfer, are they? And I didn't sign them...
Switch: Gosh, well let me check. I think I sent you the quote approval, didn't I?
PISP: Can't remember.
Switch: Well, you sent me back an acknowledgement saying you'd received it. And I'm assuming you wouldn't have been able to check the terms with Ayesha if you hadn't got them back?
PISP: Oh, yes, there they are. I mislaid them for a bit. What am I like?
Switch: OK, so you got the terms. Here they are so you can check we're on the same page. And I see the terms include that transfer request Id that you originally sent. And those terms included the condition, and were signed by the payee DFSP so you knew they were kosher, right?
PISP: Spose so.
Switch: So now, when the FIDO server sent you a challenge, that was the condition, wasn't it? Here it is, just so's you can remind yourself.
PISP: Well, I guess they look kind of like each other.
Switch: Well, you could check by looking at the quote response you already received. Was the condition you got from the FIDO server the same as the condition you got from the payee DFSP?
PISP: Sort of...
Switch: Right, so what did you show Ayesha when you asked her to approve the transfer?
PISP: Well, the terms of the transfer, of course.
Switch: Right, the ones you got back from the payee DFSP. And how did you find them? Was it by looking up the condition?
PISP: Might have been.
Switch: The ones with the condition attached, the same condition you used to find them and the same condition as the FIDO server asked you to sign?
PISP: Yeah, that one.
Switch: OK. So where is it you're saying that the discrepancy occurred?
PISP: Errr...
Switch: Because as far as I can see, the only way this can have broken down is if you showed Ayesha some different set of terms. Would you agree?
PISP: Anyone can make a mistake...

Am I misrepresenting the state of things?

@jgeewax
Copy link
Contributor

jgeewax commented Sep 29, 2020 via email

@lewisdaly
Copy link
Contributor

Hi All, thanks for this insightful conversation. I think I'm coming around to @jgeewax 's opinion - signing the actual transaction wherein anybody can do the maths and verify the transaction at a later date does make more sense.

I think the allure of using the condition is that we already have it. To generate a sha256(transaction details), we need to do some more specification work, and we also open up a the possibility that the PISP and DFSP mis-interprets or incorrectly implements these instructions.

Perhaps we can simply use a hash of the ilpPacket (see PUT /quotes/{id}), since that is a shared value that contains the transaction details.

@lewisdaly
Copy link
Contributor

lewisdaly commented Oct 2, 2020

@jgeewax Reading through Adrian's spec change proposal, it looks like v2 of the API might fix the issue where participants can't verify that the fulfilment is in fact related to the ilppacket without revealing the payee's secret.

this section contains more info about the proposed changes

@lewisdaly
Copy link
Contributor

@jgeewax and I talked about this yesterday.

The proposed implementation for generating a challenge will require the generation of txDigest, which is a SHA-256(canonicalJSON(Transaction))

Instead of waiting (or requiring) v2.0 of the FSPIOP-API for this, let's just use txDigest as our challenge.

@mjbrichards
Copy link
Contributor

It's probably worth adding that, when we presented the PISP API proposals at the CCB, the Open API for FSP Interoperability people pushed back on the idea of including the Transaction object in open text in the messages. Assuming that nothing moves terribly quickly in this area, an alternative which would allow us to move forward might be:

  • The challenge should be SHA-256(the data field of the ilpPacket object). This is an encoded version of the Transaction object, so shouldn't need to be canonicalised.
  • The content of the data field of the ilpPacket object can be decoded if required for verification.

This doesn't help us, however, with the Participants list. If we want to include the Participants list, and the Open API people don't see the need, then I don't see a realistic alternative to including payee DFSP messages in the PISP API, and hence to insisting that all DFSP participants in a scheme will need to support the PISP API if any of them do.

@adrianhopebailie
Copy link

the Open API for FSP Interoperability people pushed back on the idea of including the Transaction object in open text in the messages

what was the rationale?

@mjbrichards
Copy link
Contributor

Their view (as articulated by Henrik) was that it was a "nice-to-have" rather than "needed-needed". I had a talk with Matt and will be exploring ways round this; but I didn't want this to hold up PISP progress

@henrka
Copy link

henrka commented Oct 21, 2020

Hi @mjbrichards, I did not object to sending the transaction object in open? The requirement that was discussed was being able to link a quote from a transfer, which I said was already possible by reading the ILP Packet, why this specific request of being able link a quote from a transfer could already be done. That the transaction object will be sent in the open in the future has already been agreed in mojaloop/mojaloop-specification#13.

I would appreciate if I'm notified in the future if there is anything that we need to discuss in more detail, instead of just referring to me. It is not my purpose to hold up PISP progress, but I would also like to avoid making immediate changes in the FSPIOP API that are not really needed. It would be great if you could explain in more detail in mojaloop/mojaloop-specification#68 why these changes are needed.

Kind regards,
Henrik

@lewisdaly
Copy link
Contributor

Hi All,

Regarding this issue, I think we have a resolution for either FSPIOP API v1.1 or v2.0:

  • For v1.1, we can use a SHA-256(the data field of the ilpPacket object)
  • For v2.0 (if that is indeed an option), we can use either txDigest or txPrefix as described in Adrian's PR

@ehenrka apologies for not updating mojaloop/mojaloop-specification#68 earlier, but I assure you it's on my list of things to do. I want to make sure I have all my ducks in a row so I'm not wasting anyone's time going around in circles.

@henrka
Copy link

henrka commented Oct 22, 2020

Thanks for the update @lewisdaly!

I just want to make sure that you (not you personally, you as in PISP) to see the CCB as holding up your progress. For me, an important function of the CCB is to protect existing implementers from "unnecessary" changes as well as allowing new features that are well aligned with the existing API. As long as you can explain why something is needed, and that it cannot be done in a proper way in the existing API, and that it aligns well with the existing API, then I'm happy to approve changes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

8 participants