Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Module 0x1::jwks

JWK functions and structs.

Note: An important design constraint for this module is that the JWK consensus Rust code is unable to spawn a VM and make a Move function call. Instead, the JWK consensus Rust code will have to directly write some of the resources in this file. As a result, the structs in this file are declared so as to have a simple layout which is easily accessible in Rust.

use 0x1::bcs;
use 0x1::chain_status;
use 0x1::comparator;
use 0x1::config_buffer;
use 0x1::copyable_any;
use 0x1::error;
use 0x1::event;
use 0x1::features;
use 0x1::option;
use 0x1::reconfiguration;
use 0x1::signer;
use 0x1::string;
use 0x1::system_addresses;
use 0x1::vector;

Struct OIDCProvider

An OIDC provider.

struct OIDCProvider has copy, drop, store
Fields
name: vector<u8>
The utf-8 encoded issuer string. E.g., b"https://www.facebook.com".
config_url: vector<u8>
The ut8-8 encoded OpenID configuration URL of the provider. E.g., b"https://www.facebook.com/.well-known/openid-configuration/".

Resource SupportedOIDCProviders

A list of OIDC providers whose JWKs should be watched by validators. Maintained by governance proposals.

struct SupportedOIDCProviders has copy, drop, store, key
Fields
providers: vector<jwks::OIDCProvider>

Struct UnsupportedJWK

An JWK variant that represents the JWKs which were observed but not yet supported by Aptos. Observing UnsupportedJWKs means the providers adopted a new key type/format, and the system should be updated.

struct UnsupportedJWK has copy, drop, store
Fields
id: vector<u8>
payload: vector<u8>

Struct RSA_JWK

A JWK variant where kty is RSA.

struct RSA_JWK has copy, drop, store
Fields
kid: string::String
kty: string::String
alg: string::String
e: string::String
n: string::String

Struct JWK

A JSON web key.

struct JWK has copy, drop, store
Fields
variant: copyable_any::Any
A JWK variant packed as an Any. Currently the variant type is one of the following. - RSA_JWK - UnsupportedJWK

Struct ProviderJWKs

A provider and its JWKs.

struct ProviderJWKs has copy, drop, store
Fields
issuer: vector<u8>
The utf-8 encoding of the issuer string (e.g., "https://www.facebook.com").
version: u64
A version number is needed by JWK consensus to dedup the updates. e.g, when on chain version = 5, multiple nodes can propose an update with version = 6. Bumped every time the JWKs for the current issuer is updated. The Rust authenticator only uses the latest version.
jwks: vector<jwks::JWK>
Vector of JWK's sorted by their unique ID (from get_jwk_id) in dictionary order.

Struct AllProvidersJWKs

Multiple ProviderJWKs objects, indexed by issuer and key ID.

struct AllProvidersJWKs has copy, drop, store
Fields
entries: vector<jwks::ProviderJWKs>
Vector of ProviderJWKs sorted by ProviderJWKs::issuer in dictionary order.

Resource ObservedJWKs

The AllProvidersJWKs that validators observed and agreed on.

struct ObservedJWKs has copy, drop, store, key
Fields
jwks: jwks::AllProvidersJWKs

Struct ObservedJWKsUpdated

When ObservedJWKs is updated, this event is sent to resync the JWK consensus state in all validators.

#[event]
struct ObservedJWKsUpdated has drop, store
Fields
epoch: u64
jwks: jwks::AllProvidersJWKs

Struct Patch

A small edit or patch that is applied to a AllProvidersJWKs to obtain PatchedJWKs.

struct Patch has copy, drop, store
Fields
variant: copyable_any::Any
A Patch variant packed as an Any. Currently the variant type is one of the following. - PatchRemoveAll - PatchRemoveIssuer - PatchRemoveJWK - PatchUpsertJWK

Struct PatchRemoveAll

A Patch variant to remove all JWKs.

struct PatchRemoveAll has copy, drop, store
Fields
dummy_field: bool

Struct PatchRemoveIssuer

A Patch variant to remove an issuer and all its JWKs.

struct PatchRemoveIssuer has copy, drop, store
Fields
issuer: vector<u8>

Struct PatchRemoveJWK

A Patch variant to remove a specific JWK of an issuer.

struct PatchRemoveJWK has copy, drop, store
Fields
issuer: vector<u8>
jwk_id: vector<u8>

Struct PatchUpsertJWK

A Patch variant to upsert a JWK for an issuer.

struct PatchUpsertJWK has copy, drop, store
Fields
issuer: vector<u8>
jwk: jwks::JWK

Resource Patches

A sequence of Patch objects that are applied one by one to the ObservedJWKs.

Maintained by governance proposals.

struct Patches has key
Fields
patches: vector<jwks::Patch>

Resource PatchedJWKs

The result of applying the Patches to the ObservedJWKs. This is what applications should consume.

struct PatchedJWKs has drop, key
Fields
jwks: jwks::AllProvidersJWKs

Resource FederatedJWKs

JWKs for federated keyless accounts are stored in this resource.

struct FederatedJWKs has drop, key
Fields
jwks: jwks::AllProvidersJWKs

Constants

const DELETE_COMMAND_INDICATOR: vector<u8> = [84, 72, 73, 83, 95, 73, 83, 95, 65, 95, 68, 69, 76, 69, 84, 69, 95, 67, 79, 77, 77, 65, 78, 68];

