requestSecretsFromNamespace method
Broadcasts a pull request for held secrets to every key package
registered for namespace (minus this client). Holders that pass the
answer policy reply by sharing the matching secrets; the caller typically
then waitForSecrets. Filter the request by exact names and/or a
namePrefix (e.g. __rk. for epoch keys). excludeEnrollmentIds drops
revoked enrollments. Returns the number of key packages the request was
sent to.
Implementation
Future<int> requestSecretsFromNamespace(
String namespace, {
List<String>? names,
String? namePrefix,
Set<String> excludeEnrollmentIds = const {},
}) async {
final members = await directory.listForNamespace(namespace,
excludeEnrollmentIds: excludeEnrollmentIds);
final String selfId = enrollmentId;
// ONE id for the whole fan-out: it is what lets a second holder see that a
// first has already answered THIS request rather than other traffic at the
// same address.
final String requestId = Uuid().v4().replaceAll('-', '').substring(0, 16);
int sent = 0;
for (final member in members) {
final to = member.keyPackage;
if (to != null && to.kpid != null && !_isSelf(member, selfId)) {
// Per member: sendEnvelope throws StateError when a member advertises
// no mutually supported algorithm, and the design is N holders
// precisely so that some can be unreachable.
try {
await sendEnvelope(
to,
namespace,
{
'kind': secretRequestKind,
'requestId': requestId,
if (names != null) 'want': names,
if (namePrefix != null) 'namePrefix': namePrefix,
},
// The request answers nothing; its own id travels in the sealed
// payload, where a holder can trust it.
inReplyTo: EnvelopeAddressing.unsolicited);
sent++;
} catch (e) {
if (e is StoppedException) rethrow;
_throwIfStopped();
// Warning, not finer: a request that never went out is
// indistinguishable from one nobody answered.
logger.warning('Could not request secrets from enrollment '
'${member.enrollmentId} (kpid ${to.kpid}) in $namespace: $e. '
'The remaining members are still being asked.');
}
}
}
return sent;
}