Skip to content

Commit

Permalink
Hash Rfq privateData (#222)
Browse files Browse the repository at this point in the history
* Hash Rfq privateData

* Remove config

* Submodule to latest main

* fixing compile errors and explicitly opting into balances, also updating the tbdex submodule

* DRY digest and remove padding = false

* Add to ktdocs for RfqData

* Add ktdocs for PrivatePaymentDetails

* require()

* question mark

* Encode salt as base64Url

* Unhashed -> Create

* import order

* Update protocol/src/main/kotlin/tbdex/sdk/protocol/models/Rfq.kt

Co-authored-by: Jiyoon Koo <jiyoon@squareup.com>

* Update protocol/src/main/kotlin/tbdex/sdk/protocol/models/Rfq.kt

Co-authored-by: Jiyoon Koo <jiyoon@squareup.com>

---------

Co-authored-by: Jiyoon Koo <jiyoon@squareup.com>
  • Loading branch information
diehuxx and jiyoontbd committed Apr 1, 2024
1 parent bb32348 commit 85598e1
Show file tree
Hide file tree
Showing 13 changed files with 526 additions and 79 deletions.
12 changes: 6 additions & 6 deletions httpclient/src/test/kotlin/tbdex/sdk/httpclient/E2ETest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@ import org.junit.jupiter.api.Disabled
import tbdex.sdk.httpclient.models.GetExchangesFilter
import tbdex.sdk.httpclient.models.GetOfferingsFilter
import tbdex.sdk.httpclient.models.TbdexResponseException
import tbdex.sdk.protocol.models.CreateRfqData
import tbdex.sdk.protocol.models.CreateSelectedPayinMethod
import tbdex.sdk.protocol.models.CreateSelectedPayoutMethod
import tbdex.sdk.protocol.models.Message
import tbdex.sdk.protocol.models.Order
import tbdex.sdk.protocol.models.OrderStatus
import tbdex.sdk.protocol.models.Quote
import tbdex.sdk.protocol.models.Rfq
import tbdex.sdk.protocol.models.RfqData
import tbdex.sdk.protocol.models.SelectedPayinMethod
import tbdex.sdk.protocol.models.SelectedPayoutMethod
import web5.sdk.credentials.VerifiableCredential
import web5.sdk.crypto.InMemoryKeyManager
import web5.sdk.crypto.JwaCurve
Expand Down Expand Up @@ -254,14 +254,14 @@ class E2ETest {
private fun buildRfqData(
firstOfferingId: String,
vcJwt: String
) = RfqData(
) = CreateRfqData(
offeringId = firstOfferingId,
payin = SelectedPayinMethod(
payin = CreateSelectedPayinMethod(
kind = "NGN_ADDRESS",
paymentDetails = mapOf("walletAddress" to "ngn-wallet-address"),
amount = "1.00"
),
payout = SelectedPayoutMethod(
payout = CreateSelectedPayoutMethod(
kind = "BANK_Access Bank",
paymentDetails = mapOf(
"accountNumber" to "1234567890",
Expand Down
12 changes: 6 additions & 6 deletions httpclient/src/test/kotlin/tbdex/sdk/httpclient/TestData.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ import com.danubetech.verifiablecredentials.CredentialSubject
import de.fxlae.typeid.TypeId
import tbdex.sdk.protocol.models.Balance
import tbdex.sdk.protocol.models.BalanceData
import tbdex.sdk.protocol.models.CreateRfqData
import tbdex.sdk.protocol.models.CreateSelectedPayinMethod
import tbdex.sdk.protocol.models.CreateSelectedPayoutMethod
import tbdex.sdk.protocol.models.MessageKind
import tbdex.sdk.protocol.models.Offering
import tbdex.sdk.protocol.models.OfferingData
Expand All @@ -14,9 +17,6 @@ import tbdex.sdk.protocol.models.QuoteData
import tbdex.sdk.protocol.models.QuoteDetails
import tbdex.sdk.protocol.models.ResourceKind
import tbdex.sdk.protocol.models.Rfq
import tbdex.sdk.protocol.models.RfqData
import tbdex.sdk.protocol.models.SelectedPayinMethod
import tbdex.sdk.protocol.models.SelectedPayoutMethod
import web5.sdk.credentials.VcDataModel
import web5.sdk.credentials.VerifiableCredential
import web5.sdk.credentials.model.ConstraintsV2
Expand Down Expand Up @@ -94,10 +94,10 @@ object TestData {
val rfq = Rfq.create(
to = to,
from = ALICE_DID.uri,
rfqData = RfqData(
rfqData = CreateRfqData(
offeringId = offeringId,
payin = SelectedPayinMethod("BTC_ADDRESS", mapOf("address" to 123456), amount = "10.00"),
payout = SelectedPayoutMethod("MOMO", mapOf("phone_number" to 123456)),
payin = CreateSelectedPayinMethod("BTC_ADDRESS", mapOf("address" to 123456), amount = "10.00"),
payout = CreateSelectedPayoutMethod("MOMO", mapOf("phone_number" to 123456)),
claims = claims
)
)
Expand Down
12 changes: 6 additions & 6 deletions httpserver/src/test/kotlin/TestData.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
import de.fxlae.typeid.TypeId
import tbdex.sdk.protocol.models.Close
import tbdex.sdk.protocol.models.CloseData
import tbdex.sdk.protocol.models.CreateRfqData
import tbdex.sdk.protocol.models.CreateSelectedPayinMethod
import tbdex.sdk.protocol.models.CreateSelectedPayoutMethod
import tbdex.sdk.protocol.models.MessageKind
import tbdex.sdk.protocol.models.Offering
import tbdex.sdk.protocol.models.Order
Expand All @@ -10,9 +13,6 @@ import tbdex.sdk.protocol.models.Quote
import tbdex.sdk.protocol.models.QuoteData
import tbdex.sdk.protocol.models.QuoteDetails
import tbdex.sdk.protocol.models.Rfq
import tbdex.sdk.protocol.models.RfqData
import tbdex.sdk.protocol.models.SelectedPayinMethod
import tbdex.sdk.protocol.models.SelectedPayoutMethod
import web5.sdk.crypto.InMemoryKeyManager
import web5.sdk.dids.methods.dht.DidDht
import java.time.OffsetDateTime
Expand All @@ -26,14 +26,14 @@ object TestData {
return Rfq.create(
to = pfiDid.uri,
from = aliceDid.uri,
rfqData = RfqData(
rfqData = CreateRfqData(
offeringId = offering?.metadata?.id ?: TypeId.generate("offering").toString(),
payin = SelectedPayinMethod(
payin = CreateSelectedPayinMethod(
kind = offering?.data?.payin?.methods?.first()?.kind ?: "DEBIT_CARD",
paymentDetails = mapOf("foo" to "bar"),
amount = "1.00"
),
payout = SelectedPayoutMethod(
payout = CreateSelectedPayoutMethod(
kind = offering?.data?.payout?.methods?.first()?.kind ?: "BTC_ADDRESS",
paymentDetails = mapOf("foo" to "bar")
),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ import tbdex.sdk.httpclient.models.ErrorResponse
import tbdex.sdk.httpserver.models.CreateExchangeCallback
import tbdex.sdk.httpserver.models.ExchangesApi
import tbdex.sdk.httpserver.models.OfferingsApi
import tbdex.sdk.protocol.models.CreateRfqData
import tbdex.sdk.protocol.models.CreateSelectedPayinMethod
import tbdex.sdk.protocol.models.CreateSelectedPayoutMethod
import tbdex.sdk.protocol.models.Message
import tbdex.sdk.protocol.models.MessageKind
import tbdex.sdk.protocol.models.Offering
Expand All @@ -34,9 +37,6 @@ import tbdex.sdk.protocol.models.PayinMethod
import tbdex.sdk.protocol.models.PayoutDetails
import tbdex.sdk.protocol.models.PayoutMethod
import tbdex.sdk.protocol.models.Rfq
import tbdex.sdk.protocol.models.RfqData
import tbdex.sdk.protocol.models.SelectedPayinMethod
import tbdex.sdk.protocol.models.SelectedPayoutMethod
import tbdex.sdk.protocol.serialization.Json
import kotlin.test.Ignore
import kotlin.test.assertContains
Expand Down Expand Up @@ -132,14 +132,14 @@ class CreateExchangeTest {
private val rfq: Rfq = Rfq.create(
to = "did:ex:pfi",
from = "did:ex:alice",
rfqData = RfqData(
rfqData = CreateRfqData(
offeringId = offeringId,
payin = SelectedPayinMethod(
payin = CreateSelectedPayinMethod(
kind = "BTC_ADDRESS",
paymentDetails = mapOf(),
amount = "0.001"
),
payout = SelectedPayoutMethod(
payout = CreateSelectedPayoutMethod(
kind = "ETH_ADDRESS",
paymentDetails = mapOf()
),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,15 @@ object SignatureVerifier {
*/
fun digestOf(metadata: Metadata, data: Data): ByteArray {
val payload = mapOf("metadata" to metadata, "data" to data)
return digestOf(payload)
}

/**
* Generates a canonicalized digest of any payload.
*
* @return The digest as a byte array.
*/
fun digestOf(payload: Any): ByteArray {
val canonicalJsonSerializedPayload = JsonCanonicalizer(Json.stringify(payload))

val sha256 = MessageDigest.getInstance("SHA-256")
Expand Down
107 changes: 93 additions & 14 deletions protocol/src/main/kotlin/tbdex/sdk/protocol/models/MessageData.kt
Original file line number Diff line number Diff line change
Expand Up @@ -11,19 +11,63 @@ sealed interface MessageData : Data

/**
* A data class implementing [MessageData] that represents the contents of an [Rfq].
* This includes salted hashes of fields in RfqPrivateData.
*
* @property offeringId Offering which Alice would like to get a quote for
* @property payin selected payin amount, method, and details
* @property payout selected payout method, and details
* @property claims an array of claims that fulfill the requirements declared in the referenced Offering
* @property payin selected payin amount, method, and hashed details
* @property payout selected payout method, and hashed details
* @property claimsHash hash of claims that fulfill the requirements declared in the referenced Offering
*/
class RfqData(
val offeringId: String,
val payin: SelectedPayinMethod,
val payout: SelectedPayoutMethod,
val claims: List<String>
var claimsHash: String? = null
) : MessageData

/**
* Private data contained in a RFQ message, including data which will be placed in {@link RfqPrivateData}
*
* @property salt Randomly generated cryptographic salt used to hash privateData fields
* @property payin A container for the cleartext `payin.paymentDetails`
* @property payout A container for the cleartext `payout.paymentDetails`
* @property claims claims that fulfill the requirements declared in an Offering
*/
class RfqPrivateData(
val salt: String,
val payin: PrivatePaymentDetails? = null,
val payout: PrivatePaymentDetails? = null,
val claims: List<String>? = null
)

/**
* Data contained in a RFQ message, including data which will be placed in Rfq.privateDAta.
* Used for creating an RFQ.
*
* @property offeringId Offering which Alice would like to get a quote for
* @property payin selected payin amount, method, and cleartext payment details
* @property payout selected payout method, and cleartext payment details
* @property claims an array of hashes claims that fulfill the requirements declared in the referenced Offering
*/
class CreateRfqData(
val offeringId: String,
val payin: CreateSelectedPayinMethod,
val payout: CreateSelectedPayoutMethod,
val claims: List<String>
)

/**
* A container for the cleartext `paymentDetails`
*
* @property paymentDetails An object containing the properties defined in the
* respective Offering's requiredPaymentDetails json schema.
* When creating an Rfq, this value is taken from
* CreateSelectedPaymentMethod.paymentDetails.
*/
class PrivatePaymentDetails(
val paymentDetails: Map<String, Any>? = null
)

/**
* A data class representing the payment method selected.
*
Expand All @@ -33,35 +77,70 @@ class RfqData(
*/
sealed class SelectedPaymentMethod(
val kind: String,
val paymentDetails: Map<String, Any>? = null
var paymentDetailsHash: String? = null
) : MessageData

/**
* A data class representing the payin method selected.
*
* @property kind type of payin method
* A data class representing the payment method selected, including the cleartext payment details.
* Used for creating an RFQ.
* @property kind type of payment method
* @property paymentDetails An object containing the properties
* defined in an Offering's requiredPaymentDetails json schema
*/
sealed class CreateSelectedPaymentMethod(
val kind: String,
val paymentDetails: Map<String, Any>? = null
)

/**
* A data class representing the payin method selected.
*
* @property kind type of payment method
* @property paymentDetailsHash A hash of the object containing the properties
* defined in an Offering's requiredPaymentDetails json schema
* @property amount Amount of currency Alice wants to pay in exchange for payout currency
*/
class SelectedPayinMethod(
kind: String,
paymentDetails: Map<String, Any>? = null,
paymentDetailsHash: String? = null,
val amount: String
) : SelectedPaymentMethod(kind, paymentDetails)
) : SelectedPaymentMethod(kind, paymentDetailsHash)

/**
* A data class representing the payin method selected, including the cleartext payin details.
* @property kind type of payment method
* @property paymentDetails An object containing the properties
* defined in an Offering's requiredPaymentDetails json schema
* @property amount Amount of currency Alice wants to pay in exchange for payout currency
*/
class CreateSelectedPayinMethod(
kind: String,
paymentDetails: Map<String, Any>? = null,
val amount: String
) : CreateSelectedPaymentMethod(kind, paymentDetails)

/**
* A data class representing the payout method selected.
*
* @property kind type of payout method
* @property paymentDetails An object containing the properties
* @property kind type of payment method
* @property paymentDetailsHash A hash of the object containing the properties
* defined in an Offering's requiredPaymentDetails json schema
*/
class SelectedPayoutMethod(
kind: String,
paymentDetails: Map<String, Any>? = null
) : SelectedPaymentMethod(kind, paymentDetails)
paymentDetailsHash: String? = null
) : SelectedPaymentMethod(kind, paymentDetailsHash)

/**
* A data class representing the payin method selected, including the cleartext payout details.
* @property kind type of payment method
* @property paymentDetails An object containing the properties
* defined in an Offering's requiredPaymentDetails json schema
*/
class CreateSelectedPayoutMethod(
kind: String,
paymentDetails: Map<String, Any>? = null,
) : CreateSelectedPaymentMethod(kind, paymentDetails)

/**
* A data class implementing [MessageData] that represents the contents of a [Quote].
Expand Down
Loading

0 comments on commit 85598e1

Please sign in to comment.