Skip to content

RAM-LFE

RAM-LFE représente l'évaluation de la fonction laconique de la machine d'accès aléatoire. En Iroha, c'est la couche de fonction cachée générique pour les programmes dont la politique publique est en chaîne, mais dont la logique d'évaluation, le secret ou l'entrée brute ne devraient pas être écrits à l'état mondial. Il est utilisé par les flux d'identifiants SORA Nexus, tels que la recherche privée de téléphone ou de courrier électronique, et peut également être exposé en tant qu'assistant générique à l'exécution du programme Torii lorsqu'un profil de nœud permet les itinéraires face à l'application.

La chaîne stocke les métadonnées d'engagement de la politique et de vérification des reçus. Un résolveur ou Torii runtime évalue le programme caché, renvoie uniquement la sortie autorisée et attache un reçu que les clients, l'outillage de support ou les instructions du registre peuvent vérifier contre la politique enregistrée.

Nommage

La division des noms est importante:

DuréeLe sens .
ram_lfeL'abstraction externe de la fonction cachée: politiques du programme, engagements, reçus d'exécution et mode de vérification des reçus.
BFVLe schéma de cryptage homomorphe Brakerski/Fan-Vercauteren utilisé par les arrière-plans RAM-LFE à entrée cryptée.
ram_fhe_profileBFV - métadonnées spécifiques à la machine d'exécution cryptée programmée. Ce n'est pas un deuxième nom pour RAM-LFE.

Dans le modèle de données, RamLfeProgramPolicy et RamLfeExecutionReceipt sont des types RAM-LFE. Les paramètres BFV, les enveloppes de texte crypté et le profil du programme caché RAM-FHE appartiennent au backend d'exécution crypté utilisé par une politique.

Ce qu'il raconte

Une politique du programme RAM-LFE est enregistrée à l'échelle mondiale par program_id.

  • le compte du propriétaire qui peut activer, désactiver ou modifier autrement la politique
  • l'arrière-plan annoncé aux clients
  • le mode de vérification du reçu, soit signed ou proof;
  • un engagement pour les métadonnées cachées du programme et le secret de l'évaluateur
  • la clé publique de résolution pour les reçus signés
  • les métadonnées de saisie en cryptage public facultatives, telles que les paramètres BFV et ram_fhe_profile
  • un drapeau active qui contrôle si la police peut émettre de nouveaux reçus

Le secret caché, la valeur d'identifiant de texte clair et le corps du programme caché ne sont pas stockés dans l'état mondial. Les clients doivent traiter les engagements, les hashes opaques, les hashs de réception, les chiffres et les digests de programmes comme des valeurs protocoles opaques.

Rétrospectifs

Le support actuel RAM-LFE est axé sur trois identifiants back-end:

Retour en arrière .Utilisation
hkdf-sha3-512-prf-v1Évaluation liée à l'engagement PRF.
bfv-affine-sha3-256-v1BFV appuyée par l'évaluation secrète des affinités sur les espaces d'identification cryptés.
bfv-programmed-sha3-256-v1L'exécution programmée prise en charge par BFV sur les registres cryptés et les voies de mémoire.

Pour les politiques d'identification, l'arrière-plan programmé BFV est le chemin moderne important. Il permet aux portefeuilles de crypter les entrées normalisées localement, permet au résolveur d'évaluer sans voir un identifiant public dans la transaction, et renvoie un reçu qui lie le hash de sortie à la politique du programme enregistrée.

Mathématiques

Cette section décrit l'algèbre de niveau d'implémentation utilisée par le code actuel RAM-LFE. Ce n'est pas une preuve de sécurité; c'est la transcription déterministe et le modèle d'évaluation crypté sur lequel les politiques, les reçus et les clients doivent s'entendre.

Notation

Laissez-moi:

  • (H(m)) être Iroha Hash::new(m): Blake2b-32 sur m, le bit le moins important du byte final étant forcé à 1.
  • (N(x)) est le codage canonique Norito de x.
  • (a \parallel b) signifie la concaténation de chaîne en octets.
  • (\opérateurname{le64}(i)) être le codage petit-endian de 8 bytes d'un entier non signé.
  • (s) être le résolveur secret détenu à l'extérieur de l'état mondial.
  • (P) sont des paramètres d'ordre public.
  • (A) sont les données associées requises.
  • (x) sont des octets d'entrée normalisés ou une enveloppe d'entrée cryptée codée par Norito, selon le backend.

RAM-LFE utilise des haches séparées par domaine. Les formules ci-dessous nomment les domaines selon leur but; leurs chaînes de octets actuelles sont:

