Skip to content
This repository was archived by the owner on Dec 12, 2024. It is now read-only.

Commit c80223f

Browse files
author
Diane Huxley
authored
Merge pull request #12 from TBD54566975/rfq-private-salt
Hash private RFQ fields
2 parents 5ff5088 + 3141a31 commit c80223f

15 files changed

+713
-84
lines changed

lib/src/protocol/crypto.dart

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import 'dart:convert';
2+
import 'dart:math';
3+
import 'dart:typed_data';
4+
5+
import 'package:crypto/crypto.dart';
6+
import 'package:web5/web5.dart';
7+
8+
class CryptoUtils {
9+
static Uint8List digest(Object value) {
10+
final payload = json.encode(value);
11+
final bytes = utf8.encode(payload);
12+
return Uint8List.fromList(sha256.convert(bytes).bytes);
13+
}
14+
15+
static String generateSalt() {
16+
var random = Random.secure();
17+
var bytes = Uint8List.fromList(List.generate(16, (_) => random.nextInt(256)));
18+
return Base64Url.encode(bytes);
19+
}
20+
}

lib/src/protocol/exceptions.dart

+61
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
// Tbdex exception codes are each thrown in exactly one place.
2+
// Each code is prefixed with the name of the file in which it is thrown.
3+
enum TbdexExceptionCode {
4+
parserKindRequired,
5+
parserMessageJsonNotObject,
6+
parserMetadataMalformed,
7+
parserUnknownMessageKind,
8+
parserUnknownResourceKind,
9+
messageSignatureMissing,
10+
messageSignatureMismatch,
11+
messageUnknownKind,
12+
resourceSignatureMissing,
13+
resourceSignatureMismatch,
14+
resourceUnknownKind,
15+
rfqClaimsHashMismatch,
16+
rfqClaimsMissing,
17+
rfqOfferingIdMismatch,
18+
rfqPrivateDataMissing,
19+
rfqPayinDetailsHashMismatch,
20+
rfqPayinDetailsMissing,
21+
rfqPayinGreaterThanMax,
22+
rfqPayinLessThanMin,
23+
rfqPayinDetailsNotValid,
24+
rfqPayoutDetailsHashMismatch,
25+
rfqPayoutDetailsMissing,
26+
rfqPayoutDetailsNotValid,
27+
rfqProtocolVersionMismatch,
28+
rfqUnknownPayinKind,
29+
rfqUnknownPayoutKind,
30+
validatorNoSchema,
31+
validatorUnknownMessageKind,
32+
validatorUnknownResourceKind,
33+
validatorJsonSchemaError,
34+
}
35+
36+
// TbdexException is the parent class for all custom exceptions thrown from this package
37+
class TbdexException implements Exception {
38+
final String message;
39+
final TbdexExceptionCode code;
40+
TbdexException(this.code, this.message);
41+
42+
@override
43+
String toString() => 'TbdexException($code): $message';
44+
}
45+
46+
class TbdexParseException extends TbdexException {
47+
TbdexParseException(TbdexExceptionCode code, String message): super(code, message);
48+
}
49+
50+
class TbdexSignatureVerificationException extends TbdexException {
51+
TbdexSignatureVerificationException(TbdexExceptionCode code, String message): super(code, message);
52+
}
53+
54+
class TbdexValidatorException extends TbdexException {
55+
TbdexValidatorException(TbdexExceptionCode code, String message): super(code, message);
56+
}
57+
58+
// Thrown when verifying RFQ against offering
59+
class TbdexVerifyOfferingRequirementsException extends TbdexException {
60+
TbdexVerifyOfferingRequirementsException(TbdexExceptionCode code, String message): super(code, message);
61+
}

lib/src/protocol/json-schemas/message.schema.json

+3-6
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,6 @@
4141
}
4242
},
4343
"required": ["from", "to", "kind", "id", "exchangeId", "createdAt", "protocol"]
44-
},
45-
"Private": {
46-
"type": "object",
47-
"description": "An ephemeral JSON object used to transmit sensitive data (e.g. PII)"
4844
}
4945
},
5046
"type": "object",
@@ -60,8 +56,9 @@
6056
"type": "string",
6157
"description": "Signature that verifies the authenticity and integrity of a message"
6258
},
63-
"private": {
64-
"$ref": "#/definitions/Private"
59+
"privateData": {
60+
"type": "object",
61+
"description": "Private data which can be detached from the payload without disrupting integrity. Only used in RFQs"
6562
}
6663
},
6764
"additionalProperties": false,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
{
2+
"$schema": "http://json-schema.org/draft-07/schema#",
3+
"$id": "https://tbdex.dev/rfq-private.schema.json",
4+
"type": "object",
5+
"properties": {
6+
"additionalProperties": false,
7+
"salt": {
8+
"type": "string",
9+
"description": "Randomly generated cryptographic salt used to hash privateData fields"
10+
},
11+
"claims": {
12+
"type": "array",
13+
"items": {
14+
"type": "string"
15+
},
16+
"description": "Presentation Submission that fulfills the requirements included in the respective Offering"
17+
},
18+
"payin": {
19+
"type": "object",
20+
"additionalProperties": false,
21+
"properties": {
22+
"paymentDetails": {
23+
"type": "object",
24+
"description": "An object containing the properties defined in the respective Offering's requiredPaymentDetails json schema"
25+
}
26+
}
27+
},
28+
"payout": {
29+
"additionalProperties": false,
30+
"type": "object",
31+
"properties": {
32+
"paymentDetails": {
33+
"type": "object",
34+
"description": "An object containing the properties defined in the respective Offering's requiredPaymentDetails json schema"
35+
}
36+
}
37+
}
38+
},
39+
"required": ["salt"]
40+
}