const EFEDERATED_JWKS_TOO_LARGE: u64 = 8;

const EINSTALL_FEDERATED_JWKS_AT_APTOS_FRAMEWORK: u64 = 7;

const EINVALID_FEDERATED_JWK_SET: u64 = 9;

const EISSUER_NOT_FOUND: u64 = 5;

const EJWK_ID_NOT_FOUND: u64 = 6;

const ENATIVE_INCORRECT_VERSION: u64 = 259;

const ENATIVE_MISSING_RESOURCE_OBSERVED_JWKS: u64 = 258;

const ENATIVE_MISSING_RESOURCE_VALIDATOR_SET: u64 = 257;

const ENATIVE_MULTISIG_VERIFICATION_FAILED: u64 = 260;

const ENATIVE_NOT_ENOUGH_VOTING_POWER: u64 = 261;

const EUNEXPECTED_EPOCH: u64 = 1;

const EUNEXPECTED_VERSION: u64 = 2;

const EUNKNOWN_JWK_VARIANT: u64 = 4;

const EUNKNOWN_PATCH_VARIANT: u64 = 3;

We limit the size of a PatchedJWKs resource installed by a dapp owner for federated keyless accounts. Note: If too large, validators waste work reading it for invalid TXN signatures.

const MAX_FEDERATED_JWKS_SIZE_BYTES: u64 = 2048;

Function patch_federated_jwks

Called by a federated keyless dapp owner to install the JWKs for the federated OIDC provider (e.g., Auth0, AWS Cognito, etc). For type-safety, we explicitly use a struct FederatedJWKs { jwks: AllProviderJWKs } instead of reusing PatchedJWKs { jwks: AllProviderJWKs }, which is a JWK-consensus-specific struct.

public fun patch_federated_jwks(jwk_owner: &signer, patches: vector<jwks::Patch>)
Implementation
public fun patch_federated_jwks(jwk_owner: &signer, patches: vector<Patch>) acquires FederatedJWKs {
    // Prevents accidental calls in 0x1::jwks that install federated JWKs at the Aptos framework address.
    assert!(!system_addresses::is_aptos_framework_address(signer::address_of(jwk_owner)),
        error::invalid_argument(EINSTALL_FEDERATED_JWKS_AT_APTOS_FRAMEWORK)
    );

    let jwk_addr = signer::address_of(jwk_owner);
    if (!exists<FederatedJWKs>(jwk_addr)) {
        move_to(jwk_owner, FederatedJWKs { jwks: AllProvidersJWKs { entries: vector[] } });
    };

    let fed_jwks = borrow_global_mut<FederatedJWKs>(jwk_addr);
    patches.for_each_ref(|obj|{
        let patch: &Patch = obj;
        apply_patch(&mut fed_jwks.jwks, *patch);
    });

    // TODO: Can we check the size more efficiently instead of serializing it via BCS?
    let num_bytes = bcs::to_bytes(fed_jwks).length();
    assert!(num_bytes < MAX_FEDERATED_JWKS_SIZE_BYTES, error::invalid_argument(EFEDERATED_JWKS_TOO_LARGE));
}

Function update_federated_jwk_set

This can be called to install or update a set of JWKs for a federated OIDC provider. This function should be invoked to intially install a set of JWKs or to update a set of JWKs when a keypair is rotated.

The iss parameter is the value of the iss claim on the JWTs that are to be verified by the JWK set. kid_vec, alg_vec, e_vec, n_vec are String vectors of the JWK attributes kid, alg, e and n respectively. See https://datatracker.ietf.org/doc/html/rfc7517#section-4 for more details about the JWK attributes aforementioned.

For the example JWK set snapshot below containing 2 keys for Google found at https://www.googleapis.com/oauth2/v3/certs -

{
"keys": [
{
"alg": "RS256",
"use": "sig",
"kty": "RSA",
"n": "wNHgGSG5B5xOEQNFPW2p_6ZxZbfPoAU5VceBUuNwQWLop0ohW0vpoZLU1tAsq_S9s5iwy27rJw4EZAOGBR9oTRq1Y6Li5pDVJfmzyRNtmWCWndR-bPqhs_dkJU7MbGwcvfLsN9FSHESFrS9sfGtUX-lZfLoGux23TKdYV9EE-H-NDASxrVFUk2GWc3rL6UEMWrMnOqV9-tghybDU3fcRdNTDuXUr9qDYmhmNegYjYu4REGjqeSyIG1tuQxYpOBH-tohtcfGY-oRTS09kgsSS9Q5BRM4qqCkGP28WhlSf4ui0-norS0gKMMI1P_ZAGEsLn9p2TlYMpewvIuhjJs1thw",
"kid": "d7b939771a7800c413f90051012d975981916d71",
"e": "AQAB"
},
{
"kty": "RSA",
"kid": "b2620d5e7f132b52afe8875cdf3776c064249d04",
"alg": "RS256",
"n": "pi22xDdK2fz5gclIbDIGghLDYiRO56eW2GUcboeVlhbAuhuT5mlEYIevkxdPOg5n6qICePZiQSxkwcYMIZyLkZhSJ2d2M6Szx2gDtnAmee6o_tWdroKu0DjqwG8pZU693oLaIjLku3IK20lTs6-2TeH-pUYMjEqiFMhn-hb7wnvH_FuPTjgz9i0rEdw_Hf3Wk6CMypaUHi31y6twrMWq1jEbdQNl50EwH-RQmQ9bs3Wm9V9t-2-_Jzg3AT0Ny4zEDU7WXgN2DevM8_FVje4IgztNy29XUkeUctHsr-431_Iu23JIy6U4Kxn36X3RlVUKEkOMpkDD3kd81JPW4Ger_w",
"e": "AQAB",
"use": "sig"
}
]
}