SymboleChaîne de domaine
(D_{\mathrm{policy}})iroha.ram_lfe.policy.hkdf_sha3_512_prf.v1
(D_{\mathrm{secret}})iroha.ram_lfe.policy_secret.hkdf_sha3_512_prf.v1
(D_{\mathrm{salt}})iroha.ram_lfe.hkdf_salt.hkdf_sha3_512_prf.v1
(D_{\mathrm{hkdf_opaque}})iroha.ram_lfe.opaque_info.hkdf_sha3_512_prf.v1
(D_{\mathrm{hkdf_receipt}})iroha.ram_lfe.receipt_info.hkdf_sha3_512_prf.v1
(D_{\mathrm{opaque}})iroha.ram_lfe.opaque_hash.hkdf_sha3_512_prf.v1
(D_{\mathrm{receipt}})iroha.ram_lfe.receipt_hash.hkdf_sha3_512_prf.v1
(D_{\mathrm{affine_circuit}})iroha.ram_lfe.bfv_affine.circuit.v1
(D_{\mathrm{affine_opaque}})iroha.ram_lfe.bfv_affine.opaque_hash.v1
(D_{\mathrm{affine_receipt}})iroha.ram_lfe.bfv_affine.receipt_hash.v1
(D_{\mathrm{program_memory}})iroha.ram_lfe.bfv_program.memory.v1
(D_{\mathrm{program_opaque}})iroha.ram_lfe.bfv_program.opaque_hash.v1
(D_{\mathrm{program_receipt}})iroha.ram_lfe.bfv_program.receipt_hash.v1
(D_{\mathrm{program_digest}})iroha.ram_lfe.bfv_program.digest.v1
(D_{\mathrm{output}})iroha.ram_lfe.output_hash.v1
(D_{\mathrm{id_opaque}})iroha.ram_lfe.identifier.opaque_hash.v1
(D_{\mathrm{id_receipt}})iroha.ram_lfe.identifier.receipt_hash.v1
(D_{\mathrm{bfv_keygen}})iroha.crypto.fhe.bfv.keygen.v1
(D_{\mathrm{bfv_encrypt}})iroha.crypto.fhe.bfv.encrypt.v1
(D_{\mathrm{id_keygen}})iroha.crypto.fhe.bfv.identifier.keygen.v1
(D_{\mathrm{id_slot}})iroha.crypto.fhe.bfv.identifier.slot.v1

L'engagement politique

L'engagement politique lie les paramètres publics et le secret de résolution cachée à un backend. Tout d'abord, le secret est commis séparément:

Cs=H(Dsecrets) C_s = H(D_{\mathrm{secret}} \parallel s)

Ensuite , la transcription complète de la politique est codée:

Tpolicy=N(backend,P,Cs) T_{\mathrm{policy}} = N(\mathrm{backend}, P, C_s)

et le hash de politique publié est:

policy_hash=H(DpolicyTpolicy) \mathrm{policy\_hash} = H(D_{\mathrm{policy}} \parallel T_{\mathrm{policy}})

Le PolicyCommitment en chaîne est:

(backend,policy_hash,P) (\mathrm{backend}, \mathrm{policy\_hash}, P)

L'évaluation recompte la même valeur à partir du secret de l'exécution. Si le hash recomputé diffère, l'évaluation échoue avec un désaccord d'engagement.

HKDF-SHA3-512 Retour en arrière

Pour hkdf-sha3-512-prf-v1, la sortie est l'entrée normalisée elle-même, mais l'identifiant opaque et le hachage de reçus sont des sorties liées secrètement PRF.

La transcription de la demande est:

Treq=N(policy_hash,P,A,x) T_{\mathrm{req}} = N(\mathrm{policy\_hash}, P, A, x)

Le sel HKDF et la clé de pseudorandom sont:

salt=Dsaltpolicy_hash \mathrm{salt} = D_{\mathrm{salt}} \parallel \mathrm{policy\_hash}

PRK=HKDF-ExtractSHA3-512(salt,s) \mathrm{PRK} = \operatorname{HKDF\text{-}Extract}_{\mathrm{SHA3\text{-}512}} (\mathrm{salt}, s)

Le matériau opaque est élargi et haché:

mo=HKDF-ExpandSHA3-512(PRK,Dhkdf_opaqueTreq,32) m_o = \operatorname{HKDF\text{-}Expand}_{\mathrm{SHA3\text{-}512}} (\mathrm{PRK}, D_{\mathrm{hkdf\_opaque}} \parallel T_{\mathrm{req}}, 32)

opaque_id=H(Dopaquemo) \mathrm{opaque\_id} = H(D_{\mathrm{opaque}} \parallel m_o)

Le matériau de réception lie également l'identifiant opaque:

