Group TMR Temporary Key
The concept of GroupTMRTemporaryKey
is a feature of the Seald SDK that enables joining a group through a 2-man rule mechanism, similar to the storage of identities.
The idea is to protect a group key for a certain Auth Factor (an email address, or a phone number), with a specific overEncryptionKey
that will be stored by your servers (like the twoManRuleKey
for identity storage).
Then, when the recipient wants to join the group, they receive a challenge
by email or SMS, valid for 6 hours, and must repeat it in the application to prove they control the Auth Factor, and can join the group.
This feature is particularly useful for adding as recipient to a group a user who is not yet registered on your application, for example when sending them an invitation.
Creating a GroupTMRTemporaryKey
Group members with administrator status can create a GroupTMRTemporaryKey
for the group.
To create a GroupTMRTemporaryKey
, you need the group ID, and use the method sdk.createGroupTMRTemporaryKey()
. This method takes as arguments the ID of the group for which to create the temporary key, as well as an options object:
authFactor
, which itself contains the type of the Auth Factor used ('EM'
for an email address,'SMS'
for a phone number), and the value of this Auth Factor;rawOverEncryptionKey
, which is the key to protect thisTmrAccess
, must absolutely be the base64 encoding of a cryptographically random 64-byte buffer;isAdmin
, a Boolean defining the user's administrator status when joining the group;- Optionally,
forceGroupUpdate
, a Boolean to force the locally known group update before creating theGroupTMRTemporaryKey
, defaulting tofalse
.
TIP
To ensure end-to-end confidentiality, the rawOverEncryptionKey
must be be accessible only to the person creating the GroupTMRTemporaryKey
and to the recipient who will be joining the group.
It is your server's responsibility to store these rawOverEncryptionKey
and limit access to the user who creates the GroupTMRTemporaryKey
and the invited user.
If multiple users need to add GroupTMRTemporaryKey
for the same Auth Factor, you should create different rawOverEncryptionKey
s for each one. You can also create a different rawOverEncryptionKey
for each addition of GroupTMRTemporaryKey
, even if it is the same user adding it for the same Auth Factor, if this is simpler for you.
TIP
The forceGroupUpdate
option optimizes network requests, by forcing the before attempting to create a GroupTMRTemporaryKey
.
If the locally known group is not up-to-date, creation will fail a first time, and the function will call itself recursively with forceGroupUpdate
at true
.
The method sdk.createGroupTMRTemporaryKey()
returns an object containing the GroupTMRTemporaryKey
ID. This ID is not secret, and can be transmitted to your server. It can be used to administer the GroupTMRTemporaryKey
, or to choose which GroupTMRTemporaryKey
to use to join a group.
Example usage:
// Each `rawOverEncryptionKey` must necessarily be a 64-byte cryptographically secure random buffer, encoded in base64.
const rawOverEncryptionKey = await sealdSdk.utils.generateB64EncodedSymKey()
// Creating a `GroupTMRTemporaryKey`
const tmrAccess = await sealdSdk.createGroupTMRTemporaryKey(groupInfo.id, {
rawOverEncryptionKey,
authFactor: { type: 'EM', value: 'test@example.com' },
isAdmin: true
})
// Now, you can send the tmrAccess.id with the rawOverEncryptionKey to your servers to save them.
SealdError* err = NULL;
char* groupId = "UUID"; // The group ID
char* authFactorType = "EM";
char* authFactorValue = "user@email.com";
int isAdmin = 0; // 1 for admin, 0 otherwise
int rawOverEncryptionKeyLen = 64;
unsigned char* rawOverEncryptionKeyBytes = randomBuffer(rawOverEncryptionKeyLen); // cryptographically random buffer of 64 bytes.
SealdGroupTMRTemporaryKey* gTMRCreated = NULL;
int errCode = SealdSdk_CreateGroupTMRTemporaryKey(sdk1, groupId, authFactorType, authFactorValue, isAdmin, rawOverEncryptionKeyBytes, rawOverEncryptionKeyLen, &gTMRCreated, &err);
NSError* error = nil;
NSString* groupId = @"UUID"; // The group ID
NSData* rawOverEncryptionKey = randomData(64); // cryptographically random buffer of 64 bytes.
SealdTmrAuthFactor* tmrAuthFactor = [[SealdTmrAuthFactor alloc] initWithValue:@"user@email.com" type:@"EM"];
SealdGroupTmrTemporaryKey* gTMRKey = [sdk createGroupTMRTemporaryKeyWithGroupId:groupId
authFactor:tmrAuthFactor
isAdmin:NO
rawOverEncryptionKey:rawOverEncryptionKey
error:&error];
let groupId = "UUID" // The group ID
let tmrAuthFactor = SealdTmrAuthFactor(value: "user@email.com", type: "EM")
let rawOverEncryptionKey = randomData(64) // cryptographically random buffer of 64 bytes.
let gTMRTKCreated = try await sdk.createGroupTMRTemporaryKeyAsync(
withGroupId: groupId,
authFactor: tmrAuthFactor,
isAdmin: false,
rawOverEncryptionKey: rawOverEncryptionKey)
val groupId = "UUID" // The group ID
val tmrAuthFactor = AuthFactor(AuthFactorType.EM, "user@email.com")
val rawOverEncryptionKey = randomData(64) // cryptographically random buffer of 64 bytes.
val gTMRCreated = sdk.createGroupTMRTemporaryKey(groupId, tmrAuthFactor, rawOverEncryptionKey, isAdmin = true)
String groupId = "UUID"; // The group ID
String authFactorType = "EM";
String authFactorValue = "user@email.com";
Uint8List rawOverEncryptionKey = randomData(64); // cryptographically random buffer of 64 bytes.
final SealdGroupTMRTemporaryKey gTMRTKCreated =
await sdk.createGroupTMRTemporaryKeyAsync(groupId, authFactorType,
authFactorValue, rawOverEncryptionKey);
GroupTMRTemporaryKey
administration
There are three functions for administering GroupTMRTemporaryKeys
:
The method
sdk.listGroupTMRTemporaryKeys()
lists all existingGroupTMRTemporaryKeys
for a given group.The method
sdk.searchGroupTMRTemporaryKeys()
lists allGroupTMRTemporaryKeys
usable with a giventmrJWT
. ThetmrJWT
must be the JWT associated with an Auth Factor. It is possible to retrieve this JWT with methodssks2MR.getFactorToken()
TIP
For listGroupTMRTemporaryKeys
and searchGroupTMRTemporaryKeys
functions, if the all
option is set to true
, returns all existing pages after the page
page.
The functions listGroupTMRTemporaryKeys
and searchGroupTMRTemporaryKeys
, if the all
option is set to true
, return all existing pages starting from the page specified by the page
parameter.
- The method
sdk.deleteGroupTMRTemporaryKey()
deletes aGroupTMRTemporaryKey
.
Convert a GroupTMRTemporaryKey
to join a group
To convert a GroupTMRTemporaryKey
, one must first prove that they control the recipient's Auth Factor. To do this, obtain a JWT from the SSKS server with the ssks2MR.getFactorToken()
method. This method, of course, requires being authenticated with SSKS, that is either having an authenticatedSessionId
, or using the POST /tmr/back/challenge_send/
API endpoint (with force_auth
to true
) to obtain a challenge, and passing this challenge during the call to ssks2MR.getFactorToken()
.
Then, once you have this token, use the sdk.convertGroupTMRTemporaryKey()
method. This method takes as arguments:
groupId
, the ID of the group concerned;temporaryKeyId
, the ID of the concernedGroupTMRTemporaryKey
;- the
rawOverEncryptionKey
used; - optionally, a
deleteOnConvert
option to delete theGroupTMRTemporaryKey
after conversion.
Example usage:
// Retrieving a tmrJWT
const getFactorTokenResponse = await sdk.ssks2MR.getFactorToken({ sessionId: ssksSessionId, authFactor, challenge })
await convertGroupTMRTemporaryKey(
groupId, // The ID of the groupe to join
temporaryKeyId, // The ID of the `GroupTMRTemporaryKey` to convert
getFactorTokenResponse.token, // The token obtained by calling `ssks2MR.getFactorToken()`
rawOverEncryptionKey, // `rawOverEncryptionKey` transmitted by your server
{ deleteOnConvert: true }
)
SealdError* err = NULL;
char* groupId = "UUID"; // The group ID
char* groupTMRKeyId = "UUID"; // The ID of the group TMR key to convert
char* authFactorType = "EM";
char* authFactorValue = "user@email.com";
int deleteOnConvert = 0; // 1 to delete after conversion, 0 otherwise
int rawOverEncryptionKeyLen = 64;
unsigned char* rawOverEncryptionKeyBytes = RETRIEVED_KEY // The `rawOverEncryptionKey` transmitted by your server.
// Retrieve a JWT associated with the authentication factor from SSKS
SealdSsksTMRPlugin* ssksPluginTmrAccesses; // An initialized instance of SealdSsksTMRPlugin
char* authenticationSessionId = "UUID"; // A TMR authentication session created by your server
char* ssksTMRChallenge = "aaaaaa"; // The challenge sent to the authentication factor
SealdSsksTMRPluginGetFactorTokenResponse* retrievedToken = NULL;
int errCode = SealdSsksTMRPlugin_GetFactorToken(ssksPluginTmrAccesses, authenticationSessionId, authFactorType, authFactorValue, ssksTMRChallenge, &retrievedToken, &err);
// Convert the group TMR temporary key
int errCode = SealdSdk_ConvertGroupTMRTemporaryKey(sdk2, groupId, groupTMRKeyId, retrievedToken->Token, rawOverEncryptionKeyBytes, rawOverEncryptionKeyLen, deleteOnConvert, &err);
NSError* error = nil;
NSString* groupId = @"UUID"; // The group ID
NSString* groupTMRKeyId = @"UUID"; // The ID of the group TMR key to convert
NSData* rawOverEncryptionKey = RETRIEVED_KEY // The `rawOverEncryptionKey` transmitted by your server.
SealdTmrAuthFactor* tmrAuthFactor = [[SealdTmrAuthFactor alloc] initWithValue:@"user@email.com" type:@"EM"];
// Retrieve a JWT associated with the authentication factor from SSKS
NSString* authenticationSessionId = @"UUID"; // A TMR authentication session created by your server
NSString* ssksTMRChallenge = @"aaaaaa"; // The challenge sent to the authentication factor
SealdSsksGetFactorTokenResponse* tmrJWT = [ssksTMR getFactorToken:authenticationSessionId
authFactor:tmrAuthFactor
challenge:ssksTMRChallenge
error:&error];
// Convert the group TMR temporary key
[sdk2 convertGroupTMRTemporaryKeyWithGroupId:groupTMRId
temporaryKeyId:gTMRKey.keyId
tmrJWT:tmrJWT.token
rawOverEncryptionKey:rawOverEncryptionKey
deleteOnConvert:NO
error:&error];
let groupId = "UUID" // The group ID
let groupTMRKeyId = "UUID" // The ID of the group TMR key to convert
let tmrAuthFactor = SealdTmrAuthFactor(value: "user@email.com", type: "EM")
let rawOverEncryptionKey = RETRIEVED_KEY // The `rawOverEncryptionKey` transmitted by your server.
// Retrieve a JWT associated with the authentication factor from SSKS
let authenticationSessionId = "UUID"// A TMR authentication session created by your server
let ssksTMRChallenge = "aaaaaa" // The challenge sent to the authentication factor
let tmrJWT = try await ssksTMR.getFactorTokenAsync(
sessionId: authenticationSessionId,
authFactor: tmrAuthFactor,
challenge: ssksTMRChallenge
)
// Convert the group TMR temporary key
try await sdk.convertGroupTMRTemporaryKeyAsync(
withGroupId: groupId,
temporaryKeyId: groupTMRKeyId,
tmrJWT: tmrJWT.token,
rawOverEncryptionKey: rawOverEncryptionKey,
deleteOnConvert: false)
val groupId = "UUID" // The group ID
val groupTMRKeyId = "UUID" // The ID of the group TMR key to convert
val tmrAuthFactor = AuthFactor(AuthFactorType.EM, "user@email.com")
val rawOverEncryptionKey = RETRIEVED_KEY // The `rawOverEncryptionKey` transmitted by your server.
// Retrieve a JWT associated with the authentication factor from SSKS
val authenticationSessionId = "UUID" // A TMR authentication session created by your server
val ssksTMRChallenge = "aaaaaa" // The challenge sent to the authentication factor
val tmrJWT =
ssksPlugin.getFactorTokenAsync(
authenticationSessionId,
authFactor = tmrAuthFactor,
challenge = ssksTMRChallenge,
)
// Convert the group TMR temporary key
sdk.convertGroupTMRTemporaryKey(groupId, groupTMRKeyId, tmrJWT.token, rawOverEncryptionKey)
String groupId = "UUID"; // The group ID
String groupTMRKeyId = "UUID"; // The ID of the group TMR key to convert
String authFactorType = "EM";
String authFactorValue = "user@email.com";
Uint8List rawOverEncryptionKey = RETRIEVED_KEY // The `rawOverEncryptionKey` transmitted by your server.
// Retrieve a JWT associated with the authentication factor from SSKS
String authenticationSessionId = "UUID"; // A TMR authentication session created by your server
String ssksTMRChallenge = "aaaaaa"; // The challenge sent to the authentication factor
SealdSsksTMRPluginGetFactorTokenResponse tmrJWT =
await ssksPlugin.getAuthFactorTokenAsync(
authenticationSessionId, authFactorType, authFactorValue,
challenge: ssksTMRChallenge);
// Convert the group TMR temporary key
await sdk2.convertGroupTMRTemporaryKeyAsync(
groupId, groupTMRKeyId, tmrJWT.token, rawOverEncryptionKey);