We can call update_federated_jwk_set for Google’s iss - “https://accounts.google.com” and for each vector argument kid_vec, alg_vec, e_vec, n_vec, we set in index 0 the corresponding attribute in the first JWK and we set in index 1 the corresponding attribute in the second JWK as shown below.

use std::string::utf8;
aptos_framework::jwks::update_federated_jwk_set(
jwk_owner,
b"https://accounts.google.com",
vector[utf8(b"d7b939771a7800c413f90051012d975981916d71"), utf8(b"b2620d5e7f132b52afe8875cdf3776c064249d04")],
vector[utf8(b"RS256"), utf8(b"RS256")],
vector[utf8(b"AQAB"), utf8(b"AQAB")],
vector[
utf8(b"wNHgGSG5B5xOEQNFPW2p_6ZxZbfPoAU5VceBUuNwQWLop0ohW0vpoZLU1tAsq_S9s5iwy27rJw4EZAOGBR9oTRq1Y6Li5pDVJfmzyRNtmWCWndR-bPqhs_dkJU7MbGwcvfLsN9FSHESFrS9sfGtUX-lZfLoGux23TKdYV9EE-H-NDASxrVFUk2GWc3rL6UEMWrMnOqV9-tghybDU3fcRdNTDuXUr9qDYmhmNegYjYu4REGjqeSyIG1tuQxYpOBH-tohtcfGY-oRTS09kgsSS9Q5BRM4qqCkGP28WhlSf4ui0-norS0gKMMI1P_ZAGEsLn9p2TlYMpewvIuhjJs1thw"),
utf8(b"pi22xDdK2fz5gclIbDIGghLDYiRO56eW2GUcboeVlhbAuhuT5mlEYIevkxdPOg5n6qICePZiQSxkwcYMIZyLkZhSJ2d2M6Szx2gDtnAmee6o_tWdroKu0DjqwG8pZU693oLaIjLku3IK20lTs6-2TeH-pUYMjEqiFMhn-hb7wnvH_FuPTjgz9i0rEdw_Hf3Wk6CMypaUHi31y6twrMWq1jEbdQNl50EwH-RQmQ9bs3Wm9V9t-2-_Jzg3AT0Ny4zEDU7WXgN2DevM8_FVje4IgztNy29XUkeUctHsr-431_Iu23JIy6U4Kxn36X3RlVUKEkOMpkDD3kd81JPW4Ger_w")
]
)

See AIP-96 for more details about federated keyless - https://github.com/aptos-foundation/AIPs/blob/main/aips/aip-96.md

NOTE: Currently only RSA keys are supported.

public entry fun update_federated_jwk_set(jwk_owner: &signer, iss: vector<u8>, kid_vec: vector<string::String>, alg_vec: vector<string::String>, e_vec: vector<string::String>, n_vec: vector<string::String>)
Implementation
public entry fun update_federated_jwk_set(jwk_owner: &signer, iss: vector<u8>, kid_vec: vector<String>, alg_vec: vector<String>, e_vec: vector<String>, n_vec: vector<String>) acquires FederatedJWKs {
    assert!(!kid_vec.is_empty(), error::invalid_argument(EINVALID_FEDERATED_JWK_SET));
    let num_jwk = kid_vec.length<String>();
    assert!(alg_vec.length() == num_jwk , error::invalid_argument(EINVALID_FEDERATED_JWK_SET));
    assert!(e_vec.length() == num_jwk, error::invalid_argument(EINVALID_FEDERATED_JWK_SET));
    assert!(n_vec.length() == num_jwk, error::invalid_argument(EINVALID_FEDERATED_JWK_SET));

    let remove_all_patch = new_patch_remove_all();
    let patches = vector[remove_all_patch];
    while (!kid_vec.is_empty()) {
        let kid = kid_vec.pop_back();
        let alg = alg_vec.pop_back();
        let e = e_vec.pop_back();
        let n = n_vec.pop_back();
        let jwk = new_rsa_jwk(kid, alg, e, n);
        let patch = new_patch_upsert_jwk(iss, jwk);
        patches.push_back(patch)
    };
    patch_federated_jwks(jwk_owner, patches);
}

Function get_patched_jwk

Get a JWK by issuer and key ID from the PatchedJWKs. Abort if such a JWK does not exist. More convenient to call from Rust, since it does not wrap the JWK in an Option.

public fun get_patched_jwk(issuer: vector<u8>, jwk_id: vector<u8>): jwks::JWK
Implementation
public fun get_patched_jwk(issuer: vector<u8>, jwk_id: vector<u8>): JWK acquires PatchedJWKs {
    try_get_patched_jwk(issuer, jwk_id).extract()
}

Function try_get_patched_jwk

Get a JWK by issuer and key ID from the PatchedJWKs, if it exists. More convenient to call from Move, since it does not abort.