mr=HKDF-ExpandSHA3-512(PRK,Dhkdf_receiptTreqopaque_id,32) m_r = \operatorname{HKDF\text{-}Expand}_{\mathrm{SHA3\text{-}512}} (\mathrm{PRK}, D_{\mathrm{hkdf\_receipt}} \parallel T_{\mathrm{req}} \parallel \mathrm{opaque\_id}, 32)

receipt_hash=H(Dreceiptmropaque_id) \mathrm{receipt\_hash} = H(D_{\mathrm{receipt}} \parallel m_r \parallel \mathrm{opaque\_id})

L' arrière-plan est de retour:

(output,opaque_id,receipt_hash)=(x,opaque_id,receipt_hash) (\mathrm{output}, \mathrm{opaque\_id}, \mathrm{receipt\_hash}) = (x, \mathrm{opaque\_id}, \mathrm{receipt\_hash})

Préparateur BFV

BFV est un schéma de cryptage homomorphe basé sur le réseau. "Homomorphe" signifie qu'un programme peut ajouter et multiplier des valeurs cryptées et, après décryptage, obtenir le même résultat que s'il avait effectué les ajouts et multiplication sur les valeurs du texte clair.

Pour RAM-LFE, BFV est utilisé comme mécanisme d'entrée crypté:

  1. Un portefeuille normalise une valeur privée, comme un numéro de téléphone ou une adresse e-mail.
  2. Le portefeuille transforme les octets en petits espaces entiers.
  3. Chaque slot est crypté avec la clé publique BFV du résolveur.
  4. Le temps d'exécution du résolveur évalue le programme caché sur ces chiffrements.
  5. Le temps d'exécution décrypte uniquement la sortie du programme caché et signe ou prouve un reçu.

BFV est l'arithmétique exacte des nombres entiers et non approximative. C'est pourquoi il est mieux adapté aux octets d'identification et aux petits modules Les calculs sont plus basés sur l'inference du modèle de point flottant. Iroha C' est le courant BFV l'utilisation, chaque fente cryptée porte un modulo de valeur scalaire (t), Le texte de chiffrement lui-même vit modulo un nombre entier beaucoup plus grand (q). L'écart entre (q) et (t) permet de déchiffrer le bruit introduit par les opérations de chiffrement et d'homomorphie.

Un texte de chiffrement BFV a deux composantes polynomielles:

c=(c0,c1) c=(c_0,c_1)

La clé secrète est un autre polynôme (s_k). Le décryptage combine les composants:

v=c0+c1sk v = c_0 + c_1s_k

Si le texte cryptographique a été correctement formé et que le bruit est encore suffisamment faible, (v) est proche du texte simple à l'échelle. La rotation récupère le coefficient de texte simple modulo (t).

Opération simpleL' opération de texte crypté
(m+n)Ajouter des composants de texte crypté.
(m+\alpha)Ajoutez une constante de texte clair à l'échelle dans (c_0).
(\alpha m)Écaillez les deux composants du texte crypté par (\alpha).
(mn)Multipliez les polynômes de texte chiffré, redimensionnez, puis relineez.

La multiplication est l'opération coûteuse. Un produit de deux chiffres à deux composants crée naturellement un chiffre à trois composants qui se décrypte avec (1), (s_k) et (s_k^2). Relinearization utilise une clé d'évaluation publiée pour replier le terme (s_k^2) dans un texte chiffré à deux composants normal. Cela maintient des ajouts et des multiplications ultérieurs en utilisant la même forme du texte chiffré.

BFV est également "nivelé": chaque opération cryptée consomme un budget de bruit. Cette mise en œuvre ne démarre pas les textes chiffrés pour rafraîchir ce budget. Au lieu de cela, RAM-LFE publie un petit ram_fhe_profile et accepte seulement une forme de programme cachée limitée. Cela maintient l'évaluation dans la profondeur prise en charge de l'ensemble de paramètres. Le profil programmé actuel permet un nombre fixe du registre, un nombre fixe des voies de mémoire et au plus une multiplication de texte chiffré par étape programmée.

Dans cette conception RAM-LFE, BFV cache l'entrée du client des données du registre public et des observateurs qui ne voient que la charge utile de la transaction ou de la route. Cela ne signifie pas que la chaîne exécute elle-même des programmes chiffrés arbitraires. Le résolveur Torii runtime possède toujours le matériel secret BFV, évalue le programme caché configuré, décrypte la sortie autorisée et atteste le résultat.

Le cas d'utilisation de l'identifiant choisit délibérément une simple représentation.

text
[length, byte_0, byte_1, ..., byte_n, 0, 0, ...]

