Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FIP: Support mix of Accounts & Personas #306

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
66 commits
Select commit Hold shift + click to select a range
c91f8f6
Reintroduce `From<RoleWithFactorInstances<ROLE>> for ScryptoAccessRule`
CyonAlexRDX Dec 6, 2024
2d3259e
impl From<MatrixOfFactorInstances> for ScryptoRuleSet
CyonAlexRDX Dec 9, 2024
502ba59
Merge branch 'main' into scrypto_access_rules_for_matrix_of_factor_in…
CyonAlexRDX Dec 12, 2024
7eb9e2d
bump Scrypto and RET
CyonAlexRDX Dec 12, 2024
4f39117
[no ci] WIP
Sajjon Dec 12, 2024
bf57732
[no ci] WIP
Sajjon Dec 12, 2024
ed7f78d
[no ci] wip
CyonAlexRDX Dec 13, 2024
9f7790d
create_access_controller
Sajjon Dec 13, 2024
332c6a9
[no ci] WIP
Sajjon Dec 13, 2024
9cded45
[no ci] wip
CyonAlexRDX Dec 13, 2024
8faaf3e
merge
Sajjon Dec 15, 2024
5efcfff
[no ci] WIP
Sajjon Dec 16, 2024
20fccd0
[no ci] wip
CyonAlexRDX Dec 16, 2024
f2ba3fa
[no ci] WIP
Sajjon Dec 16, 2024
642c9cc
[no ci] WIP
Sajjon Dec 16, 2024
d68c597
update readme
Sajjon Dec 16, 2024
9c8e7bc
fix swift
CyonAlexRDX Dec 16, 2024
6e56fa2
doc
CyonAlexRDX Dec 16, 2024
a0f3e6a
removed unused code
CyonAlexRDX Dec 16, 2024
15200f5
more tests
Sajjon Dec 16, 2024
67bc386
[no ci] WIP
Sajjon Dec 17, 2024
e54a5d3
polish
Sajjon Dec 17, 2024
3ba71d3
fix typo
Sajjon Dec 17, 2024
5940db0
Merge branch 'scrypto_access_rules_for_matrix_of_factor_instances' in…
Sajjon Dec 17, 2024
a2f77c8
[no ci] WIP
Sajjon Dec 17, 2024
48a568f
Merge branch 'main' into ac/mix_persona_and_accounts_in_factor_instan…
Sajjon Dec 17, 2024
ce3e9fd
[no ci] WIP
Sajjon Dec 17, 2024
01288fa
[no ci] WIP
Sajjon Dec 17, 2024
48874f9
wip
Sajjon Dec 17, 2024
f5319b1
[no ci] WIP
Sajjon Dec 17, 2024
ed84dba
[no ci] WIP
Sajjon Dec 17, 2024
8da5a2d
[no ci] WIP
Sajjon Dec 17, 2024
9a367a4
[no ci] WIP
Sajjon Dec 17, 2024
59d597f
[no ci] WIP
Sajjon Dec 17, 2024
e3de138
[no ci] WIP
Sajjon Dec 17, 2024
c0e0b42
[no ci] WIP
Sajjon Dec 17, 2024
2dc1484
[no ci] WIP
Sajjon Dec 17, 2024
64c17f8
[no ci] WIP
Sajjon Dec 18, 2024
4b477c6
[no ci] WIP
Sajjon Dec 18, 2024
8ae342f
[no ci] WIP
Sajjon Dec 18, 2024
1da8628
[no ci] WIP
Sajjon Dec 18, 2024
22c5855
[no ci] WIP
Sajjon Dec 18, 2024
41b1a11
[no ci] WIP
Sajjon Dec 18, 2024
4b8c041
[no ci] WIP
Sajjon Dec 18, 2024
ccf46b6
[no ci] WIP
Sajjon Dec 18, 2024
8e4c576
[no ci] WIP
Sajjon Dec 18, 2024
f269392
[no ci] WIP
Sajjon Dec 18, 2024
e8ff22f
[no ci] WIP
Sajjon Dec 18, 2024
632f907
[no ci] WIP
Sajjon Dec 18, 2024
9d89a31
clippy fix
Sajjon Dec 18, 2024
93bc314
remove prints
Sajjon Dec 18, 2024
18009fa
merge main
Sajjon Dec 18, 2024
d67aea5
add ROLA key to builder
Sajjon Dec 19, 2024
1ff2f54
fix failing tests (the hard ones - JSON remain... easy)
Sajjon Dec 19, 2024
b2ca5b9
fix all tests
Sajjon Dec 19, 2024
cd456d2
fixes
Sajjon Dec 19, 2024
e8d16d6
fix swift tests
Sajjon Dec 19, 2024
6715385
fmt
CyonAlexRDX Dec 19, 2024
5e29d97
Merge branch 'main' into ac/mix_persona_and_accounts_in_factor_instan…
Sajjon Dec 20, 2024
3a21681
merge
Sajjon Dec 20, 2024
d69166b
__OFFLINE_ONLY_securify_entities
CyonAlexRDX Dec 20, 2024
a8146bb
[no ci] WIP
Sajjon Dec 20, 2024
92fe297
fix last test
Sajjon Dec 20, 2024
8d0d109
add more tests
Sajjon Dec 20, 2024
4d0b9ab
review fixes
Sajjon Dec 20, 2024
ee84209
review polish
Sajjon Dec 20, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,9 @@ struct ShieldTests {

#expect(builder.validate() == .ConfirmationRoleMustHaveAtLeastOneFactor)
builder.addFactorSourceToConfirmationOverride(factorSourceId: .sampleArculus)

builder.setAuthenticationSigningFactor(new: .sampleDevice)

#expect(builder.validate() == nil)
#expect((try? builder.build()) != nil)
}
Expand Down Expand Up @@ -173,6 +176,8 @@ struct ShieldTests {
builder.addFactorSourceToRecoveryOverride(factorSourceId: .sampleArculus)
builder.addFactorSourceToConfirmationOverride(factorSourceId: .sampleArculusOther)

builder.setAuthenticationSigningFactor(new: .sampleDevice)

let shield = try! builder.build()

#expect(shield.matrixOfFactors.primaryRole.overrideFactors.isEmpty)
Expand Down Expand Up @@ -205,6 +210,8 @@ struct ShieldTests {
builder.removeFactorFromPrimary(factorSourceId: .sampleArculusOther)
builder.removeFactorFromRecovery(factorSourceId: .sampleLedgerOther)

builder.setAuthenticationSigningFactor(new: .sampleDevice)

// Validate
#expect(builder.validate() == nil)

Expand Down
2 changes: 1 addition & 1 deletion crates/sargon-uniffi/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]
name = "sargon-uniffi"
# Don't forget to update version in crates/sargon/Cargo.toml
version = "1.1.92"
version = "1.1.93"
edition = "2021"
build = "build.rs"

Expand Down
9 changes: 9 additions & 0 deletions crates/sargon-uniffi/src/core/error/common_error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -862,6 +862,15 @@ pub enum CommonError {

#[error("Cannot securify entity that has provisional security config")]
CannotSecurifyEntityHasProvisionalSecurityConfig = 10241,

#[error("Too few FactorInstances derived")]
TooFewFactorInstancesDerived = 10242,

#[error("Missing Authentication Signing FactorInstance mapping SecurityStructureOfFactorSources into SecurityStructureOfFactorInstances.")]
MissingRolaKeyForSecurityStructureOfFactorInstances = 10243,

#[error("SecurityStateAccessController address mismatch")]
SecurityStateAccessControllerAddressMismatch = 10244,
}

#[uniffi::export]
Expand Down
4 changes: 4 additions & 0 deletions crates/sargon-uniffi/src/keys_collector/derivation_purpose.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ pub enum DerivationPurpose {
/// for identity MFA
SecurifyingPersona,

/// When applying a security shield to accounts and personas mixed, initiates keys collection
/// for account MFA
SecurifyingAccountsAndPersonas,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need the other two purposes above? Does SecurifyingAccountsAndPersonas mean necessarily and, or it can happen to have only accounts or personas?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes if you only security account or only security personas those other are used.

But host will most likely map all variants to same Crowdin key anyway...

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the host API, it will send a list of AccountOrPersona right? and then in Sargon you map to specific purpose based on the input, I think I've seen this logic in this PR.


/// When adding a new factor source, initiates keys collection
/// for collecting various factor instances.
PreDerivingKeys,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,11 @@ impl SecurityShieldBuilder {
self.get(|builder| builder.get_name())
}

pub fn get_authentication_signing_factor(&self) -> Option<FactorSourceID> {
self.get(|builder| builder.get_authentication_signing_factor())
.map(|x| x.into())
}

pub fn get_primary_threshold_factors(&self) -> Vec<FactorSourceID> {
self.get_factors(|builder| builder.get_primary_threshold_factors())
}
Expand All @@ -135,6 +140,17 @@ impl SecurityShieldBuilder {
self.set(|builder| builder.set_name(&name));
}

pub fn set_authentication_signing_factor(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For other factors we seem to follow add/remove, any reason to not do the same here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add / remove makes sense when we support multiple factors. This is always one single

&self,
new: Option<FactorSourceID>,
) {
self.set(|builder| {
builder.set_authentication_signing_factor(
new.clone().map(|x| x.into_internal()),
)
});
}

pub fn remove_factor_from_all_roles(
&self,
factor_source_id: FactorSourceID,
Expand Down Expand Up @@ -756,6 +772,18 @@ mod tests {
sut.remove_factor_from_confirmation(f.clone());
assert_eq!(xs, sut.get_confirmation_factors());

assert_eq!(
sut.validate().unwrap(),
SecurityShieldBuilderInvalidReason::MissingAuthSigningFactor
);
sut.set_authentication_signing_factor(Some(
FactorSourceID::sample_device_other(),
));
assert_eq!(
sut.get_authentication_signing_factor(),
Some(FactorSourceID::sample_device_other())
);

let v0 = sut.validate();
let v1 = sut.validate(); // can call validate many times!
assert_eq!(v0, v1);
Expand All @@ -764,6 +792,10 @@ mod tests {
let shield = sut.build().unwrap(); // can call build many times!
assert_eq!(shield0, shield);

assert_eq!(
shield.authentication_signing_factor,
FactorSourceID::sample_device_other()
);
assert_eq!(shield.metadata.display_name.value, "S.H.I.E.L.D.");
assert_eq!(
shield.matrix_of_factors.primary_role.override_factors,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ use thiserror::Error as ThisError;
Clone, Debug, ThisError, PartialEq, InternalConversion, uniffi::Error,
)]
pub enum SecurityShieldBuilderInvalidReason {
#[error("Auth Signing Factor Missing")]
MissingAuthSigningFactor,

#[error("Shield name is invalid")]
ShieldNameInvalid,

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ pub struct SecurityStructureOfFactorSourceIDs {
/// The structure of factors to use for certain roles, Primary, Recovery
/// and Confirmation role.
pub matrix_of_factors: MatrixOfFactorSourceIDs,

/// The factor to use for authentication signing aka true Rola Key.
pub authentication_signing_factor: FactorSourceID,
}

delegate_debug_into!(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ pub struct SecurityStructureOfFactorSources {
/// The structure of factors to use for certain roles, Primary, Recovery
/// and Confirmation role.
pub matrix_of_factors: MatrixOfFactorSources,

/// The factor to use for authentication signing aka true Rola Key.
pub authentication_signing_factor: FactorSource,
}

#[uniffi::export]
Expand Down
2 changes: 1 addition & 1 deletion crates/sargon/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]
name = "sargon"
# Don't forget to update version in crates/sargon-uniffi/Cargo.toml
version = "1.1.92"
version = "1.1.93"
edition = "2021"
build = "build.rs"

Expand Down
9 changes: 9 additions & 0 deletions crates/sargon/src/core/error/common_error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -859,6 +859,15 @@ pub enum CommonError {

#[error("Cannot securify entity that has provisional security config")]
CannotSecurifyEntityHasProvisionalSecurityConfig = 10241,

#[error("Too few FactorInstances derived")]
TooFewFactorInstancesDerived = 10242,

#[error("Missing Authentication Signing FactorInstance mapping SecurityStructureOfFactorSources into SecurityStructureOfFactorInstances.")]
MissingRolaKeyForSecurityStructureOfFactorInstances = 10243,

#[error("SecurityStateAccessController address mismatch")]
SecurityStateAccessControllerAddressMismatch = 10244,
}

impl CommonError {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,6 @@ mod tests {
fn public_key() {
let private_key = SUT::sample();
let public_key = private_key.public_key();
println!("{:?}", public_key);
assert_eq!(
public_key,
KeyAgreementPublicKey::from_hex("8679bc1fe3210b2ce84793668b05218fdc4c220bc05387b7d2ac0d4c7b7c5d10".to_owned())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,12 @@ pub enum DerivationPreset {
#[debug("A-MFA")]
AccountMfa,

/// Used to form DerivationPaths used to derive FactorInstances
/// for Authentication Signing (Securified) for accounts
/// `(EntityKind::Account, KeySpace::Securified, KeyKind::AuthenticationSigning)`
#[debug("A-Rola")]
AccountRola,

/// Used to form DerivationPaths used to derive FactorInstances
/// for "veci": Virtual Entity Creating (Factor)Instance for personas.
/// `(EntityKind::Identity, KeySpace::Unsecurified, KeyKind::TransactionSigning)`
Expand All @@ -35,6 +41,12 @@ pub enum DerivationPreset {
/// `(EntityKind::Identity, KeySpace::Securified, KeyKind::TransactionSigning)`
#[debug("I-MFA")]
IdentityMfa,

/// Used to form DerivationPaths used to derive FactorInstances
/// for Authentication Signing (Securified) for peresonas
/// `(EntityKind::Identity, KeySpace::Securified, KeyKind::AuthenticationSigning)`
#[debug("I-Rola")]
IdentityRola,
}

// =============
Expand Down Expand Up @@ -63,6 +75,15 @@ impl DerivationPreset {
CAP26EntityKind::Identity => Self::IdentityMfa,
}
}

/// Selects a `DerivationPreset` for MFA based on `CAP26EntityKind`,
/// i.e. either `DerivationPreset::AccountRola` or `DerivationPreset::IdentityRola`.
pub fn rola_entity_kind(entity_kind: CAP26EntityKind) -> Self {
match entity_kind {
CAP26EntityKind::Account => Self::AccountRola,
CAP26EntityKind::Identity => Self::IdentityRola,
}
}
}

// =============
Expand All @@ -72,8 +93,12 @@ impl DerivationPreset {
/// Returns the `CAP26EntityKind` of the `DerivationPreset`.
pub fn entity_kind(&self) -> CAP26EntityKind {
match self {
Self::AccountVeci | Self::AccountMfa => CAP26EntityKind::Account,
Self::IdentityVeci | Self::IdentityMfa => CAP26EntityKind::Identity,
Self::AccountVeci | Self::AccountMfa | Self::AccountRola => {
CAP26EntityKind::Account
}
Self::IdentityVeci | Self::IdentityMfa | Self::IdentityRola => {
CAP26EntityKind::Identity
}
}
}

Expand All @@ -84,6 +109,9 @@ impl DerivationPreset {
| Self::IdentityVeci
| Self::AccountMfa
| Self::IdentityMfa => CAP26KeyKind::TransactionSigning,
Self::AccountRola | Self::IdentityRola => {
CAP26KeyKind::AuthenticationSigning
}
}
}

Expand All @@ -96,7 +124,10 @@ impl DerivationPreset {
is_hardened: true,
}
}
Self::AccountMfa | Self::IdentityMfa => KeySpace::Securified,
Self::AccountMfa
| Self::IdentityMfa
| Self::AccountRola
| Self::IdentityRola => KeySpace::Securified,
}
}

Expand Down Expand Up @@ -136,4 +167,28 @@ mod tests {
fn inequality() {
assert_ne!(SUT::sample(), SUT::sample_other());
}

#[test]
fn test_mfa_entity_kind() {
assert_eq!(
SUT::mfa_entity_kind(CAP26EntityKind::Account),
SUT::AccountMfa
);
assert_eq!(
SUT::mfa_entity_kind(CAP26EntityKind::Identity),
SUT::IdentityMfa
);
}

#[test]
fn test_rola_entity_kind() {
assert_eq!(
SUT::rola_entity_kind(CAP26EntityKind::Account),
SUT::AccountRola
);
assert_eq!(
SUT::rola_entity_kind(CAP26EntityKind::Identity),
SUT::IdentityRola
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,16 @@ impl TryFrom<IndexAgnosticPath> for DerivationPreset {
CAP26KeyKind::TransactionSigning,
KeySpace::Securified,
) => Ok(DerivationPreset::IdentityMfa),
(
CAP26EntityKind::Account,
CAP26KeyKind::AuthenticationSigning,
KeySpace::Securified,
) => Ok(DerivationPreset::AccountRola),
(
CAP26EntityKind::Identity,
CAP26KeyKind::AuthenticationSigning,
KeySpace::Securified,
) => Ok(DerivationPreset::IdentityRola),
_ => Err(CommonError::InvalidBIP32Path {
bad_value:
"Invalid combination of entity_kind, key_kind and key_space"
Expand Down Expand Up @@ -334,4 +344,39 @@ mod tests {
assert_json_value_fails::<SUT>(json!(""));
assert_json_value_fails::<SUT>(json!(" "));
}

#[test]
fn derivation_preset_rola_valid_account() {
let preset = DerivationPreset::try_from(SUT::new(
NetworkID::Mainnet,
CAP26EntityKind::Account,
CAP26KeyKind::AuthenticationSigning,
KeySpace::Securified,
))
.unwrap();
assert_eq!(preset, DerivationPreset::AccountRola);
}

#[test]
fn derivation_preset_rola_invalid_not_securified() {
let res = DerivationPreset::try_from(SUT::new(
NetworkID::Mainnet,
CAP26EntityKind::Account,
CAP26KeyKind::AuthenticationSigning,
KeySpace::Unsecurified { is_hardened: true },
));
assert!(matches!(res, Err(CommonError::InvalidBIP32Path { .. })));
}

#[test]
fn derivation_preset_rola_valid_identity() {
let preset = DerivationPreset::try_from(SUT::new(
NetworkID::Mainnet,
CAP26EntityKind::Identity,
CAP26KeyKind::AuthenticationSigning,
KeySpace::Securified,
))
.unwrap();
assert_eq!(preset, DerivationPreset::IdentityRola);
}
}
Loading
Loading