public fun try_get_patched_jwk(issuer: vector<u8>, jwk_id: vector<u8>): option::Option<jwks::JWK>
Implementation
public fun try_get_patched_jwk(issuer: vector<u8>, jwk_id: vector<u8>): Option<JWK> acquires PatchedJWKs {
    let jwks = &borrow_global<PatchedJWKs>(@aptos_framework).jwks;
    try_get_jwk_by_issuer(jwks, issuer, jwk_id)
}

Function upsert_oidc_provider

Deprecated by upsert_oidc_provider_for_next_epoch().

TODO: update all the tests that reference this function, then disable this function.

public fun upsert_oidc_provider(fx: &signer, name: vector<u8>, config_url: vector<u8>): option::Option<vector<u8>>
Implementation
public fun upsert_oidc_provider(fx: &signer, name: vector<u8>, config_url: vector<u8>): Option<vector<u8>> acquires SupportedOIDCProviders {
    system_addresses::assert_aptos_framework(fx);
    chain_status::assert_genesis();

    let provider_set = borrow_global_mut<SupportedOIDCProviders>(@aptos_framework);

    let old_config_url= remove_oidc_provider_internal(provider_set, name);
    provider_set.providers.push_back(OIDCProvider { name, config_url });
    old_config_url
}

Function upsert_oidc_provider_for_next_epoch

Used in on-chain governances to update the supported OIDC providers, effective starting next epoch. Example usage:

aptos_framework::jwks::upsert_oidc_provider_for_next_epoch(
&framework_signer,
b"https://accounts.google.com",
b"https://accounts.google.com/.well-known/openid-configuration"
);
aptos_framework::aptos_governance::reconfigure(&framework_signer);
public fun upsert_oidc_provider_for_next_epoch(fx: &signer, name: vector<u8>, config_url: vector<u8>): option::Option<vector<u8>>
Implementation
public fun upsert_oidc_provider_for_next_epoch(fx: &signer, name: vector<u8>, config_url: vector<u8>): Option<vector<u8>> acquires SupportedOIDCProviders {
    system_addresses::assert_aptos_framework(fx);

    let provider_set = if (config_buffer::does_exist<SupportedOIDCProviders>()) {
        config_buffer::extract_v2<SupportedOIDCProviders>()
    } else {
        *borrow_global<SupportedOIDCProviders>(@aptos_framework)
    };

    let old_config_url = remove_oidc_provider_internal(&mut provider_set, name);
    provider_set.providers.push_back(OIDCProvider { name, config_url });
    config_buffer::upsert(provider_set);
    old_config_url
}

Function remove_oidc_provider

Deprecated by remove_oidc_provider_for_next_epoch().

TODO: update all the tests that reference this function, then disable this function.

public fun remove_oidc_provider(fx: &signer, name: vector<u8>): option::Option<vector<u8>>
Implementation
public fun remove_oidc_provider(fx: &signer, name: vector<u8>): Option<vector<u8>> acquires SupportedOIDCProviders {
    system_addresses::assert_aptos_framework(fx);
    chain_status::assert_genesis();

    let provider_set = borrow_global_mut<SupportedOIDCProviders>(@aptos_framework);
    remove_oidc_provider_internal(provider_set, name)
}

Function remove_oidc_provider_for_next_epoch

Used in on-chain governances to update the supported OIDC providers, effective starting next epoch. Example usage:

aptos_framework::jwks::remove_oidc_provider_for_next_epoch(
&framework_signer,
b"https://accounts.google.com",
);
aptos_framework::aptos_governance::reconfigure(&framework_signer);
public fun remove_oidc_provider_for_next_epoch(fx: &signer, name: vector<u8>): option::Option<vector<u8>>
Implementation
public fun remove_oidc_provider_for_next_epoch(fx: &signer, name: vector<u8>): Option<vector<u8>> acquires SupportedOIDCProviders {
    system_addresses::assert_aptos_framework(fx);

    let provider_set = if (config_buffer::does_exist<SupportedOIDCProviders>()) {
        config_buffer::extract_v2<SupportedOIDCProviders>()
    } else {
        *borrow_global<SupportedOIDCProviders>(@aptos_framework)
    };
    let ret = remove_oidc_provider_internal(&mut provider_set, name);
    config_buffer::upsert(provider_set);
    ret
}

Function on_new_epoch

Only used in reconfigurations to apply the pending SupportedOIDCProviders, if there is any.

public(friend) fun on_new_epoch(framework: &signer)
Implementation
public(friend) fun on_new_epoch(framework: &signer) acquires SupportedOIDCProviders {
    system_addresses::assert_aptos_framework(framework);
    if (config_buffer::does_exist<SupportedOIDCProviders>()) {
        let new_config = config_buffer::extract_v2<SupportedOIDCProviders>();
        if (exists<SupportedOIDCProviders>(@aptos_framework)) {
            *borrow_global_mut<SupportedOIDCProviders>(@aptos_framework) = new_config;
        } else {
            move_to(framework, new_config);
        }
    }
}

Function set_patches

Set the Patches. Only called in governance proposals.