Chaque élément est crypté comme son propre BFV texte chiffré scalaire. Cette forme rend la normalisation et la validation de l'enveloppe explicites, permet aux portefeuilles de créer des requêtes cryptées à partir de paramètres publics, et permet au résolveur de canoniser les entrées cryptées équivalentes dans une transcription stable de réception.

Modèle d'anneau BFV

Les arrière-plans BFV utilisent l'anneau polynomial négacyclique:

Rq=Zq[X]/(Xn+1) R_q = \mathbb{Z}_q[X] / (X^n + 1)

et anneaux de texte clair:

Rt=Zt[X]/(Xn+1) R_t = \mathbb{Z}_t[X] / (X^n + 1)

où:

  • (n) est polynomial_degree, une puissance de deux
  • (q) est ciphertext_modulus
  • (t) est plaintext_modulus
  • (q > t) et (t \mid q)
  • (\Delta = q/t)
  • (B = 2^{\mathrm{decomposition_base_log}})

Les vecteurs des coefficients de texte clair sont codés en écaillant chaque coefficient:

EncPlain(m)i=Δmimodq \operatorname{EncPlain}(m)_i = \Delta m_i \bmod q

Déchiffrement du centre-élévateur pour chaque coefficient de:

v=c0+c1skRq v = c_0 + c_1 s_k \in R_q

il le fait ensuite retourner à (R_t):

Dec(c)i=tcenterq(vi)qmodt \operatorname{Dec}(c)_i = \left\lfloor \frac{t \cdot \operatorname{center}_q(v_i)}{q} \right\rceil \bmod t

Ici (s_k) est le polynôme de clé secrète BFV, et non le résolveur secret extérieur RAM-LFE (s).

BFV Génération clé

Pour les entrées d'identifiants cryptés, le matériau clé BFV est déterminant par résolveur et les données secrètes associées:

σid=H(Did_keygenAs) \sigma_{\mathrm{id}} = H(D_{\mathrm{id\_keygen}} \parallel A \parallel s)

Le BFV RNG est semé comme suit:

ChaCha20Rng(H(Dbfv_keygenσid)) \operatorname{ChaCha20Rng}(H(D_{\mathrm{bfv\_keygen}} \parallel \sigma_{\mathrm{id}}))

Les échantillons de générateurs clés:

  • (s_k \in {-1,0,1}^n), représenté par le modulo (q)
  • (a \leftarrow R_q) uniformément
  • (e \in {-1,0,1}^n)

La clé publique est:

pk=(b,a),b=aske(modq) \mathrm{pk}=(b,a),\qquad b = -a s_k - e \pmod q

Pour la relinearisation, (s_k^2) doit être le produit de l'anneau dans (R_q). Pour chaque chiffre de base-(B) (j), prenez l'échantillon (a_j) uniformément et (e_j) à partir de la petite distribution, puis publiez:

rlkj=(bj,aj),bj=ajskej+Bjsk2(modq) \mathrm{rlk}_j=(b_j,a_j),\qquad b_j = -a_j s_k - e_j + B^j s_k^2 \pmod q

Le public BFV les métadonnées de la politique contiennent ((n,q,t,B)), la clé publique et max_input_bytes. Les États membres BFV La clé secrète et la clé de relinearisation restent dans le temps d'exécution du résolveur.

BFV Le chiffrement et les opérations

Pour chiffrer un polynôme de texte clair (m), l'implémentation produit une autre ChaCha20 RNG à partir de:

H(Dbfv_encryptseed) H(D_{\mathrm{bfv\_encrypt}} \parallel \mathrm{seed})

Il prélève des échantillons (u,e_1,e_2 \in {-1,0,1}^n) et calcule:

c0=bu+e1+EncPlain(m)(modq) c_0 = b u + e_1 + \operatorname{EncPlain}(m) \pmod q

c1=au+e2(modq) c_1 = a u + e_2 \pmod q

Le texte du chiffrement est (c=(c_0,c_1)).

L'addition homomorphe est composante:

c+d=(c0+d0, c1+d1)(modq) c+d=(c_0+d_0,\ c_1+d_1)\pmod q

L'ajout d'une échelle de texte clair (\alpha) au coefficient zéro ne change que (c_0):

c+α=(c0+Δα, c1)(modq) c+\alpha = (c_0 + \Delta\alpha,\ c_1)\pmod q

Multiplication par une échelle de texte clair (\alpha) équivaut aux deux composants:

αc=(αc0, αc1)(modq) \alpha c = (\alpha c_0,\ \alpha c_1)\pmod q

Pour deux textes de chiffrement (c=(c_0,c _1)) et (d=(d_0,d_1)), la multiplication du texte de chiffrement calcule d'abord un texte de chiffrage de trois tailles et mesure chaque coefficient en arrière par (t/q):

