View Issue Details

IDProjectCategoryView StatusLast Update
0008703GNUnetreclaimpublic2024-06-08 12:03
Reporterthejackimonster Assigned Toschanzen  
PrioritynormalSeveritymajorReproducibilityalways
Status closedResolutionfixed 
Product VersionGit master 
Target Version0.21.2Fixed in Version0.21.2 
Summary0008703: Consuming a ticket does not rely on the subjects key
DescriptionWhen you issue a ticket, it shouldn't be possible by any party besides the relying subject to consume the ticket gaining access to its linked attributes. However it seems like if you issue a ticket on a host, every key is able to consume the ticket successfully.
Steps To Reproducegnunet-identity -C a
gnunet-identity -C b
gnunet-identity -C c

key_a=$(gnunet-identity -d -e a | awk '{print $3}')
key_b=$(gnunet-identity -d -e b | awk '{print $3}')
key_c=$(gnunet-identity -d -e c | awk '{print $3}')

gnunet-reclaim -e a -a aaa -V bbb
ticket=$(gnunet-reclaim -e a -i aaa -r $key_b)

gnunet-reclaim -e a -C $ticket
gnunet-reclaim -e b -C $ticket
gnunet-reclaim -e c -C $ticket
Additional InformationI've tried whether a ticket can also be consumed by any party on a different host. So I copied ego keys and namestore to a separate machine. But no key was able to consume the ticket instead, not even the one from original issuer or relying subject.
TagsNo tags attached.

Activities

schanzen

2024-04-04 08:33

administrator   ~0022101

Yes, I think we talked about this before. The Ticket is not itself protected from unauthorized access.
Meaning that if you accidently publish or disclose it to third parties, they will be able to use it.
In such a case, you have to revoke the ticket and issue a new one.

thejackimonster

2024-04-04 12:11

developer   ~0022106

But if that's intended, why does the function GNUNET_RECLAIM_ticket_consume() require a private identity key?

thejackimonster

2024-04-04 23:19

developer   ~0022119

I've implemented this patch now: https://git.gnunet.org/gnunet.git/commit/?h=dev/thejackimonster/reclaim-changes&id=087834e85fb20f98b6695f62cff2b5b65a32e542

With this the ticket can only be consumed by the relying subject. Looking at the paper, I also assumed such encryption and decryption via public and private key of that identity would be used internally. But maybe there's a reason for the current implementation, I don't see right now.

So I keep the patch on a separate branch first.

schanzen

2024-04-05 19:31

administrator   ~0022125

The reason why ticket_consume contains the private key is (probably) historical, but it made sense to keep it in oder to do some sanitiy checking (am I the RP).

The "problem" is that with your patch you are now encrypting twice, and I think if it is added it should be properly justified.
I initially thought you wanted to encrypt the ticket, which would make more sense (then you only have to encrypt one thing and not all attribute references).
I may be swayed to say yes to a ticket that is encrypted to a RP by default.
But it just is not obvious that this is what you want.
Encryption will cause a lot of overhead if you do Encrypt-then-Sign. And if you already have a secure channel (e.g. TLS) with your RP, it is completely unnecessary.
For example, reclaimID has an OIDC layer in which the user always has access to a secure channel (HTTPS) to the RP.

If your use case does not have such a secure channel, then yes, you may want to encrypt the ticket (or rather: establish a secure channel first).

schanzen

2024-04-05 19:51

administrator   ~0022128

To get on the right path I suggest the following: Formulate an attacker that would some abuse the fact that your proposed encryption is not in place.
Then we can talk about mitigations.
You example in the OP is incomplete: "However it seems like if you issue a ticket on a host, every key is able to consume the ticket successfully" <= how is the adversary on the same host (so another user I assume) gaining access to the ticket?

schanzen

2024-04-06 11:21

administrator   ~0022134

Here is another idea:
We could issue the ticket for a RP with key pair (a,B) by generating an ephemeral ECDH key pair (a,A) and publish the attribute references under label: = SHA(ECDH(a,B))
The transferred ticket then contains not the label (or rnd what I think it is called), but only A.
Only the RP the ticket was issued for will be able to calculate label := SHA(ECDH(b,A )) to retrieve the attributes.
Encryption is then still outsourced to GNS, but you have your RP binding.

thejackimonster

2024-04-07 20:56

developer   ~0022139

I think one issue I can think of is that the relying party might act malicious. So when A provides B a ticket to access some attributes from A. B can practically generate a pseudo-ticket for C which looks like it was created from A for C to access the same attributes.

Currently this would be done via swapping out the public key from B with the one from C in a ticket (from A to B). It looks like a ticket from A. It behaves like a ticket from A. But I think B should not be able to fake such tickets. It invalidates the signature from A to some degree. You can only verify that the attributes are set by A but you don't know whether A actually provided you access to them rather than anyone.

Even if we encrypt the label from the ticket, this issue stays, I think. But it's solved if the attribute references are encrypted so that only B can read them. Because B can't publish similar references which can be decrypted by C in the zone from A.

schanzen

2024-04-08 10:20

administrator   ~0022142

Last edited: 2024-04-08 10:48

If C is also malicous, then B can just forward the decrypted attribute references (or the attribute values) to C on demand.
If C is not malicous, and the attack is B impersonating A through a ticket, then this should always be mitigated by having the ticket origin authenticated.
E.g. Pubkey authentication / establishment of a secure channel between A and B/C.
Using the DH approach above would also prevent the ticket abuse. But so would a simple signature from A on the ticket.