public fun set_patches(fx: &signer, patches: vector<jwks::Patch>)
Implementation
public fun set_patches(fx: &signer, patches: vector<Patch>) acquires Patches, PatchedJWKs, ObservedJWKs {
    system_addresses::assert_aptos_framework(fx);
    borrow_global_mut<Patches>(@aptos_framework).patches = patches;
    regenerate_patched_jwks();
}

Function new_patch_remove_all

Create a Patch that removes all entries.

public fun new_patch_remove_all(): jwks::Patch
Implementation
public fun new_patch_remove_all(): Patch {
    Patch {
        variant: copyable_any::pack(PatchRemoveAll {}),
    }
}

Function new_patch_remove_issuer

Create a Patch that removes the entry of a given issuer, if exists.

public fun new_patch_remove_issuer(issuer: vector<u8>): jwks::Patch
Implementation
public fun new_patch_remove_issuer(issuer: vector<u8>): Patch {
    Patch {
        variant: copyable_any::pack(PatchRemoveIssuer { issuer }),
    }
}

Function new_patch_remove_jwk

Create a Patch that removes the entry of a given issuer, if exists.

public fun new_patch_remove_jwk(issuer: vector<u8>, jwk_id: vector<u8>): jwks::Patch
Implementation
public fun new_patch_remove_jwk(issuer: vector<u8>, jwk_id: vector<u8>): Patch {
    Patch {
        variant: copyable_any::pack(PatchRemoveJWK { issuer, jwk_id })
    }
}

Function new_patch_upsert_jwk

Create a Patch that upserts a JWK into an issuer’s JWK set.

public fun new_patch_upsert_jwk(issuer: vector<u8>, jwk: jwks::JWK): jwks::Patch
Implementation
public fun new_patch_upsert_jwk(issuer: vector<u8>, jwk: JWK): Patch {
    Patch {
        variant: copyable_any::pack(PatchUpsertJWK { issuer, jwk })
    }
}

Function new_rsa_jwk

Create a JWK of variant RSA_JWK.

public fun new_rsa_jwk(kid: string::String, alg: string::String, e: string::String, n: string::String): jwks::JWK
Implementation
public fun new_rsa_jwk(kid: String, alg: String, e: String, n: String): JWK {
    JWK {
        variant: copyable_any::pack(RSA_JWK {
            kid,
            kty: utf8(b"RSA"),
            e,
            n,
            alg,
        }),
    }
}

Function new_unsupported_jwk

Create a JWK of variant UnsupportedJWK.

public fun new_unsupported_jwk(id: vector<u8>, payload: vector<u8>): jwks::JWK
Implementation
public fun new_unsupported_jwk(id: vector<u8>, payload: vector<u8>): JWK {
    JWK {
        variant: copyable_any::pack(UnsupportedJWK { id, payload })
    }
}

Function initialize

Initialize some JWK resources. Should only be invoked by genesis.

public fun initialize(fx: &signer)
Implementation
public fun initialize(fx: &signer) {
    system_addresses::assert_aptos_framework(fx);
    move_to(fx, SupportedOIDCProviders { providers: vector[] });
    move_to(fx, ObservedJWKs { jwks: AllProvidersJWKs { entries: vector[] } });
    move_to(fx, Patches { patches: vector[] });
    move_to(fx, PatchedJWKs { jwks: AllProvidersJWKs { entries: vector[] } });
}

Function initialize_with_defaults

Initialize some JWK resources. Should only be invoked by genesis.

public fun initialize_with_defaults(fx: &signer, providers: vector<jwks::OIDCProvider>, patches: vector<jwks::Patch>)
Implementation
public fun initialize_with_defaults(fx: &signer, providers: vector<OIDCProvider>, patches: vector<Patch>) {
    system_addresses::assert_aptos_framework(fx);
    move_to(fx, SupportedOIDCProviders { providers });
    move_to(fx, ObservedJWKs { jwks: AllProvidersJWKs { entries: vector[] } });
    move_to(fx, Patches { patches });
    move_to(fx, PatchedJWKs { jwks: AllProvidersJWKs { entries: vector[] } });
    regenerate_patched_jwks();
}

Function remove_oidc_provider_internal

Helper function that removes an OIDC provider from the SupportedOIDCProviders. Returns the old config URL of the provider, if any, as an Option.

fun remove_oidc_provider_internal(provider_set: &mut jwks::SupportedOIDCProviders, name: vector<u8>): option::Option<vector<u8>>
Implementation
fun remove_oidc_provider_internal(provider_set: &mut SupportedOIDCProviders, name: vector<u8>): Option<vector<u8>> {
    let (name_exists, idx) = provider_set.providers.find(|obj| {
        let provider: &OIDCProvider = obj;
        provider.name == name
    });

    if (name_exists) {
        let old_provider = provider_set.providers.swap_remove(idx);
        option::some(old_provider.config_url)
    } else {
        option::none()
    }
}

Function upsert_into_observed_jwks

Only used by validators to publish their observed JWK update.

NOTE: It is assumed verification has been done to ensure each update is quorum-certified, and its version equals to the on-chain version + 1.