c~0=t(c0d0)qmodq \tilde c_0 = \left\lfloor \frac{t(c_0 d_0)}{q} \right\rceil \bmod q

c~1=t(c0d1+c1d0)qmodq \tilde c_1 = \left\lfloor \frac{t(c_0 d_1 + c_1 d_0)}{q} \right\rceil \bmod q

c~2=t(c1d1)qmodq \tilde c_2 = \left\lfloor \frac{t(c_1 d_1)}{q} \right\rceil \bmod q

Tous les produits ci-dessus sont des produits d'anneaux négacycliques dans (R_q). Puis (\tilde c_2) est décomposé en polynômes de base-(B):

c~2=jBjuj \tilde c_2 = \sum_j B^j u_j

et réallinérifiés:

c0=c~0+jujbj(modq) c'_0 = \tilde c_0 + \sum_j u_j b_j \pmod q

c1=c~1+jujaj(modq) c'_1 = \tilde c_1 + \sum_j u_j a_j \pmod q

Le résultat est encore une fois un texte chiffré à deux composants BFV.

Enveloppe de texte de chiffrement

Une chaîne d'entrée en octets d'identifiant:

x=(x0,,x1) x=(x_0,\ldots,x_{\ell-1})

est codé dans des espaces scalaires:

m0= m_0 = \ell

mi+1=xi,0i< m_{i+1}=x_i,\qquad 0 \le i < \ell

et toutes les fentes restantes sont zéro jusqu'à max_input_bytes + 1. Chaque fente scalaire est cryptée comme le polynôme de texte clair à coefficient zéro ([m_i]). La graine de chiffrement par fente est:

σi=H(Did_slotseedle64(i)) \sigma_i = H(D_{\mathrm{id\_slot}} \parallel \mathrm{seed} \parallel \operatorname{le64}(i))

L'enveloppe d'identifiant cryptée est:

(BFV.Encpk([m0];σ0),,BFV.Encpk([mM];σM)) (\operatorname{BFV.Enc}_{\mathrm{pk}}([m_0];\sigma_0),\ldots, \operatorname{BFV.Enc}_{\mathrm{pk}}([m_M];\sigma_M))

où (M=\mathrm{max_input_bytes}).

BFV Retour en arrière

Pour bfv-affine-sha3-256-v1, le temps d'exécution dérive d'abord du matériau clé BFV de (s) et (A). Les paramètres publics dérivés doivent correspondre exactement aux paramètres publiques engagés en chaîne.

La graine de circuit affine est:

σaffine=H(Daffine_circuitspolicy_hashA) \sigma_{\mathrm{affine}} = H(D_{\mathrm{affine\_circuit}} \parallel s \parallel \mathrm{policy\_hash} \parallel A)

À partir de cette graine, les échantillons en cours d'exécution, modulo (t), un circuit affini de 32 rangées:

yj=bj+iwj,imi(modt),0j<32 y_j = b_j + \sum_i w_{j,i} m_i \pmod t, \qquad 0 \le j < 32

où (m_i) sont les fentes d'identifiant décryptées. Homomorphiquement, il calcule la même valeur sur des textes cryptés:

Cj=bj+iwj,iCi C_j = b_j + \sum_i w_{j,i} C_i

Le résolveur décrypte chaque (C_j), exige que tous les coefficients de texte ordinaire arrière soient zéro, convertit les valeurs du coefficient-zéro en octets et forme:

O=(y0,,y31) O=(y_0,\ldots,y_{31})

Puis:

opaque_id=H(Daffine_opaquepolicy_hashO) \mathrm{opaque\_id} = H(D_{\mathrm{affine\_opaque}} \parallel \mathrm{policy\_hash} \parallel O)

receipt_hash=H(Daffine_receiptpolicy_hashOopaque_id) \mathrm{receipt\_hash} = H(D_{\mathrm{affine\_receipt}} \parallel \mathrm{policy\_hash} \parallel O \parallel \mathrm{opaque\_id})

BFV L'arrière-plan programmé

Pour bfv-programmed-sha3-256-v1, les paramètres publics comprennent les paramèters de cryptage de l'identifiant BFV, ainsi qu'un digeste du programme caché:

program_digest=H(Dprogram_digestN(program)) \mathrm{program\_digest} = H(D_{\mathrm{program\_digest}} \parallel N(\mathrm{program}))

Le profil actuel RAM-FHE est le suivant:

champLa valeur
profile_version1
register_count4
memory_lane_count32
ciphertext_mul_per_step1
encrypted_input_moderesolver_canonicalized_envelope_v1
min_ciphertext_modulus(2^{52})