So I think we may want to add the signature of the issuer to the ticket.
And we may want to replace the random secret in the ticket with an ephemeral DH key.

Still, I would not encrypt the attribute references. Under the DH and EUF assumptions of the primitives above, the GNS encryption itself should be sufficient.

thejackimonster

2024-04-08 12:23

developer   ~0022143

Okay, sounds reasonable.

schanzen

2024-04-08 14:57

administrator   ~0022149

After some more discussions I think this can be (while we are at it) improved more:

1. The ticket does not really need the AUDIENCE pubkey. Once the RP resolves the shared secret label in the IDENTITY zone, it should be able to resolve a record of type RECLAIM_RP_KEY that contains its pubkey. If it does not, it is not a ticket issued that was issued by IDENTITY for AUDIENCE. This removes the need for a signature on the ticket. In fact, it would allow us to define a ticket as a GNS name with format <TicketID>.<IDENTITY> which is quite nice.
2. The ticket ID (the secret label used to resolve the attribute references) may be derived from a DH as noted above. But I am not sure if that is always necessary. It imposes a (slight) burden on implementations that could otherwise solely rely on GNS. I need to think about this a bit.

I will update https://lsd.gnunet.org/lsd0002/ with input from this discussion.

schanzen

2024-04-30 11:35

administrator   ~0022320

Last edited: 2024-04-30 11:38

I have made some changes to the reclaim API in master:

1. The ticket is now simply a GNS name in the form <secret label>.<zTLD> where <zTLD> is the issuer public key Base32GNS encoded.
2. The audience is no longer part of the ticket.
3. When issuing a ticket, the issuer must provide a Relying Party URI (RP URI). This is a string. It is usually provided by the RP. For example: urn:gns:<Base32(identity)>
4. When provided with a Ticket, the RP resolves the contained GNS name and MUST verify that the RP URI matches what it expects to be. The RP URI is found in a URI record. The ticket_consume API requires the caller to provide the expected RP URI and takes care of this check.

In your attack, B can no longer forge a ticket by replacing the audience key as it no longer exists. Also, the RP can now verify that the ticket was actually issued to it by providing a suitable RP URI. So the attack is thwarted.
Transfer of the ticket must still be done securely. But this is "out of band" of the reclaimID authorization flow.

Note that if you need to extract the RP identity for some reason when handling the ticket, you need to provide a RP URI that will allow you to extract this info again. The ticket_consume API will NOT give you the retrieved RP URI as you have to provide it anyway.

I have tried to modify the messenger API to accomodate for the changes, but I guess both messenger and the OpenID plugins are currently not properly functioning with the new API.

thejackimonster

2024-05-02 22:52

developer   ~0022330

Sounds like a good solution. Since the issuer and the relying party both may know the RP URI, they can both consume the ticket, I assume. So that means I probably don't need to adjust libgnunetchat because of these changes.

Otherwise I think I will need to adjust the ticket message struct from the Messenger service. So that it either carries the ticket as well as the RP URI or it's possible to derive the RP URI from the public key of the RP. So both (recipient and sender) can consume the ticket. I also like that the ticket is now only a GNS name. That means I can probably use a variable char array in the ticket message body. That allows dropping the include of the reclaim header in the messenger service header. Since all core functionality regarding tickets for attribute exchange in the chats is in libgnunetchat anyway.

schanzen

2024-05-03 19:10

administrator   ~0022339

Resolving and scheduling for next release.

schanzen

2024-06-08 12:03

administrator   ~0022559

0.21.2 released

Issue History

Date Modified Username Field Change
2024-04-04 02:59 thejackimonster New Issue
2024-04-04 08:33 schanzen Note Added: 0022101
2024-04-04 12:11 thejackimonster Note Added: 0022106
2024-04-04 23:19 thejackimonster Note Added: 0022119
2024-04-05 19:31 schanzen Note Added: 0022125
2024-04-05 19:51 schanzen Note Added: 0022128
2024-04-06 11:21 schanzen Note Added: 0022134
2024-04-07 20:56 thejackimonster Note Added: 0022139
2024-04-08 10:20 schanzen Note Added: 0022142
2024-04-08 10:48 schanzen Note Edited: 0022142
2024-04-08 12:23 thejackimonster Note Added: 0022143
2024-04-08 14:57 schanzen Note Added: 0022149
2024-04-30 11:35 schanzen Note Added: 0022320
2024-04-30 11:35 schanzen Assigned To => schanzen
2024-04-30 11:35 schanzen Status new => assigned
2024-04-30 11:35 schanzen Note Edited: 0022320
2024-04-30 11:37 schanzen Note Edited: 0022320
2024-04-30 11:38 schanzen Note Edited: 0022320
2024-05-02 22:52 thejackimonster Note Added: 0022330
2024-05-03 19:10 schanzen Status assigned => resolved
2024-05-03 19:10 schanzen Resolution open => fixed
2024-05-03 19:10 schanzen Fixed in Version => 0.21.2
2024-05-03 19:10 schanzen Note Added: 0022339
2024-05-03 19:10 schanzen Target Version => 0.21.2
2024-06-08 12:03 schanzen Note Added: 0022559
2024-06-08 12:03 schanzen Status resolved => closed