rippled
Loading...
Searching...
No Matches
CredentialHelpers.cpp
1//------------------------------------------------------------------------------
2/*
3 This file is part of rippled: https://github.com/ripple/rippled
4 Copyright (c) 2024 Ripple Labs Inc.
5
6 Permission to use, copy, modify, and/or distribute this software for any
7 purpose with or without fee is hereby granted, provided that the above
8 copyright notice and this permission notice appear in all copies.
9
10 THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17*/
18//==============================================================================
19
20#include <xrpld/app/misc/CredentialHelpers.h>
21#include <xrpld/ledger/View.h>
22
23#include <xrpl/protocol/digest.h>
24
25#include <unordered_set>
26
27namespace ripple {
28namespace credentials {
29
30bool
32 std::shared_ptr<SLE const> const& sleCredential,
33 NetClock::time_point const& closed)
34{
35 std::uint32_t const exp = (*sleCredential)[~sfExpiration].value_or(
37 std::uint32_t const now = closed.time_since_epoch().count();
38 return now > exp;
39}
40
41bool
42removeExpired(ApplyView& view, STTx const& tx, beast::Journal const j)
43{
44 auto const closeTime = view.info().parentCloseTime;
45 bool foundExpired = false;
46
47 STVector256 const& arr(tx.getFieldV256(sfCredentialIDs));
48 for (auto const& h : arr)
49 {
50 // Credentials already checked in preclaim. Look only for expired here.
51 auto const k = keylet::credential(h);
52 auto const sleCred = view.peek(k);
53
54 if (sleCred && checkExpired(sleCred, closeTime))
55 {
56 JLOG(j.trace())
57 << "Credentials are expired. Cred: " << sleCred->getText();
58 // delete expired credentials even if the transaction failed
59 deleteSLE(view, sleCred, j);
60 foundExpired = true;
61 }
62 }
63
64 return foundExpired;
65}
66
67TER
69 ApplyView& view,
70 std::shared_ptr<SLE> const& sleCredential,
72{
73 if (!sleCredential)
74 return tecNO_ENTRY;
75
76 auto delSLE =
77 [&view, &sleCredential, j](
78 AccountID const& account, SField const& node, bool isOwner) -> TER {
79 auto const sleAccount = view.peek(keylet::account(account));
80 if (!sleAccount)
81 {
82 JLOG(j.fatal()) << "Internal error: can't retrieve Owner account.";
83 return tecINTERNAL;
84 }
85
86 // Remove object from owner directory
87 std::uint64_t const page = sleCredential->getFieldU64(node);
88 if (!view.dirRemove(
89 keylet::ownerDir(account), page, sleCredential->key(), false))
90 {
91 JLOG(j.fatal()) << "Unable to delete Credential from owner.";
92 return tefBAD_LEDGER;
93 }
94
95 if (isOwner)
96 adjustOwnerCount(view, sleAccount, -1, j);
97
98 return tesSUCCESS;
99 };
100
101 auto const issuer = sleCredential->getAccountID(sfIssuer);
102 auto const subject = sleCredential->getAccountID(sfSubject);
103 bool const accepted = sleCredential->getFlags() & lsfAccepted;
104
105 auto err = delSLE(issuer, sfIssuerNode, !accepted || (subject == issuer));
106 if (!isTesSuccess(err))
107 return err;
108
109 if (subject != issuer)
110 {
111 err = delSLE(subject, sfSubjectNode, accepted);
112 if (!isTesSuccess(err))
113 return err;
114 }
115
116 // Remove object from ledger
117 view.erase(sleCredential);
118
119 return tesSUCCESS;
120}
121
122NotTEC
124{
125 if (!ctx.tx.isFieldPresent(sfCredentialIDs))
126 return tesSUCCESS;
127
128 auto const& credentials = ctx.tx.getFieldV256(sfCredentialIDs);
129 if (credentials.empty() || (credentials.size() > maxCredentialsArraySize))
130 {
131 JLOG(ctx.j.trace())
132 << "Malformed transaction: Credentials array size is invalid: "
133 << credentials.size();
134 return temMALFORMED;
135 }
136
138 for (auto const& cred : credentials)
139 {
140 auto [it, ins] = duplicates.insert(cred);
141 if (!ins)
142 {
143 JLOG(ctx.j.trace())
144 << "Malformed transaction: duplicates in credentials.";
145 return temMALFORMED;
146 }
147 }
148
149 return tesSUCCESS;
150}
151
152TER
153valid(PreclaimContext const& ctx, AccountID const& src)
154{
155 if (!ctx.tx.isFieldPresent(sfCredentialIDs))
156 return tesSUCCESS;
157
158 auto const& credIDs(ctx.tx.getFieldV256(sfCredentialIDs));
159 for (auto const& h : credIDs)
160 {
161 auto const sleCred = ctx.view.read(keylet::credential(h));
162 if (!sleCred)
163 {
164 JLOG(ctx.j.trace()) << "Credential doesn't exist. Cred: " << h;
165 return tecBAD_CREDENTIALS;
166 }
167
168 if (sleCred->getAccountID(sfSubject) != src)
169 {
170 JLOG(ctx.j.trace())
171 << "Credential doesn't belong to the source account. Cred: "
172 << h;
173 return tecBAD_CREDENTIALS;
174 }
175
176 if (!(sleCred->getFlags() & lsfAccepted))
177 {
178 JLOG(ctx.j.trace()) << "Credential isn't accepted. Cred: " << h;
179 return tecBAD_CREDENTIALS;
180 }
181
182 // Expiration checks are in doApply
183 }
184
185 return tesSUCCESS;
186}
187
188TER
189authorized(ApplyContext const& ctx, AccountID const& dst)
190{
191 auto const& credIDs(ctx.tx.getFieldV256(sfCredentialIDs));
194 lifeExtender.reserve(credIDs.size());
195 for (auto const& h : credIDs)
196 {
197 auto sleCred = ctx.view().read(keylet::credential(h));
198 if (!sleCred) // already checked in preclaim
199 return tefINTERNAL;
200
201 auto [it, ins] =
202 sorted.emplace((*sleCred)[sfIssuer], (*sleCred)[sfCredentialType]);
203 if (!ins)
204 return tefINTERNAL;
205 lifeExtender.push_back(std::move(sleCred));
206 }
207
208 if (!ctx.view().exists(keylet::depositPreauth(dst, sorted)))
209 {
210 JLOG(ctx.journal.trace()) << "DepositPreauth doesn't exist";
211 return tecNO_PERMISSION;
212 }
213
214 return tesSUCCESS;
215}
216
218makeSorted(STArray const& credentials)
219{
221 for (auto const& cred : credentials)
222 {
223 auto [it, ins] = out.emplace(cred[sfIssuer], cred[sfCredentialType]);
224 if (!ins)
225 return {};
226 }
227 return out;
228}
229
230NotTEC
231checkArray(STArray const& credentials, unsigned maxSize, beast::Journal j)
232{
233 if (credentials.empty() || (credentials.size() > maxSize))
234 {
235 JLOG(j.trace()) << "Malformed transaction: "
236 "Invalid credentials size: "
237 << credentials.size();
238 return credentials.empty() ? temARRAY_EMPTY : temARRAY_TOO_LARGE;
239 }
240
242 for (auto const& credential : credentials)
243 {
244 auto const& issuer = credential[sfIssuer];
245 if (!issuer)
246 {
247 JLOG(j.trace()) << "Malformed transaction: "
248 "Issuer account is invalid: "
249 << to_string(issuer);
251 }
252
253 auto const ct = credential[sfCredentialType];
254 if (ct.empty() || (ct.size() > maxCredentialTypeLength))
255 {
256 JLOG(j.trace()) << "Malformed transaction: "
257 "Invalid credentialType size: "
258 << ct.size();
259 return temMALFORMED;
260 }
261
262 auto [it, ins] = duplicates.insert(sha512Half(issuer, ct));
263 if (!ins)
264 {
265 JLOG(j.trace()) << "Malformed transaction: "
266 "duplicates in credenentials.";
267 return temMALFORMED;
268 }
269 }
270
271 return tesSUCCESS;
272}
273
274} // namespace credentials
275
276TER
278 ApplyContext& ctx,
279 AccountID const& src,
280 AccountID const& dst,
281 std::shared_ptr<SLE> const& sleDst)
282{
283 // If depositPreauth is enabled, then an account that requires
284 // authorization has at least two ways to get a payment in:
285 // 1. If src == dst, or
286 // 2. If src is deposit preauthorized by dst (either by account or by
287 // credentials).
288
289 bool const credentialsPresent = ctx.tx.isFieldPresent(sfCredentialIDs);
290
291 if (credentialsPresent &&
292 credentials::removeExpired(ctx.view(), ctx.tx, ctx.journal))
293 return tecEXPIRED;
294
295 if (sleDst && (sleDst->getFlags() & lsfDepositAuth))
296 {
297 if (src != dst)
298 {
299 if (!ctx.view().exists(keylet::depositPreauth(dst, src)))
300 return !credentialsPresent ? tecNO_PERMISSION
301 : credentials::authorized(ctx, dst);
302 }
303 }
304
305 return tesSUCCESS;
306}
307
308} // namespace ripple
A generic endpoint for log messages.
Definition: Journal.h:60
Stream fatal() const
Definition: Journal.h:352
Stream trace() const
Severity stream access functions.
Definition: Journal.h:322
State information when applying a tx.
Definition: ApplyContext.h:37
ApplyView & view()
Definition: ApplyContext.h:55
beast::Journal const journal
Definition: ApplyContext.h:52
Writeable view to a ledger, for applying a transaction.
Definition: ApplyView.h:141
bool dirRemove(Keylet const &directory, std::uint64_t page, uint256 const &key, bool keepRoot)
Remove an entry from a directory.
Definition: ApplyView.cpp:190
virtual std::shared_ptr< SLE > peek(Keylet const &k)=0
Prepare to modify the SLE associated with key.
virtual void erase(std::shared_ptr< SLE > const &sle)=0
Remove a peeked SLE.
virtual std::shared_ptr< SLE const > read(Keylet const &k) const =0
Return the state item associated with a key.
virtual bool exists(Keylet const &k) const =0
Determine if a state item exists.
virtual LedgerInfo const & info() const =0
Returns information about the ledger.
Identifies fields.
Definition: SField.h:144
bool empty() const
Definition: STArray.h:254
size_type size() const
Definition: STArray.h:248
const STVector256 & getFieldV256(SField const &field) const
Definition: STObject.cpp:679
bool isFieldPresent(SField const &field) const
Definition: STObject.cpp:484
T emplace(T... args)
T insert(T... args)
NotTEC checkFields(PreflightContext const &ctx)
TER deleteSLE(ApplyView &view, std::shared_ptr< SLE > const &sleCredential, beast::Journal j)
bool removeExpired(ApplyView &view, STTx const &tx, beast::Journal const j)
TER authorized(ApplyContext const &ctx, AccountID const &dst)
TER valid(PreclaimContext const &ctx, AccountID const &src)
NotTEC checkArray(STArray const &credentials, unsigned maxSize, beast::Journal j)
bool checkExpired(std::shared_ptr< SLE const > const &sleCredential, NetClock::time_point const &closed)
std::set< std::pair< AccountID, Slice > > makeSorted(STArray const &credentials)
Keylet credential(AccountID const &subject, AccountID const &issuer, Slice const &credType) noexcept
Definition: Indexes.cpp:536
Keylet account(AccountID const &id) noexcept
AccountID root.
Definition: Indexes.cpp:175
Keylet ownerDir(AccountID const &id) noexcept
The root page of an account's directory.
Definition: Indexes.cpp:365
Keylet depositPreauth(AccountID const &owner, AccountID const &preauthorized) noexcept
A DepositPreauth.
Definition: Indexes.cpp:333
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: algorithm.h:26
@ lsfDepositAuth
bool isTesSuccess(TER x)
Definition: TER.h:656
@ tefBAD_LEDGER
Definition: TER.h:170
@ tefINTERNAL
Definition: TER.h:173
static bool adjustOwnerCount(ApplyContext &ctx, int count)
Definition: SetOracle.cpp:186
std::size_t constexpr maxCredentialsArraySize
The maximum number of credentials can be passed in array.
Definition: Protocol.h:107
@ accepted
Manifest is valid.
std::size_t constexpr maxCredentialTypeLength
The maximum length of a CredentialType inside a Credential.
Definition: Protocol.h:104
@ tecNO_ENTRY
Definition: TER.h:293
@ tecINTERNAL
Definition: TER.h:297
@ tecBAD_CREDENTIALS
Definition: TER.h:346
@ tecNO_PERMISSION
Definition: TER.h:292
@ tecEXPIRED
Definition: TER.h:301
@ tesSUCCESS
Definition: TER.h:242
TER verifyDepositPreauth(ApplyContext &ctx, AccountID const &src, AccountID const &dst, std::shared_ptr< SLE > const &sleDst)
std::string to_string(base_uint< Bits, Tag > const &a)
Definition: base_uint.h:630
@ credential
Credentials signature.
TERSubset< CanCvtToTER > TER
Definition: TER.h:627
sha512_half_hasher::result_type sha512Half(Args const &... args)
Returns the SHA512-Half of a series of objects.
Definition: digest.h:225
@ temMALFORMED
Definition: TER.h:87
@ temARRAY_EMPTY
Definition: TER.h:140
@ temARRAY_TOO_LARGE
Definition: TER.h:141
@ temINVALID_ACCOUNT_ID
Definition: TER.h:119
T push_back(T... args)
T reserve(T... args)
NetClock::time_point parentCloseTime
Definition: LedgerHeader.h:42
State information when determining if a tx is likely to claim a fee.
Definition: Transactor.h:54
ReadView const & view
Definition: Transactor.h:57
beast::Journal const j
Definition: Transactor.h:61
State information when preflighting a tx.
Definition: Transactor.h:33
beast::Journal const j
Definition: Transactor.h:39
T time_since_epoch(T... args)