L'entrée de texte clair présentée à Torii est cryptée dans la même enveloppe BFV avant l'exécution. La semence déterministe de ce chiffrement du côté serveur est:

H("iroha.ram_lfe.execute.plaintext_bfv.v1"N(program_id)x) H( \texttt{"iroha.ram\_lfe.execute.plaintext\_bfv.v1"} \parallel N(\mathrm{program\_id}) \parallel x )

Pour les entrées cryptées fournies à l'extérieur, le résolveur décrypte l'enveloppe d'identifiant et la recrypte sur cette enveloppe déterministique avant de l'exécuter. Cette canonisation maintient les hashes de réception stables sur des chiffres sémantiquement égaux BFV.

Les voies de mémoire cryptées initiales sont dérivées des:

σmem=H(Dprogram_memoryspolicy_hashAle64(0)) \sigma_{\mathrm{mem}} = H(D_{\mathrm{program\_memory}} \parallel s \parallel \mathrm{policy\_hash} \parallel A \parallel \operatorname{le64}(0))

Pour chacune des 32 voies, les échantillons de temps d'exécution (r_j \in [0,t)) et stocke un texte chiffré BFV cryptant (r_j). Le programme caché exécute ensuite sur des registres cryptés et la mémoire cryptée:

InstructionL' algèbre
LoadInput(dst, i)(R_{\mathrm{dst}} \leftarrow C_i)
LoadState(dst, j)(R_{\mathrm{dst}} \leftarrow S_j)
StoreState(j, src)(S_j \leftarrow R_{\mathrm{src}})
LoadConst(dst, a)R _ {\mathrm{dst}} \leftrow \operatorname{Enc}(a))
Add(dst, a, b)(R_{\mathrm{dst}} \leftarrow R_a + R_b)
AddPlain(dst, src, a)(R_{\mathrm{dst}} \leftarrow R_{\mathrm{src}} + a)
SubPlain(dst, src, a)(R_{\mathrm{dst}} \leftarrow R_{\mathrm{src}} - a)
MulPlain(dst, src, a)(R_{\mathrm{dst}} \leftarrow aR_{\mathrm{src}})
Mul(dst, a, b)(R_{\mathrm{dst}} \leftarrow R_aR_b), puis relineer
SelectEqZero(dst, cond, z, nz)Déchiffrer (R_{\mathrm{cond}}); sélectionner (R_z) lorsqu'il est zéro, sinon (R_{nz}).
Output(src)Ajouter (R_{\mathrm{src}}) à la liste du registre de sortie.

Une fois la bande d'instructions terminée, le résolveur décrypte chaque registre de sortie, convertit le coefficient zéro en un octet et concaténage ces octets:

O=bytes(Dec(Ro0)0,,Dec(Rok)0) O = \operatorname{bytes}(\operatorname{Dec}(R_{o_0})_0,\ldots, \operatorname{Dec}(R_{o_k})_0)

Les hashs génériques de backend programmés sont:

opaque_hash=H(Dprogram_opaquepolicy_hashO) \mathrm{opaque\_hash} = H(D_{\mathrm{program\_opaque}} \parallel \mathrm{policy\_hash} \parallel O)

receipt_hashprogram=H(Dprogram_receiptpolicy_hashOopaque_hash) \mathrm{receipt\_hash}_{\mathrm{program}} = H(D_{\mathrm{program\_receipt}} \parallel \mathrm{policy\_hash} \parallel O \parallel \mathrm{opaque\_hash})

Le ruban d'identification par défaut programmé a 64 fentes d'entrée. Pour chaque fente (i), il charge la fente d'entrée, charge la voie de mémoire (i \bmod 32), les ajoute et donne le résultat:

R0Ci,R1Simod32,R2R0+R1,Output(R2) R_0 \leftarrow C_i,\qquad R_1 \leftarrow S_{i\bmod 32},\qquad R_2 \leftarrow R_0 + R_1,\qquad \operatorname{Output}(R_2)

Hashs de sortie et reçus

Le reçu d'exécution générique RAM-LFE ne signe pas la sortie brute. Il signale le hash de sortie:

output_hash=H(DoutputO) \mathrm{output\_hash} = H(D_{\mathrm{output}} \parallel O)

Pour les reçus d'exécution Torii RAM-LFE, les données associées sont les octets de l'identifiant du programme canonique:

A=N(program_id) A = N(\mathrm{program\_id})

associated_data_hash=H(A) \mathrm{associated\_data\_hash}=H(A)

La charge utile du reçu signé est:

