Closed TelegramSam closed 2 years ago
To clarify what is in #185 :
skid
(in the protected header -> visible for all recipients): contains a reference to sender's public key as a DID URI. encrypted_skid
(in recipient header -> visible for all recipients but only the legitimate recipient will be able to decrypt): contains an encrypted reference to the sender's public key as a DID URI.The PR says both are OPTIONAL but either MUST be used as far as I understood.
Generic JWE/ECDH-1PU implementations considerations:
skid
and the unwrapped encrypted_skid
as being proposed refer to a blob of data that identifies either by reference or by value the sender's static key for the recipient(s) to decrypt the JWE.skid
or encrypted_skid
is a public key by value or by reference. In that case, the sender's public key will need to be provided to the JWE/ECDH-1PU unpack regardless. skid
and encrypted_skid
but instead use something like spk
or encrypted_spk
and make that part of the ECDH-1PU spec to allow JWE implementations (that have no idea what a DID is) to be used for DIDComm authcrypt without special handling. encrypted_spk
should then use a similar format to encrypted_key
which is the raw bytes and not the JWK. In that case you won't have the bloat of the JWK and only the bloat from base64url encoding it. For privacy reasons, it is probably not a good idea to use skid
at all. We should instead use the encrypted_skid
(respectively spk
or encrypted_spk
). I don't know if there are use cases where the plain skid
makes sense. If there are use cases, I can live with that as long as we say that encrypted_skid
is recommended.
Size considerations:
encrypted_skid
(encrypted_spk
) contains the JWK, then the JWE could become large. If it contains the raw bytes, it would still be considerable if it needs to be encrypted for each recipient.EIP2844 considerations:
Updated:
Even with the encrypted_spk
, we would need to provide a link to the public key in the DID Document to verify that the public key belongs to the from
DIDComm property. There is also the question, what encoding (e.g., JWK etc.) should be used for the encrypted_spk
? IMO, it must be the encoding that is also used in the DID Document. Otherwise, you'd end up with converting public key encodings between each other but in order to allow generic JWE/ECDH-1PU implementations to support DIDComm authcrypt we will probably have to agree on one encoding (e.g., JWK because JWE is JOSE). The conversion of public key encodings makes me a bit nervous.
do you expect DID resolution of the DID URI to the sender's key need to happen in the EIP2844 implementation or outside?
Do you mean when decrypting? I would prefer if it happens outside for decryption since it would not be great if wallets needed to support resolving random DID methods. However, this seems less than ideal from a security perspective (a malicious app could send you the wrong public key for the skid
. Not sure what implications this would have however.
@TelegramSam I think I got convinced that including the public key material is a good idea but I would argue we are not using skid
and encrypted_skid
for that but instead use names that tell that the actual public key is included and not a reference, e.g., spk
/sender_key
and encrypted_spk
/encrypted_sender_key
.
Mixing up the layering does not make sense for generic ECDH-1PU/JWE implementations. It also makes less sense for the wallet interface that is proposed under EIP2844.
contains the JWK, then the JWE could become large. If it contains the raw bytes
I think more than raw bytes would be needed as the key type/curve need to be identified. (like in the DID document VerificationMethod itself.)
I think more than raw bytes would be needed as the key type/curve need to be identified. (like in the DID document VerificationMethod itself.)
It would really make a lot of sense to use multicodec for this! I.e. the thing that did:key
uses to identify a key with a single varint. See DID Key spec
Even with the encrypted_spk, we would need to provide a link to the public key in the DID Document to verify that the public key belongs to the from DIDComm property.
The from
DIDComm property MUST have a clear link with the sender key used in the JWE. Is there already text in the spec defining this link? (there should be).
I believe there were three scenarios mentioned where the key value is useful.
(1) The key value in the DID Document was changed for the DID URI that was used (and versions aren't globally supported).
(2) The DID method might not be supported causing a DID resolution failure.
(3) Some peer
-like DID methods need bootstrapping that isn't within the DID URI.
I'd argue that the first two scenarios can still cause a failure, but at a different layer. As we are using 1PU for sender authentication, the receiver MUST be able to link the from
property to the sender key being used. This means that the DID URI needs to be dereferenced and that relationship between from
and key verified. i.e., when this can't happen, we have a failure at another layer. With the failure being an authentication issue, do we expect the receiver to send a problem report?
With the third situation, it would be nice if that bootstrapping could have happened in the DID URI (or out-of-band). I think this topic becomes a discussion on how to attach this information (similar to the previous - can't resolve so how do we attach (a subset of) that resolution result with the expectation that it still needs to be validated against the from
DID URI at another layer).
In any case, I think we should retain a skid definition as a defined input into 1PU. I think it makes sense for skid
/encrypted_skid
to be able to resolve the key value (a DID URI). For the case where we effectively want to attach the contents of the resolved VerificationMethod, we should talk about the best way to do that (when needed). I don't think that discussion changes what skid
/encrypted_skid
is though.
contains the JWK, then the JWE could become large. If it contains the raw bytes
I think more than raw bytes would be needed as the key type/curve need to be identified. (like in the DID document VerificationMethod itself.)
Yep, that is right but with additional context, i.e., type, curve of epk
, you would probably have everything you needed. But I'm not arguing for it. Also think more data is better than guessing :)
How about this as a proposal?
kid
s and skid
s MUST be a DID key reference (ex: did:example:1234567890#keyid)from
and to
attributes in the inner message are optional. If missing, their value is equal to the DID portion of the corresponding skid
/kid
in the encryption layer.from_prior
value remains the same, though if from
is not present it defaults to the skid
DID portion (without fragment) as described above.Both from and to attributes in the inner message are optional. If missing, their value is equal to the DID portion of the corresponding skid/kid in the encryption layer.
In an attempt to prevent key re-use between the encryption layer and the inner message, I suggest removing this statement. Keys for the encryption layer should be sourced from the KeyAgreement DID method ID as kid while the inner message can use the main verification method ID.
If you need to send a message to a recipient that will not be able to resolve your DID, a locally resolvable DID method (ex: peer did) MUST be used.
Or send anonymously, right? :)
- Keys MUST NOT be passed by value in the encryption layer. If you need to send a message to a recipient that will not be able to resolve your DID, a locally resolvable DID method (ex: peer did) MUST be used.
Two questions:
skid
which is needed for privacy reasons?Keys for the encryption layer should be sourced from the KeyAgreement DID method ID as kid while the inner message can use the main verification method ID.
The kids in the encryption layer refer to keys, while the from and to attributes inside the message refer to DIDs, who's resolved DID Documents would contain both verification and encryption keys. If you agree with this, than I don't think I need to remove that statement at all. Do I understand your argument correctly?
Or send anonymously, right? :)
Yes, but without reinvention of message authentication.
Two questions:
- Does this mean that providing the sender public key in the JWE is out of scope of our spec?
It does mean that the sender public key must be resolved, not contained directly. This may be resolved via a DID method that can be resolved locally (did:peer), but does not support inline keys directly.
- How does this address encryption of
skid
which is needed for privacy reasons?
We still need to encrypt the skid
to the recipient. (this proposal does not change that need.)
Two questions:
- Does this mean that providing the sender public key in the JWE is out of scope of our spec?
It does mean that the sender public key must be resolved, not contained directly. This may be resolved via a DID method that can be resolved locally (did:peer), but does not support inline keys directly.
- How does this address encryption of
skid
which is needed for privacy reasons?We still need to encrypt the
skid
to the recipient. (this proposal does not change that need.)
If we assume that skid
is encrypted (to recipient) and DID Resolution needs to happen, then what would be the API of a ECDH-1PU decryption lib? I'm concerned that some architectures will need a two-step process where in the first step the JWE needs to be partially decrypted, before the rest of the JWE could be actually decrypted.
The following is a simplified version but it kind of feels weird to have those separate steps. Certainly, you could provide a key resolver instance to the decryptJWE
directly (or similar). But in some architectures this might not be possible, e.g., when DID resolution cannot be done by the application that manages the keys (and decrypts the JWE).
const jwe = getNextDidCommMessage(...)
// this will be actually decrypted in another application or context (e.g., remote, different app etc)
const senderDidUri = decryptSkid(jwe, ...)
// tbd: check sender verification method is authorized
const senderPublicKey = resolver.dereferenceUrl(senderDidUri)
// this will be actually decrypted in another application or context (e.g., remote, different app etc)
const message = decryptJWE(jwe, senderPublicKey, ...)
// tbd: check recipient verification method is authorized
This is why I actually think providing the public key by value might makes sense, not only for local DID methods.
@awoie You highlight the difference between the value and ID approaches. I can be happy with either approach, but we should pick one. I'm not a fan of allowing both ID and value approaches. I'm sure that discussion will be plentiful in our meeting today.
Would skid
be likely a JWE (JWT)? E.g., Direct Mode (no key wrapping).
when DID resolution cannot be done by the application that manages the keys (and decrypts the JWE).
DID resolution must occur prior to processing the message.
I can be happy with either approach, but we should pick one. I'm not a fan of allowing both ID and value approaches.
I'm not a fan of the key value approach since the key must be resolvable by ID for message processing anyways.
Keys for the encryption layer should be sourced from the KeyAgreement DID method ID as kid while the inner message can use the main verification method ID.
The kids in the encryption layer refer to keys, while the from and to attributes inside the message refer to DIDs, who's resolved DID Documents would contain both verification and encryption keys. If you agree with this, than I don't think I need to remove that statement at all. Do I understand your argument correctly?
Then it's ok, we're on the same page.
👀
Since the did has to specify a particular key, why don't we just place an additional restraint on the didUrl contained within the key to also pass the key by value?
E.g. SKID MUST contain the entire publicKey multicodec encoded that is usedby the sender as a URL parameter. did:example:123#multicodec(publicKey)
or did:example:123?key=multicodec(publicKey)&version=3
This is something that @dhh1128 was suggesting should have been done anyways within did-core. Why don't we just use that and then the key can be passed by reference and value at the same time allowing for an implementation to implement this in a variety of different ways.
@kdenhartog I am against DIDComm attempting to profile how a DID method or DID controller sets their key identifiers.
Also, some DID methods have their own restrictions on the identifier. Sidetree, for example, restricts the length of key identifiers to 50 Base64URL encoded characters.
What about MUST contain the DID URI but MAY also include the sender public key (e.g., as multicodec) in addition?
We would need to agree on some separator between those two fields in the skid
. Or we just introduce a new header for the key and register with IANA, e.g., sender_pk
. It could be a MUST skid
but MAY contain sender_pk
in addition.
This would be nice since it would allow implementations that only rely on skid
to ignore sender_pk
but for implementations that actually use an architecture that decouples DID resolution from KMS, they would try to find sender_pk
first to do the decryption.
Update: This approach would at least help those implementers that optimize for those architectures to have a normative reference they can refer to.
@kdenhartog I am against DIDComm attempting to profile how a DID method or DID controller sets their key identifiers.
Also, some DID methods have their own restrictions on the identifier. Sidetree, for example, restricts the length of key identifiers to 50 Base64URL encoded characters.
That's alright, the usage of the key url parameter could be used to perform a lookup based upon the publicKey property rather than based on matching of the id property. The purpose in that url parameter was not to assume that the verification method id will always be equal to the key.
For example did:example:123?key=multicodec(publicKey)#key1
could be used as well to address this concern. Then when dereferencing occurs the query parameter gets dereferenced first based upon the publicKey*
parameter and then validated that the fragment matches the dereferenced verification method. If it doesn't then de-referencing fails.
@troyronda @kdenhartog what would be the issue with the approach I described above?
Some JWE profiles even overload encrypted_key
with multiple properties, e.g., nonce and value. That would be another option.
This would be nice since it would allow implementations that only rely on
skid
to ignoresender_pk
but for implementations that actually use an architecture that decouples DID resolution from KMS, they would try to findsender_pk
first to do the decryption.
Only when receiving a message could implementations ignore sender_pk
. Everyone would have to include the sender_pk
in addition to the skid when sending.
Questions I have:
sender_pk
is necessary, can we be smart about when it's required? Maybe send initially, and stop if you confirm the other side doesn't need it. Can we use an error back to sender to indicate the need for the sender_pk
if a message is received without it?@troyronda @kdenhartog what would be the issue with the approach I described above?
Some JWE profiles even overload
encrypted_key
with multiple properties, e.g., nonce and value. That would be another option.
I'd be alright with this as described.
@TelegramSam here's my take based on conversations with @awoie and @oed this week.
This would be nice since it would allow implementations that only rely on
skid
to ignoresender_pk
but for implementations that actually use an architecture that decouples DID resolution from KMS, they would try to findsender_pk
first to do the decryption.Only when receiving a message could implementations ignore
sender_pk
. Everyone would have to include thesender_pk
in addition to the skid when sending.
I think ignoring the sender_pk
could be done when sending as well. It would just require 2 RPC calls to be done. The reason this is a problem is because it also leads to the potential of 2 consent requests in the wallet UI to receive a message.
One alternative is to build the resolver on the RPC server side, but this leads to a misalignment of decisions around trust at the DID layer because the wallet implementors (people who write the RPC server) decide which DID methods for all dApps rather than allowing the dApps to individually decide which DID methods are trusted.
Additionally when the RPC server decides which DID methods to support, it prevents the ability of extending the decision of trust in DID methods to the user which is advantageous to self-sovereignty and decentralization. This is why the resolution shouldn't be done inside the wallet layer to reduce to a single RPC call.
In summation, if necessary skid
only can be supported by Ethereum wallets but there's definitive UX hurdles that are created because of it. Hence, @awoie initial hesitation in settling for the currently solution.
Questions I have:
* What is the effect of not supporting keys by value on the Ethereum community? Is it a pain or an absolute show stopper?
It's not an absolute show stopper for all Ethereum wallet implementations, but it does have effects that could require two consent requests to receive a message for some Ethereum wallets are implemented today. This is because some wallet implementations require consent be gather at the UI layer before allowing access to the key. This in my eyes is borderline absolute show stopper.
* If including the `sender_pk` is necessary, can we be smart about when it's required? Maybe send initially, and stop if you confirm the other side doesn't need it. Can we use an error back to sender to indicate the need for the `sender_pk` if a message is received without it?
It's not necessary, but is an advantage for both dev UX and user UX in some implementations. Hence, @awoie being alright with MAY
language. It allows them to build better UX when interacting within the Ethereum dApp ecosystem who want to optimize, but doesn't prevent broad interop still.
* If this is really an effort to make it easier for a potentially large community to participate, who among us is willing to take this extra step?
I think this is why @awoie option may be better than mine, it doesn't require participation to get interop, but it does help. Mine on the other hand requires implementation (while also not requiring key looping).
@kdenhartog Thanks for the great summary.
Having no option to include the key by value will be a show stopper because the UX would be not great. However, for those cases where an agent does not send the key by value and only by reference, we would be still interoperable. For "ETH-agent" to "ETH-agent" I assume that we would always send both, skid and key by value to have better UX. How the key is actually encoded (new field, inside existing field + separator) is flexible.
WG 20210524 - Consensus that an optional (for both sender and receiver) field will work. Kyle to work on PR.
I saw this on my TODO list but couldn't remember the deeper context for it. Found it and assigned it to myself.
I believe the safest more general kid/skid values we can use are did:key
representations as per Aries RFC 360
did:key
does not seem conducent to verifying the identity of the sender. I believe would mean performing some version of key equality testing between what is in the sender's DID document (possibly a JWK) and the multibase version of the key, and this would need to be formalized for the supported key types. If the skid
is simply a reference to a key in the document then this would not be required.
Yeah isn't using a did:key effectively like a different identifier? Are you planning to use alsoKnownAs or something with that method @Baha-sk? I was under the impression we'd put the did-url that matches the key inside the kid property and that needs to be matched to the id property in the did document.
did:key does not seem conducent to verifying the identity of the sender.
although we aim to identify the sender in the end, the skid
's purpose is for identifying the key itself. The nice thing about did:key is it's self identifying and the public key can be extracted from it directly. With the did:key, we don't need an additional step for key equality testing.
The other nice thing is if the key changes, did:key will change accordingly. So no need to check for versions, or translate to JWKs. The JWE logic doesn't need to be aware of DID specific relationship with the keys, it will only deal with string values in skid
/kid
.
Yeah isn't using a did:key effectively like a different identifier? Are you planning to use alsoKnownAs or something with that method @Baha-sk? I was under the impression we'd put the did-url that matches the key inside the kid property and that needs to be matched to the id property in the did document.
Originally, I had the same thought @kdenhartog, which is using the did-url key identifier in KeyAgreement's ID.
I was thinking to simply use the same values as-is in the jwe envelope to reduce identifier translation between the envelope and the DID doc. But since KeyAgreement's IDs can have relative did-url values starting with #
, having a skid/kid value looking like #keys-1
doesn't tell much about the key ID, it will require an additional header for the DID reference.
While if we use did:key
representation in skid
/kid
header, an app can easily compare its value against the service bloc and also extract the public key directly.
Hmm, I think I'm lost and it seems similar to @andrewwhitehead question as well. So if I trust did:example:123
and the associated keys, how am I able to know that the message was sent by did:example:123
if the skid is of did:key
?
Originally, I had the same thought @kdenhartog, which is using the did-url key identifier in KeyAgreement's ID. I was thinking to simply use the same values as-is in the jwe envelope to reduce identifier translation between the envelope and the DID doc. But since KeyAgreement's IDs can have relative did-url values starting with
#
, having a skid/kid value looking like#keys-1
doesn't tell much about the key ID, it will require an additional header for the DID reference.
I don't understand why we'd need to pass #referenceFragment
. Wouldn't we instead pass did:example:123#referenceFragment
and then a dereferencer would perform the mapping between did:example:123#referenceFragment
and #referenceFragment
which is contained in the did document of did:example:123
.
how am I able to know that the message was sent by did:example:123 if the skid is of did:key?
+1 to this question. It feels important to me.
+1, DID URL approach, i.e., skid=did:example:123#key-1
-1, did:key approach, i.e., skid=did:key:z6MkpTHR8VNsBxYAAWHut2Geadd9jSwuBV8xRoAnwWsdvktH
- In the worst case, i'd need to convert each public key material in the sender's DID Doc and compare against the did:key. In the DID URL approach, I'd only need to convert once (max). It also feels weird to have different identifiers for skid
and from
(if provided).
@kdenhartog, @dhh1128, @awoie
first: the didComm service bloc in the DID doc uses did:key for recipientKeys/senderKey. At the encryption envelope layer, we should just follow suit for packing the JWE envelopes (this Aries RFC explains the reasons behind it, cc @swcurran).
second: requiring dereferencing keys to the DID doc means the encryption layer needs to be aware of the DID doc. Using did:key makes it easier to work with keys independently. DIDComm frameworks should be able to provide did:key -> raw key translations anyway. You have a valid concern about how to validate ownership of the key in the DID doc, but i believe this should be done at a higher level that handles the payload content. ie: now my envelope was successfully decrypted, does my payload correspond to data in myDID/theirDID/oob invitation/DID connection. In fact oob invitations don't have DIDs yet, so linking to a DID makes DIDComm unworkable for these types to requests.
So to make things simple, encrypt/decrypt using did:key and once decrypted, a higher level layer can verify the did:keys in the kid field exists in the DIDcomm service bloc of the DID doc/connection or are part of the oob request in case of no DIDs exchanged yet.
third: this is for @awoie about skid and kid formats. We should have the same format for both headers. What is really weird is having the encryption layer meddling with the DID doc ID if it's a fragment (eg #key-1), then append the DID url: (did:peer:abc#key-1). To avoid this confusion and versioning of keys issues, using did:key guarantees ID consistency across the board.
The question I have for you is: do the encryption layer really require dereferencing skid/kid and validating these values with DID doc VM IDs? or should the higher layer handle this verification? And what to do for requests that don't have a DID connection yet (like oob invitations)?
IMHO, the encryption layer should just handle encryption/decryption using provided keys and kid/skid values with minimum translation possible from the rest of the DID docs and connections involved. Once the envelope is decrypted, the payload receiver at higher level can do additional checks as needed. I have been trying to find a proper solution to this problem for the last couple of weeks and using did:key was the one that makes most sense as i was implementing this change in AFGO.
If you think my suggestion is offtrack, please let me know. I intend to update the DIDcomm docs with using did:key values in kid/skid and we can probably discuss this issue during next Monday's community meeting as it's urgent for completion of V2 interop.
@Baha-sk I appreciate the convenience of the encryption layer being able to unpack the message without resolving a DID document first, and I think that's worth exploring more, as it could be good for performance and the separation of layers if it can be implemented without sacrificing security or introducing too much complication in the next layers. However..
So to make things simple, encrypt/decrypt using did:key and once decrypted, a higher level layer can verify the did:keys in the kid field exists in the DIDcomm service bloc of the DID doc/connection or are part of the oob request in case of no DIDs exchanged yet.
This does not seem like an acceptable form of authentication, because it's ignoring the authentication mechanisms that the DID document already provides.
I also personally don't like the dependence on the did:key standard. Although it's convenient in that it's already specified, if the public key is to be encoded inline then that format should be specified by DIDComm itself or a closely adjacent specification.
first: the didComm service bloc in the DID doc uses did:key for recipientKeys/senderKey
That's the DIDComm v1 service block. We are not bound by it.
I also personally don't like the dependence on the did:key standard. Although it's convenient in that it's already specified, if the public key is to be encoded inline then that format should be specified by DIDComm itself or a closely adjacent specification.
+100.
@andrewwhitehead @dhh1128 how can we handle oob invitations then where DIDs are still not exchanged? ie there's no DID yet to dereference the kid/skid in this case.
first: the didComm service bloc in the DID doc uses did:key for recipientKeys/senderKey
That's the DIDComm v1 service block. We are not bound by it.
@dhh1128 can you point me to where this is mentioned in the docs ?
@Baha-sk please find some comments below ...
@kdenhartog, @dhh1128, @awoie
first: the didComm service bloc in the DID doc uses did:key for recipientKeys/senderKey. At the encryption envelope layer, we should just follow suit for packing the JWE envelopes (this Aries RFC explains the reasons behind it, cc @swcurran).
Please find the DIDComm Messaging (v2) service block here: https://identity.foundation/didcomm-messaging/spec/#did-document-service-endpoint.
second: requiring dereferencing keys to the DID doc means the encryption layer needs to be aware of the DID doc. Using did:key makes it easier to work with keys independently. DIDComm frameworks should be able to provide did:key -> raw key translations anyway. You have a valid concern about how to validate ownership of the key in the DID doc, but i believe this should be done at a higher level that handles the payload content. ie: now my envelope was successfully decrypted, does my payload correspond to data in myDID/theirDID/oob invitation/DID connection. In fact oob invitations don't have DIDs yet, so linking to a DID makes DIDComm unworkable for these types to requests.
OOB messages are plaintext and can (should?) have a from
property (a DID). See example in the current DIF DIDComm Messaging spec. Unfortunately, the OOB section has two examples. The first one seems to be broken and we should fix that.
So to make things simple, encrypt/decrypt using did:key and once decrypted, a higher level layer can verify the did:keys in the kid field exists in the DIDcomm service bloc of the DID doc/connection or are part of the oob request in case of no DIDs exchanged yet.
I also think that the higher layer should verify that and not the encryption layer. I'd assume that the higher layer provides the resolved/dereferenced keys to the encryption layer. I just don't see the benefit of using a DID method (did:key) that is potentially different from the from
/to
DID method.
third: this is for @awoie about skid and kid formats. We should have the same format for both headers. What is really weird is having the encryption layer meddling with the DID doc ID if it's a fragment (eg #key-1), then append the DID url: (did:peer:abc#key-1). To avoid this confusion and versioning of keys issues, using did:key guarantees ID consistency across the board.
As mentioned above, I agree and I don't think that dereferencing the DID URL should be done by the encryption layer. But I don't see the need to use did:key:alicekey
instead of did:example:alice#key
when did:example:alice
is used for from
. It just makes the key auth process harder (i.e., checking whether key is authorized by DID).
Further, if did:key was used for kid
/skid
, and you translate those in the encryption layer, it means, you'd also need DID resolution on that layer anyways.
The question I have for you is: do the encryption layer really require dereferencing skid/kid and validating these values with DID doc VM IDs? or should the higher layer handle this verification? And what to do for requests that don't have a DID connection yet (like oob invitations)?
IMO, the encryption layer does not care about kid
and skid
at all. You'd need to find the keys one layer above (based on kid
/skid
) and provide those to the encryption layer.
Thank you @awoie for the detailed response. Indeed, we have some work to do to remove RecipientKeys
from the service bloc in this case to be DIDComm V2 compatible.
DID url it is then.. i need to double check what needs to be done for oob invitations though.
@TelegramSam I guess we can close this issue?
Closing this issue since discussion happens in #219
PR #185 Proposes that Sender Key IDs (SKID) should be a reference.
Historically, we've argued for passing the SKID by Value.
Please articulate your position on this issue, that we can all understand the various opinions and points for this decision.