public fun upsert_into_observed_jwks(fx: &signer, provider_jwks_vec: vector<jwks::ProviderJWKs>)
Implementation
public fun upsert_into_observed_jwks(fx: &signer, provider_jwks_vec: vector<ProviderJWKs>) acquires ObservedJWKs, PatchedJWKs, Patches {
    system_addresses::assert_aptos_framework(fx);
    let observed_jwks = borrow_global_mut<ObservedJWKs>(@aptos_framework);

    if (features::is_jwk_consensus_per_key_mode_enabled()) {
        provider_jwks_vec.for_each(|proposed_provider_jwks|{
            let maybe_cur_issuer_jwks = remove_issuer(&mut observed_jwks.jwks, proposed_provider_jwks.issuer);
            let cur_issuer_jwks = if (option::is_some(&maybe_cur_issuer_jwks)) {
                option::extract(&mut maybe_cur_issuer_jwks)
            } else {
                ProviderJWKs {
                    issuer: proposed_provider_jwks.issuer,
                    version: 0,
                    jwks: vector[],
                }
            };
            assert!(cur_issuer_jwks.version + 1 == proposed_provider_jwks.version, error::invalid_argument(EUNEXPECTED_VERSION));
            vector::for_each(proposed_provider_jwks.jwks, |jwk|{
                let variant_type_name = *string::bytes(copyable_any::type_name(&jwk.variant));
                let is_delete = if (variant_type_name == b"0x1::jwks::UnsupportedJWK") {
                    let repr = copyable_any::unpack<UnsupportedJWK>(jwk.variant);
                    &repr.payload == &DELETE_COMMAND_INDICATOR
                } else {
                    false
                };
                if (is_delete) {
                    remove_jwk(&mut cur_issuer_jwks, get_jwk_id(&jwk));
                } else {
                    upsert_jwk(&mut cur_issuer_jwks, jwk);
                }
            });
            cur_issuer_jwks.version += 1;
            upsert_provider_jwks(&mut observed_jwks.jwks, cur_issuer_jwks);
        });
    } else {
        provider_jwks_vec.for_each(|provider_jwks| {
            upsert_provider_jwks(&mut observed_jwks.jwks, provider_jwks);
        });
    };

    let epoch = reconfiguration::current_epoch();
    emit(ObservedJWKsUpdated { epoch, jwks: observed_jwks.jwks });
    regenerate_patched_jwks();
}

Function remove_issuer_from_observed_jwks

Only used by governance to delete an issuer from ObservedJWKs, if it exists.

Return the potentially existing ProviderJWKs of the given issuer.

public fun remove_issuer_from_observed_jwks(fx: &signer, issuer: vector<u8>): option::Option<jwks::ProviderJWKs>
Implementation
public fun remove_issuer_from_observed_jwks(fx: &signer, issuer: vector<u8>): Option<ProviderJWKs> acquires ObservedJWKs, PatchedJWKs, Patches {
    system_addresses::assert_aptos_framework(fx);
    let observed_jwks = borrow_global_mut<ObservedJWKs>(@aptos_framework);
    let old_value = remove_issuer(&mut observed_jwks.jwks, issuer);

    let epoch = reconfiguration::current_epoch();
    emit(ObservedJWKsUpdated { epoch, jwks: observed_jwks.jwks });
    regenerate_patched_jwks();

    old_value
}

Function regenerate_patched_jwks

Regenerate PatchedJWKs from ObservedJWKs and Patches and save the result.

fun regenerate_patched_jwks()
Implementation
fun regenerate_patched_jwks() acquires PatchedJWKs, Patches, ObservedJWKs {
    let jwks = borrow_global<ObservedJWKs>(@aptos_framework).jwks;
    let patches = borrow_global<Patches>(@aptos_framework);
    patches.patches.for_each_ref(|obj|{
        let patch: &Patch = obj;
        apply_patch(&mut jwks, *patch);
    });
    *borrow_global_mut<PatchedJWKs>(@aptos_framework) = PatchedJWKs { jwks };
}

Function try_get_jwk_by_issuer

Get a JWK by issuer and key ID from an AllProvidersJWKs, if it exists.

fun try_get_jwk_by_issuer(jwks: &jwks::AllProvidersJWKs, issuer: vector<u8>, jwk_id: vector<u8>): option::Option<jwks::JWK>
Implementation
fun try_get_jwk_by_issuer(jwks: &AllProvidersJWKs, issuer: vector<u8>, jwk_id: vector<u8>): Option<JWK> {
    let (issuer_found, index) = jwks.entries.find(|obj| {
        let provider_jwks: &ProviderJWKs = obj;
        issuer == provider_jwks.issuer
    });

    if (issuer_found) {
        try_get_jwk_by_id(jwks.entries.borrow(index), jwk_id)
    } else {
        option::none()
    }
}

Function try_get_jwk_by_id

Get a JWK by key ID from a ProviderJWKs, if it exists.

fun try_get_jwk_by_id(provider_jwks: &jwks::ProviderJWKs, jwk_id: vector<u8>): option::Option<jwks::JWK>
Implementation
fun try_get_jwk_by_id(provider_jwks: &ProviderJWKs, jwk_id: vector<u8>): Option<JWK> {
    let (jwk_id_found, index) = provider_jwks.jwks.find(|obj|{
        let jwk: &JWK = obj;
        jwk_id == get_jwk_id(jwk)
    });

    if (jwk_id_found) {
        option::some(provider_jwks.jwks[index])
    } else {
        option::none()
    }
}

Function get_jwk_id

Get the ID of a JWK.