R=(program_id,program_digest,backend,verification_mode,output_hash,associated_data_hash,executed_at_ms,expires_at_ms) R = (\mathrm{program\_id}, \mathrm{program\_digest}, \mathrm{backend}, \mathrm{verification\_mode}, \mathrm{output\_hash}, \mathrm{associated\_data\_hash}, \mathrm{executed\_at\_ms}, \mathrm{expires\_at\_ms})

Pour le mode signed:

attestation=Signresolver(N(R)) \mathrm{attestation} = \operatorname{Sign}_{\mathrm{resolver}}(N(R))

La vérification vérifie la signature avec resolver_public_key et rejette le reçu, à moins que toutes ces équations ne soient:

R.program_id=policy.program_id R.\mathrm{program\_id} = \mathrm{policy.program\_id}

R.backend=policy.backend R.\mathrm{backend} = \mathrm{policy.backend}

R.verification_mode=policy.verification_mode R.\mathrm{verification\_mode} = \mathrm{policy.verification\_mode}

R.program_digest=policy.public_parameters.hidden_program_digest R.\mathrm{program\_digest} = \mathrm{policy.public\_parameters.hidden\_program\_digest}

R.associated_data_hash=H(N(policy.program_id)) R.\mathrm{associated\_data\_hash} = H(N(\mathrm{policy.program\_id}))

Si l'appelant fournit output_hex, le vérificateur vérifie également:

H(Doutputbytes(output_hex))=R.output_hash H(D_{\mathrm{output}} \parallel \operatorname{bytes}(\mathrm{output\_hex})) = R.\mathrm{output\_hash}

Pour le mode proof, l'attestation porte une enveloppe de preuve au lieu d'une signature. La vérification vérifie que le backend de la preuve, l'identifiant du circuit, le hash du schéma d'entrée publique, le hash de la clé de vérification et les instances publiques exposées correspondent aux métadonnées du vérificateur de preuve et au hash des reçus payload codés.

hR=H(N(R))=(h0,,h31) h_R = H(N(R)) = (h_0,\ldots,h_{31})

Les instances publiques attendues sont quatre colonnes à un élément. La colonne (j) contient des octets (h_{8j}\ldots h_{8j+7}) suivis de 24 octets zéro:

instancej=h8jh8j+7024,0j<4 \mathrm{instance}_j = h_{8j}\parallel\cdots\parallel h_{8j+7}\parallel 0^{24}, \qquad 0 \le j < 4

Projection de l'identifiant

La résolution de l'identifiant n'utilise pas le backend générique opaque_hash En tant qu'identifiant de compte opaque à l'utilisateur. RAM-LFE hash de sortie à travers des domaines spécifiques à l'identifiant:

opaque_idid=H(Did_opaqueN(program_id)output_hash) \mathrm{opaque\_id}_{\mathrm{id}} = H(D_{\mathrm{id\_opaque}} \parallel N(\mathrm{program\_id}) \parallel \mathrm{output\_hash})

receipt_hashid=H(Did_receiptN(program_id)output_hashopaque_idid) \mathrm{receipt\_hash}_{\mathrm{id}} = H(D_{\mathrm{id\_receipt}} \parallel N(\mathrm{program\_id}) \parallel \mathrm{output\_hash} \parallel \mathrm{opaque\_id}_{\mathrm{id}})

Une IdentifierResolutionReceipt signe une charge utile de niveau supérieur:

I=(policy_id,R,opaque_idid,receipt_hashid,uaid,account_id) I = (\mathrm{policy\_id}, R, \mathrm{opaque\_id}_{\mathrm{id}}, \mathrm{receipt\_hash}_{\mathrm{id}}, \mathrm{uaid}, \mathrm{account\_id})

Pour les reçus d'identification signés:

attestation=Signresolver(N(I)) \mathrm{attestation} = \operatorname{Sign}_{\mathrm{resolver}}(N(I))

ClaimIdentifier n'accepte le reçu que lorsque la signature ou la preuve est valide, que la charge utile d'exécution intégrée RAM-LFE correspond à la politique du programme référencée et que les uaid et account_id sont l'obligation requise.

Flux d'exécution

L'exécution générique RAM-LFE a la forme suivante:

  1. La gouvernance ou les registres d'un opérateur RamLfeProgramPolicy.
  2. Le propriétaire active la police.
  3. Le client lit les métadonnées d'ordre public de Torii.
  4. Le client soumet exactement un formulaire d'entrée au résolveur: texte clair input_hex ou enveloppe d'entrée cryptée BFV.
  5. Le temps d'exécution évalue le programme caché et renvoie output_hex, output_hash, opaque_hash, receipt_hash et un RamLfeExecutionReceipt.
  6. Le client ou le backend vérifie la réception par rapport à la politique publiée, en vérifiant optionnellement que le output_hex retourné est lié au output_hash de la réception.
  7. Une instruction de niveau supérieur, telle que ClaimIdentifier, peut intégrer le reçu attesté au lieu d'intégrer l'entrée brute.