lib/src/protocol/json-schemas/rfq.schema.json

+10-13
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,9 @@
88
"type": "string",
99
"description": "Offering which Alice would like to get a quote for"
1010
},
11-
"claims": {
12-
"type": "array",
13-
"items": {
14-
"type": "string"
15-
},
16-
"description": "Presentation Submission that fulfills the requirements included in the respective Offering"
11+
"claimsHash": {
12+
"type": "string",
13+
"description": "Digests of Presentation Submissions that fulfills the requirements included in the respective Offering"
1714
},
1815
"payin": {
1916
"type": "object",
@@ -25,9 +22,9 @@
2522
"type": "string",
2623
"description": "Type of payment method e.g. BTC_ADDRESS, DEBIT_CARD, MOMO_MPESA"
2724
},
28-
"paymentDetails": {
29-
"type": "object",
30-
"description": "An object containing the properties defined in the respective Offering's requiredPaymentDetails json schema"
25+
"paymentDetailsHash": {
26+
"type": "string",
27+
"description": "Digest of an object containing the properties defined in the respective Offering's requiredPaymentDetails json schema"
3128
}
3229
},
3330
"required": ["amount", "kind"]
@@ -39,13 +36,13 @@
3936
"type": "string",
4037
"description": "Selected payout method from the respective offering"
4138
},
42-
"paymentDetails": {
43-
"type": "object",
44-
"description": "An object containing the properties defined in the respective Offering's requiredPaymentDetails json schema"
39+
"paymentDetailsHash": {
40+
"type": "string",
41+
"description": "Digest of an object containing the properties defined in the respective Offering's requiredPaymentDetails json schema"
4542
}
4643
},
4744
"required": ["kind"]
4845
}
4946
},
50-
"required": ["offeringId", "claims", "payin", "payout"]
47+
"required": ["offeringId", "payin", "payout"]
5148
}

lib/src/protocol/models/message.dart

+8-7
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import 'dart:convert';
22
import 'dart:typed_data';
33

4-
import 'package:crypto/crypto.dart';
4+
import 'package:tbdex/src/protocol/crypto.dart';
5+
import 'package:tbdex/src/protocol/exceptions.dart';
56
import 'package:tbdex/src/protocol/models/message_data.dart';
67
import 'package:tbdex/src/protocol/models/resource.dart';
78
import 'package:tbdex/src/protocol/validator.dart';
@@ -41,7 +42,7 @@ class MessageMetadata extends Metadata {
4142
return MessageMetadata(
4243
kind: MessageKind.values.firstWhere(
4344
(kind) => kind.name == json['kind'],
44-
orElse: () => throw Exception('unknown message kind: ${json['kind']}'),
45+
orElse: () => throw TbdexParseException(TbdexExceptionCode.messageUnknownKind, 'unknown message kind: ${json['kind']}'),
4546
),
4647
to: json['to'],
4748
from: json['from'],
@@ -91,7 +92,8 @@ abstract class Message {
9192

9293
Future<void> verify() async {
9394
if (signature == null) {
94-
throw Exception(
95+
throw TbdexSignatureVerificationException(
96+
TbdexExceptionCode.messageSignatureMissing,
9597
'signature verification failed: expected signature property to exist',
9698
);
9799
}
@@ -102,7 +104,8 @@ abstract class Message {
102104
final signingDid = parsedDidUrl.uri;
103105

104106
if (signingDid != metadata.from) {
105-
throw Exception(
107+
throw TbdexSignatureVerificationException(
108+
TbdexExceptionCode.messageSignatureMismatch,
106109
'signature verification failed: was not signed by the expected DID',
107110
);
108111
}
@@ -111,9 +114,7 @@ abstract class Message {
111114
}
112115

113116
Uint8List _digest() {
114-
final payload = json.encode({'metadata': metadata, 'data': data});
115-
final bytes = utf8.encode(payload);
116-
return Uint8List.fromList(sha256.convert(bytes).bytes);
117+
return CryptoUtils.digest({'metadata': metadata, 'data': data});
117118
}
118119

119120
@override

0 commit comments

Comments
 (0)