fun get_jwk_id(jwk: &jwks::JWK): vector<u8>
Implementation
fun get_jwk_id(jwk: &JWK): vector<u8> {
    let variant_type_name = *jwk.variant.type_name().bytes();
    if (variant_type_name == b"0x1::jwks::RSA_JWK") {
        let rsa = jwk.variant.unpack<RSA_JWK>();
        *rsa.kid.bytes()
    } else if (variant_type_name == b"0x1::jwks::UnsupportedJWK") {
        let unsupported = jwk.variant.unpack<UnsupportedJWK>();
        unsupported.id
    } else {
        abort(error::invalid_argument(EUNKNOWN_JWK_VARIANT))
    }
}

Function upsert_provider_jwks

Upsert a ProviderJWKs into an AllProvidersJWKs. If this upsert replaced an existing entry, return it. Maintains the sorted-by-issuer invariant in AllProvidersJWKs.

fun upsert_provider_jwks(jwks: &mut jwks::AllProvidersJWKs, provider_jwks: jwks::ProviderJWKs): option::Option<jwks::ProviderJWKs>
Implementation
fun upsert_provider_jwks(jwks: &mut AllProvidersJWKs, provider_jwks: ProviderJWKs): Option<ProviderJWKs> {
    // NOTE: Using a linear-time search here because we do not expect too many providers.
    let found = false;
    let index = 0;
    let num_entries = jwks.entries.length();
    while (index < num_entries) {
        let cur_entry = jwks.entries.borrow(index);
        let comparison = compare_u8_vector(provider_jwks.issuer, cur_entry.issuer);
        if (comparison.is_greater_than()) {
            index += 1;
        } else {
            found = comparison.is_equal();
            break
        }
    };

    // Now if `found == true`, `index` points to the JWK we want to update/remove; otherwise, `index` points to
    // where we want to insert.
    let ret = if (found) {
        let entry = jwks.entries.borrow_mut(index);
        let old_entry = option::some(*entry);
        *entry = provider_jwks;
        old_entry
    } else {
        jwks.entries.insert(index, provider_jwks);
        option::none()
    };

    ret
}

Function remove_issuer

Remove the entry of an issuer from a AllProvidersJWKs and return the entry, if exists. Maintains the sorted-by-issuer invariant in AllProvidersJWKs.

fun remove_issuer(jwks: &mut jwks::AllProvidersJWKs, issuer: vector<u8>): option::Option<jwks::ProviderJWKs>
Implementation
fun remove_issuer(jwks: &mut AllProvidersJWKs, issuer: vector<u8>): Option<ProviderJWKs> {
    let (found, index) = jwks.entries.find(|obj| {
        let provider_jwk_set: &ProviderJWKs = obj;
        provider_jwk_set.issuer == issuer
    });

    let ret = if (found) {
        option::some(jwks.entries.remove(index))
    } else {
        option::none()
    };

    ret
}

Function upsert_jwk

Upsert a JWK into a ProviderJWKs. If this upsert replaced an existing entry, return it.

fun upsert_jwk(set: &mut jwks::ProviderJWKs, jwk: jwks::JWK): option::Option<jwks::JWK>
Implementation
fun upsert_jwk(set: &mut ProviderJWKs, jwk: JWK): Option<JWK> {
    let found = false;
    let index = 0;
    let num_entries = set.jwks.length();
    while (index < num_entries) {
        let cur_entry = set.jwks.borrow(index);
        let comparison = compare_u8_vector(get_jwk_id(&jwk), get_jwk_id(cur_entry));
        if (comparison.is_greater_than()) {
            index += 1;
        } else {
            found = comparison.is_equal();
            break
        }
    };

    // Now if `found == true`, `index` points to the JWK we want to update/remove; otherwise, `index` points to
    // where we want to insert.
    let ret = if (found) {
        let entry = set.jwks.borrow_mut(index);
        let old_entry = option::some(*entry);
        *entry = jwk;
        old_entry
    } else {
        set.jwks.insert(index, jwk);
        option::none()
    };

    ret
}

Function remove_jwk

Remove the entry of a key ID from a ProviderJWKs and return the entry, if exists.

fun remove_jwk(jwks: &mut jwks::ProviderJWKs, jwk_id: vector<u8>): option::Option<jwks::JWK>
Implementation
fun remove_jwk(jwks: &mut ProviderJWKs, jwk_id: vector<u8>): Option<JWK> {
    let (found, index) = jwks.jwks.find(|obj| {
        let jwk: &JWK = obj;
        jwk_id == get_jwk_id(jwk)
    });

    let ret = if (found) {
        option::some(jwks.jwks.remove(index))
    } else {
        option::none()
    };

    ret
}

Function apply_patch

Modify an AllProvidersJWKs object with a Patch. Maintains the sorted-by-issuer invariant in AllProvidersJWKs.