Politiques relatives à l'identification

Les politiques d'identification sont une utilisation concrète de RAM-LFE. Elles ajoutent un espace de noms d'entreprise et une règle de normalisation au-dessus d'une politique de programme générique:

text
RegisterRamLfeProgramPolicy(
  program_id = "phone_team",
  owner = "<POLICY_OWNER>",
  backend = "bfv-programmed-sha3-256-v1",
  verification_mode = "signed",
  commitment = "<HIDDEN_PROGRAM_POLICY_COMMITMENT>",
  resolver_public_key = "<RESOLVER_PUBLIC_KEY>"
)
ActivateRamLfeProgramPolicy(program_id = "phone_team")

RegisterIdentifierPolicy(
  id = "phone#team",
  owner = "<POLICY_OWNER>",
  normalization = "PhoneE164",
  program_id = "phone_team",
  note = "Private phone registration for team dataspace"
)
ActivateIdentifierPolicy(policy_id = "phone#team")

La couche d'identification utilise le reçu RAM-LFE pour lier:

  • policy_id
  • l'identifiant opaque dérivé de la fonction cachée
  • la déterministique receipt_hash
  • le compte est UAID
  • le canonique account_id
  • la charge utile d'exécution générique RAM-LFE

Pour l'intégration en face de l'utilisateur, gardez les pseudonymes des comptes séparés des identifiants privés. Les pseudonymes sont des noms publics; les numéros de téléphone, les adresses e-mail et autres valeurs similaires doivent circuler dans les politiques d'identification et les reçus.

Route Torii

Lorsque la famille de routes orientée vers l'application est activée, Torii expose le RAM-LFE et les aides à l'identification:

RouteObjectif
GET /v1/ram-lfe/program-policiesListe des politiques de programme actives et inactives RAM-LFE et des métadonnées d'exécution publique.
POST /v1/ram-lfe/programs/{program_id}/executeExécuter un programme à partir de input_hex ou encrypted_input et retourner les hashes de sortie plus un reçu sans état.
POST /v1/ram-lfe/receipts/verifyVérifiez un RamLfeExecutionReceipt par rapport à la politique publiée et comparez optionnellement le output_hex au output_hash.
GET /v1/identifier-policiesListe des politiques d'identification, des modes de normalisation, des clés de résolution et des métadonnées de saisie cryptées.
POST /v1/accounts/{account_id}/identifiers/claim-receiptÉmettre le reçu que l'utilisateur peut insérer dans ClaimIdentifier.
POST /v1/identifiers/resolveRésoudre une entrée d'identifiant normalisé sur le compte lié lorsqu'il existe une réclamation active.
GET /v1/identifiers/receipts/{receipt_hash}Rechercher une demande d'identification persistante en utilisant un hash de réception pour les outils d'audit et de soutien.

Vérifiez toujours le document /openapi ou /openapi.json du nœud cible avant de construire par rapport à ces routes. La disponibilité dépend de la construction du node et du profil réseau.

Temps d'exécution du nœud

Torii C' est en cours . RAM-LFE le temps d'exécution est configuré sous: torii.ram_lfe.programs[*], clés par program_id. Chaque programme configuré doit être conforme à l'engagement de la politique en chaîne et fournir le matériel nécessaire pour évaluer et Les routes d'identification réutilisent ce même temps d'exécution; elles ne nécessitent pas une surface de configuration séparée identifiant-résolveur.

L'enregistrement d'une politique sur la chaîne ne suffit pas par lui-même. Un nœud cible doit également exposer la famille de routes et avoir le matériel d'exécution correspondant pour les programmes qu'il s'attend à exécuter.

Roues de garde opérationnelles

  • Enregistrer les politiques inactives, vérifier les métadonnées publiques, puis les activer.
  • Gardez les secrets de l'évaluateur cachés, les clés de signature du résolveur et le matériel secret BFV hors des documents, logs, transactions et paquets de clients.
  • Ne mettez pas d'identifiants bruts dans des pseudonymes de compte, des métadonnées de transaction, des événements ou des champs d'état mondial.
  • Vérifier les reçus du côté client avant de soumettre des instructions de niveau supérieur lorsque le SDK expose un vérificateur.
  • Utilisez des champs d'expiration où les reçus obsolètes ne devraient pas rester valables à jamais.
  • Rotate en enregistrant un nouveau programme ou une nouvelle politique d'identification, en migrant les clients et en désactivant l'ancienne politique dès que de nouveaux reçus circulent.