fun apply_patch(jwks: &mut jwks::AllProvidersJWKs, patch: jwks::Patch)
Implementation
fun apply_patch(jwks: &mut AllProvidersJWKs, patch: Patch) {
    let variant_type_name = *patch.variant.type_name().bytes();
    if (variant_type_name == b"0x1::jwks::PatchRemoveAll") {
        jwks.entries = vector[];
    } else if (variant_type_name == b"0x1::jwks::PatchRemoveIssuer") {
        let cmd = patch.variant.unpack<PatchRemoveIssuer>();
        remove_issuer(jwks, cmd.issuer);
    } else if (variant_type_name == b"0x1::jwks::PatchRemoveJWK") {
        let cmd = patch.variant.unpack<PatchRemoveJWK>();
        // TODO: This is inefficient: we remove the issuer, modify its JWKs & and reinsert the updated issuer. Why
        // not just update it in place?
        let existing_jwk_set = remove_issuer(jwks, cmd.issuer);
        if (existing_jwk_set.is_some()) {
            let jwk_set = existing_jwk_set.extract();
            remove_jwk(&mut jwk_set, cmd.jwk_id);
            upsert_provider_jwks(jwks, jwk_set);
        };
    } else if (variant_type_name == b"0x1::jwks::PatchUpsertJWK") {
        let cmd = patch.variant.unpack<PatchUpsertJWK>();
        // TODO: This is inefficient: we remove the issuer, modify its JWKs & and reinsert the updated issuer. Why
        // not just update it in place?
        let existing_jwk_set = remove_issuer(jwks, cmd.issuer);
        let jwk_set = if (existing_jwk_set.is_some()) {
            existing_jwk_set.extract()
        } else {
            ProviderJWKs {
                version: 0,
                issuer: cmd.issuer,
                jwks: vector[],
            }
        };
        upsert_jwk(&mut jwk_set, cmd.jwk);
        upsert_provider_jwks(jwks, jwk_set);
    } else {
        abort(std::error::invalid_argument(EUNKNOWN_PATCH_VARIANT))
    }
}

Specification

Function patch_federated_jwks

public fun patch_federated_jwks(jwk_owner: &signer, patches: vector<jwks::Patch>)
pragma verify_duration_estimate = 80;

Function update_federated_jwk_set

public entry fun update_federated_jwk_set(jwk_owner: &signer, iss: vector<u8>, kid_vec: vector<string::String>, alg_vec: vector<string::String>, e_vec: vector<string::String>, n_vec: vector<string::String>)
pragma verify_duration_estimate = 80;

Function get_patched_jwk

public fun get_patched_jwk(issuer: vector<u8>, jwk_id: vector<u8>): jwks::JWK
pragma verify_duration_estimate = 80;

Function try_get_patched_jwk

public fun try_get_patched_jwk(issuer: vector<u8>, jwk_id: vector<u8>): option::Option<jwks::JWK>
pragma verify_duration_estimate = 80;

Function upsert_oidc_provider_for_next_epoch

public fun upsert_oidc_provider_for_next_epoch(fx: &signer, name: vector<u8>, config_url: vector<u8>): option::Option<vector<u8>>
pragma verify = false;

Function remove_oidc_provider_for_next_epoch

public fun remove_oidc_provider_for_next_epoch(fx: &signer, name: vector<u8>): option::Option<vector<u8>>
pragma verify = false;

Function on_new_epoch

public(friend) fun on_new_epoch(framework: &signer)
requires @aptos_framework == std::signer::address_of(framework);
include config_buffer::OnNewEpochRequirement<SupportedOIDCProviders>;
aborts_if false;

Function set_patches

public fun set_patches(fx: &signer, patches: vector<jwks::Patch>)
pragma verify_duration_estimate = 80;

Function initialize_with_defaults

public fun initialize_with_defaults(fx: &signer, providers: vector<jwks::OIDCProvider>, patches: vector<jwks::Patch>)
pragma verify = false;

Function upsert_into_observed_jwks

public fun upsert_into_observed_jwks(fx: &signer, provider_jwks_vec: vector<jwks::ProviderJWKs>)
pragma verify_duration_estimate = 80;

Function remove_issuer_from_observed_jwks

public fun remove_issuer_from_observed_jwks(fx: &signer, issuer: vector<u8>): option::Option<jwks::ProviderJWKs>
pragma verify_duration_estimate = 80;

Function regenerate_patched_jwks

fun regenerate_patched_jwks()
pragma verify_duration_estimate = 80;

Function try_get_jwk_by_issuer

fun try_get_jwk_by_issuer(jwks: &jwks::AllProvidersJWKs, issuer: vector<u8>, jwk_id: vector<u8>): option::Option<jwks::JWK>
pragma verify_duration_estimate = 80;

Function try_get_jwk_by_id

fun try_get_jwk_by_id(provider_jwks: &jwks::ProviderJWKs, jwk_id: vector<u8>): option::Option<jwks::JWK>
pragma verify_duration_estimate = 80;

Function remove_issuer

fun remove_issuer(jwks: &mut jwks::AllProvidersJWKs, issuer: vector<u8>): option::Option<jwks::ProviderJWKs>
pragma opaque;
ensures option::is_none(result) <==> (forall jwk: ProviderJWKs where vector::spec_contains(old(jwks).entries, jwk): jwk.issuer != issuer);
ensures option::is_none(result) ==> old(jwks) == jwks;
ensures option::is_some(result) ==> vector::spec_contains(old(jwks).entries, option::borrow(result));

Function remove_jwk

fun remove_jwk(jwks: &mut jwks::ProviderJWKs, jwk_id: vector<u8>): option::Option<jwks::JWK>
pragma verify_duration_estimate = 80;

Function apply_patch

fun apply_patch(jwks: &mut jwks::AllProvidersJWKs, patch: jwks::Patch)
pragma verify_duration_estimate = 80;