paint-brush
কী ম্যানেজমেন্ট সিস্টেম: আপনার যা জানা দরকারদ্বারা@vivalaakam
270 পড়া

কী ম্যানেজমেন্ট সিস্টেম: আপনার যা জানা দরকার

দ্বারা Andrey Makarov25m2024/07/16
Read on Terminal Reader

অতিদীর্ঘ; পড়তে

পরিষেবাটি একটি 256-বিট ব্যক্তিগত কী সংরক্ষণ করবে (বেশিরভাগ ব্লকচেইন নেটওয়ার্কগুলিতে ব্যবহৃত হয়) এটি ইভিএম নেটওয়ার্কগুলির জন্য লেনদেন এবং বার্তাগুলি স্বাক্ষর করবে (অন্যান্য নেটওয়ার্কগুলির জন্য সমর্থন পরে করা যেতে পারে) একজন ব্যবহারকারীর ব্যক্তিগত কী আমাদের পরিষেবা ছেড়ে যাবে না৷ আমরা যে কীগুলি ভাগ করি তা অবশ্যই অনন্য হতে হবে। আমাদের প্রতিটি কী সহ কার্যকলাপের একটি লগ থাকা উচিত।
featured image - কী ম্যানেজমেন্ট সিস্টেম: আপনার যা জানা দরকার
Andrey Makarov HackerNoon profile picture

খুব বেশি দিন আগে, একটি মাগল চাকরিতে, বিভিন্ন পরিষেবার জন্য ব্যক্তিগত কী সংরক্ষণ করার বিষয়ে আমাদের একটি প্রশ্ন ছিল। প্রক্রিয়াটিতে, আমরা বেশ কয়েকটি সমাধান খুঁজে পেয়েছি যা বিভিন্ন কারণে খুব উপযুক্ত ছিল না। আমি সিদ্ধান্ত নিয়েছিলাম যে কিছু সময় ফিরে আসব।

পরিষেবার জন্য প্রয়োজনীয়তা

নিজের জন্য, আমি এই পরিষেবাটি কাজ করার জন্য কয়েকটি প্রয়োজনীয়তা হাইলাইট করেছি:

  • আপাতত, পরিষেবাটিকে অবশ্যই একটি 256-বিট ব্যক্তিগত কী সংরক্ষণ করতে হবে (বেশিরভাগ ব্লকচেইন নেটওয়ার্কে ব্যবহৃত)
  • ইভিএম নেটওয়ার্কের জন্য লেনদেন এবং বার্তাগুলিতে স্বাক্ষর করা উচিত (অন্যান্য নেটওয়ার্কগুলির জন্য সমর্থন পরে করা যেতে পারে)।
  • একজন ব্যবহারকারীর সীমাহীন সংখ্যক কী থাকতে পারে।
  • একটি ব্যবহারকারীর ব্যক্তিগত কী আমাদের পরিষেবা ছেড়ে যাবে না.
  • প্রতিটি কী সীমাহীন সংখ্যক লোকের সাথে ভাগ করা যায়।
  • আমরা যে কীগুলি ভাগ করি তা অবশ্যই অনন্য হতে হবে।
  • আমাদের প্রতিটি কী সহ কার্যকলাপের একটি লগ থাকা উচিত।
  • যদি sk ব্যবহার করে পরিষেবাটি যদি কী আপস করা হয়, তাহলে আমাদের এটি প্রত্যাহার করতে সক্ষম হওয়া উচিত।


ইতিমধ্যে কাজের প্রক্রিয়ায়, যখন আমি বুঝতে পেরেছিলাম যে এই কাজটি কীভাবে সমাধান করা উচিত, আমি আরও একটি প্রয়োজনীয়তা হাইলাইট করেছি:

  • আমরা প্রতিটি কী এর সুযোগ সীমিত করতে সক্ষম হতে হবে. যাইহোক, যেহেতু কাজটি ইতিমধ্যেই চলছে, তাই আমি এটি পরবর্তী নিবন্ধের জন্য ছেড়ে দিয়েছি। অতএব, আমরা এই নিবন্ধের মধ্যে বার্তা স্বাক্ষর করার জন্য নিজেদেরকে সীমাবদ্ধ করব।

সমস্যা সমাধানের জন্য বিকল্প

যেহেতু নিবন্ধে কীটি অনেকবার ব্যবহার করা হবে, তাই বিভ্রান্তি এড়াতে আমি প্রাইভেট কী pk কল করব এবং sk শেয়ার করা কী।


প্রাথমিক বিকল্পটি ছিল pbkdf2 ফাংশনে কীটি এনক্রিপ্ট করা, কিন্তু স্বাক্ষর অ্যাক্সেস করার জন্য কীটি কীভাবে ভাগ করা যায় তা নিয়ে একটি সমস্যা ছিল কারণ এই অ্যালগরিদমের প্রক্রিয়ায় আমাদের কাছে শুধুমাত্র একটি কী রয়েছে।


আমি সমস্যা সমাধানের জন্য দুটি বিকল্প খুঁজে পেয়েছি:

  1. আমাদের কাছে ডাটাবেসে সংরক্ষিত এনক্রিপ্ট করা কী-এর একটি মাস্টার কী আছে এবং আমরা ইতিমধ্যেই জেনারেট করা কী শেয়ার করেছি, যা মূল কী-তে নিয়ে যায়। আমি বলব না যে আমি এই বিকল্পটি পছন্দ করেছি কারণ আপনি যদি ডাটাবেসে অ্যাক্সেস পান তবে pk কীটি ডিক্রিপ্ট করা সহজ।


  2. আমরা প্রতিটি কী চেক করার জন্য আমাদের pk কী-এর একটি পৃথক উদাহরণ তৈরি করি। আমি বলব না যে আমি এই বিকল্পটি খুব পছন্দ করি।


তাই, ঘুরতে ঘুরতে এবং কীভাবে sk কীটি সুবিধাজনক করা যায় তা নিয়ে ভাবতে গিয়ে, আমার মনে পড়ল যে শামির সিক্রেটস শেয়ারিং (এসএসএস) ব্যবহার করার সময়, আপনি sk কীটিকে অনন্য করতে পারেন এবং কেবলমাত্র কীটির একটি অংশ ভাগ করতে পারেন। বাকি অংশগুলি নিরাপত্তায় ব্যাকএন্ডে সংরক্ষণ করা হবে এবং আপনি যাকে চান এই অংশগুলি দিতে পারেন।


এটি দেখতে এইরকম হবে: আমরা আমাদের এসএসএস-জেনারেটেড কী দিয়ে pk কী এনক্রিপ্ট করি, কীটির কিছু অংশ বিভিন্ন স্টোরেজে সংরক্ষণ করি এবং কীটির অংশ ব্যবহারকারীকে sk হিসেবে দিই। প্রায় 10-15 মিনিট পরে, আমি একটি সোজা জিনিস বুঝতে পেরেছি:


SSS ব্যবহার করার সময়, আমাদের অন্য কিছু দিয়ে আমাদের pk কী এনক্রিপ্ট করার দরকার নেই কারণ SSS এটিকে কিছুটা পরিচালনা করতে পারে, এবং এই সমাধানটি আমার মতে, PK কী সংরক্ষণের জন্য উপযুক্ত। ব্যবহারকারীর সহ বিভিন্ন স্টোরেজ বিকল্প ব্যবহার করে এটি সর্বদা অংশে বিচ্ছিন্ন করা হয়। যদি এটি প্রত্যাহার করার প্রয়োজন হয়, আমরা আমাদের sk কী-এর সূচক তথ্য মুছে ফেলি এবং দ্রুত একটি নতুন একত্রিত করি।


এই নিবন্ধে, আমি এসএসএস-এর নীতিগুলির উপর আলোচনা করব না; আমি ইতিমধ্যে এই বিষয়ে একটি ছোট নিবন্ধ লিখেছি এবং এই নিবন্ধ থেকে অনেক নীতি আমাদের নতুন পরিষেবার ভিত্তি তৈরি করবে।

স্থাপত্য

আমাদের পরিষেবার নীতি নিম্নরূপ হবে:

  1. ব্যবহারকারী একটি কী তৈরি করতে বেছে নেয়।


  2. আমরা পরিষেবার জন্য একটি উপযুক্ত কী তৈরি করি। এটা হবে আমাদের pk কী। এটি সম্পূর্ণরূপে পরিষেবা ছেড়ে যায় না।


  3. SSS ব্যবহার করে, আমরা আমাদের কী বিভক্ত করি যাতে pk কী পুনরুদ্ধার করতে বিভক্ত কী-এর তিনটি অংশের প্রয়োজন হয়। প্রতিটি বিভক্ত কী দুটি অংশ নিয়ে গঠিত: x: আমাদের কী এর অবস্থান y: এই অবস্থানের মান


  4. আমরা প্রথম অংশটি ভল্টে নিক্ষেপ করি (এটি সংবেদনশীল তথ্য সংরক্ষণের জন্য যে কোনও পরিষেবা হতে পারে যা API এর মাধ্যমে অ্যাক্সেস করা যেতে পারে)।


  5. দ্বিতীয় অংশটি আমরা ডাটাবেসে সংরক্ষণ করি (আমি PostgreSQL ব্যবহার করতে যাচ্ছি)।


  6. তৃতীয় অংশটি আমরা আংশিকভাবে ডাটাবেসে সংরক্ষণ করি এবং অন্য অংশটি আমরা ব্যবহারকারীকে দিই ( sk )। আমাদের প্রয়োজনীয় মান খুঁজে পেতে SK ব্যবহার করতে, আমরা keccak256(sk) ডাটাবেসে সংরক্ষণ করি। যতদূর জানি, এখনো ভাঙেনি।


  7. যখন ব্যবহারকারীর কোনো কিছুতে স্বাক্ষর করার প্রয়োজন হয়, আমরা অ্যাপ্লিকেশনের বিভিন্ন অংশ থেকে ব্যক্তিগত কী সংগ্রহ করে তাতে স্বাক্ষর করি।


এই পদ্ধতির একটি অসুবিধা আছে, যদি sk কী অ্যাডমিনিস্ট্রেটর তার দ্বারা তৈরি করা সমস্ত sk কী হারিয়ে ফেলে, আমরা মূল কীটি ফিরিয়ে আনতে পারি না। একটি বিকল্প হিসাবে, আপনি মূল কীটির একটি ব্যাকআপ করতে পারেন, তবে এটি অন্য সময়ের জন্য =)।

তথ্যশালা

আমার কাজের ফলস্বরূপ, আমার এই ডাটাবেস কাঠামো রয়েছে:

  • ব্যবহারকারীরা ব্যবহারকারী ওরফে কী অ্যাডমিনিস্ট্রেটর সম্পর্কে তথ্য সংরক্ষণ করে।


  • কীগুলি কী সম্পর্কে প্রাথমিক তথ্য সংরক্ষণ করে, যেমন আমাদের শেয়ারের দ্বিতীয় অংশ, যে সূচী দ্বারা আপনি ভল্টে শেয়ারের প্রথম অংশ খুঁজে পেতে পারেন এবং অন্যান্য তথ্য যেমন আমাদের ব্যক্তিগত কী-এর ঠিকানা।


  • শেয়ারে শেয়ারের একটি অংশ থাকে এবং এই শেয়ারের হ্যাশড মানও সংরক্ষণ করে। এটি করা হয় যাতে আমরা এটি ডাটাবেসে খুঁজে পেতে পারি।


  • কী দিয়ে যেকোনো ক্রিয়াকলাপ লগ করা হয় , যেমন কী তৈরি করা এবং সমস্ত স্বাক্ষর, এখানে পড়ে।

উপলব্ধি

আমি এই পরিষেবার জন্য অ্যাক্টিক্স-ওয়েব ফ্রেমওয়ার্কের সাথে মরিচা প্রোগ্রামিং ভাষা ব্যবহার করেছি। আমি তাদের কর্মক্ষেত্রে সব সময় ব্যবহার করি, তাহলে কেন নয়?


আমি যেমন বলেছি, নিম্নলিখিত কারণে ডাটাবেস Postgresql হবে।

বহুপদ

 lazy_static! { static ref PRIME: BigUint = BigUint::from_str_radix( "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141", 16 ) .expect("N parse error"); } #[derive(Clone, Debug)] pub struct Share { pub x: BigUint, pub y: BigUint, } #[derive(Clone, Debug, Serialize, Deserialize)] pub struct ShareStore { pub x: String, pub y: String, } impl From<Share> for ShareStore { fn from(share: Share) -> Self { ShareStore { x: hex::encode(share.x.to_bytes_be()), y: hex::encode(share.y.to_bytes_be()), } } } impl From<&Share> for ShareStore { fn from(share: &Share) -> Self { ShareStore { x: hex::encode(share.x.to_bytes_be()), y: hex::encode(share.y.to_bytes_be()), } } } pub struct Polynomial { prime: BigUint, } impl Polynomial { pub(crate) fn new() -> Self { Polynomial { prime: PRIME.clone(), } } // Calculates the modular multiplicative inverse of `a` modulo `m` using Fermat's Little Theorem. fn mod_inverse(&self, a: &BigUint, m: &BigUint) -> BigUint { a.modpow(&(m - 2u32), m) } // Generates a random polynomial of a given degree with the secret as the constant term. fn random_polynomial(&self, degree: usize, secret: &BigUint) -> Vec<BigUint> { let mut coefficients = vec![secret.clone()]; for _ in 0..degree { let index = BigUint::from_bytes_be(generate_random().as_slice()); coefficients.push(index); } coefficients } // Evaluates a polynomial at a given point `x`, using Horner's method for efficient computation under a prime modulus. fn evaluate_polynomial(&self, coefficients: &[BigUint], x: &BigUint) -> BigUint { let mut result = BigUint::zero(); let mut power = BigUint::one(); for coeff in coefficients { result = (&result + (coeff * &power) % &self.prime) % &self.prime; power = (&power * x) % &self.prime; } result } // Generates `num_shares` shares from a secret, using a polynomial of degree `threshold - 1`. pub fn generate_shares( &self, secret: &BigUint, num_shares: usize, threshold: usize, ) -> Vec<Share> { let coefficients = self.random_polynomial(threshold - 1, secret); let mut shares = vec![]; for _x in 1..=num_shares { let x = BigUint::from_bytes_be(generate_random().as_slice()); let y = self.evaluate_polynomial(&coefficients, &x); shares.push(Share { x, y }); } shares } // Reconstructs the secret from a subset of shares using Lagrange interpolation in a finite field. pub fn reconstruct_secret(&self, shares: &Vec<Share>) -> BigUint { let mut secret = BigUint::zero(); for share_i in shares { let mut numerator = BigUint::one(); let mut denominator = BigUint::one(); for share_j in shares { if share_i.x != share_j.x { numerator = (&numerator * &share_j.x) % &self.prime; let diff = if share_j.x > share_i.x { &share_j.x - &share_i.x } else { &self.prime - (&share_i.x - &share_j.x) }; denominator = (&denominator * &diff) % &self.prime; } } let lagrange = (&share_i.y * &numerator * self.mod_inverse(&denominator, &self.prime)) % &self.prime; secret = (&secret + &lagrange) % &self.prime; } secret } // Adds a new share to the existing set of shares using Lagrange interpolation in a finite field. pub fn add_share(&self, shares: &Vec<Share>) -> Share { let new_index = BigUint::from_bytes_be(generate_random().as_slice()); let mut result = BigUint::zero(); for share_i in shares { let mut lambda = BigUint::one(); for share_j in shares { if share_i.x != share_j.x { let numerator = if new_index.clone() >= share_j.x { (new_index.clone() - &share_j.x) % &self.prime } else { (&self.prime - (&share_j.x - new_index.clone()) % &self.prime) % &self.prime }; let denominator = if share_i.x >= share_j.x { (&share_i.x - &share_j.x) % &self.prime } else { (&self.prime - (&share_j.x - &share_i.x) % &self.prime) % &self.prime }; lambda = (&lambda * &numerator * self.mod_inverse(&denominator, &self.prime)) % &self.prime; } } result = (&result + &share_i.y * &lambda) % &self.prime; } Share { x: new_index, y: result, } } }

আমি এখানে কিছুটা স্বীকারোক্তি দেব: আমি একজন গণিতবিদ নই। এবং যখন আমি এই সম্পর্কে যতটা তথ্য খুঁজে বের করার চেষ্টা করেছি, আসলে, এটি আমার আগের নিবন্ধ থেকে অভিযোজিত কোড।


আপনি এখানে এই বৈশিষ্ট্য সম্পর্কে আরও পড়তে পারেন https://en.wikipedia.org/wiki/Lagrange_polynomial


এই কাঠামোটি (বা শ্রেণী, যেটি আরও সুবিধাজনক) সেই প্রক্রিয়াটির সবচেয়ে গুরুত্বপূর্ণ অংশটি সম্পাদন করে যা আমরা আজকে বর্ণনা করেছি - pk কীটিকে টুকরো টুকরো করে আবার এটিকে আবার একত্রিত করা।

ব্যবহারকারী তৈরি করুন

 #[derive(Serialize, Deserialize)] pub struct CreateUserResponse { pub secret: String, } pub async fn users_create_handler(app_data: web::Data<AppData>) -> HttpResponse { let code = generate_code(); match create_user( CreateOrUpdateUser { secret: code.clone(), }, app_data.get_db_connection(), ) .await { Ok(_) => HttpResponse::Ok().json(CreateUserResponse { secret: code }), Err(e) => { return HttpResponse::InternalServerError().body(format!("Error creating user: {}", e)); } } }

এখানে, সবকিছু যতটা সম্ভব সহজ; আমরা এমন একজন ব্যবহারকারী তৈরি করি যার কীগুলির সাথে কাজ করার জন্য একটি মাস্টার কী রয়েছে। অন্য কোনো পক্ষকে আমাদের চাবি দিয়ে কিছু করতে না দেওয়ার জন্য এটি করা হয়। আদর্শভাবে, এই কী কোনোভাবেই বিতরণ করা উচিত নয়।

কী তৈরি করুন

 pub async fn keys_generate_handler(req: HttpRequest, app_data: web::Data<AppData>) -> HttpResponse { // Check if the request has a master key header let Some(Ok(master_key)) = req.headers().get(MASTER_KEY).map(|header| header.to_str()) else { return HttpResponse::Unauthorized().finish(); }; // Check if user with master key exist let user = match get_user_by_secret(&master_key, app_data.get_db_connection()).await { Ok(user) => user, Err(UserErrors::NotFound(_)) => { return HttpResponse::Unauthorized().finish(); } Err(e) => { return HttpResponse::InternalServerError().body(format!("Error getting user: {}", e)); } }; // generate random `pk` private key let private_key = generate_random(); let Ok(signer) = PrivateKeySigner::from_slice(private_key.as_slice()) else { return HttpResponse::InternalServerError().finish(); }; let secret = BigUint::from_bytes_be(private_key.as_slice()); let poly = Polynomial::new(); // divide `pk` key into 3 shares let shares = poly .generate_shares(&secret, 3, 3) .iter() .map(Into::into) .collect::<Vec<ShareStore>>(); // store first part at Vault let path = generate_code(); if let Err(err) = kv2::set( app_data.get_vault_client().as_ref(), "secret", &path, &shares[0], ) .await { return HttpResponse::InternalServerError().body(format!("Error setting secret: {}", err)); } // Store second part at database and path to first share let key = CreateOrUpdateKey { user_id: user.id, local_key: shares[1].y.clone(), local_index: shares[1].y.clone(), cloud_key: path, address: signer.address(), }; let key = match create_key(key, app_data.get_db_connection()).await { Ok(key) => key, Err(err) => { return HttpResponse::InternalServerError() .body(format!("Error creating key: {}", err)); } }; // Store third part at database as share let share = match create_share( CreateOrUpdateShare { secret: shares[2].y.clone(), key_id: key.id, user_index: shares[2].x.clone(), owner: SharesOwner::Admin, }, app_data.get_db_connection(), ) .await { Ok(share) => share, Err(err) => { return HttpResponse::InternalServerError() .body(format!("Error creating share: {}", err)); } }; let Ok(user_key) = hex::decode(&shares[2].y) else { return HttpResponse::InternalServerError().finish(); }; // Store log let _ = create_log( CreateLog { key_id: key.id, action: "generate_key".to_string(), data: serde_json::json!({ "user_id": user.id }), message: None, }, app_data.get_db_connection(), ) .await; // Return the key and share identifier HttpResponse::Ok().json(KeysGenerateResponse { key: STANDARD.encode(user_key), id: share.id, }) }

ব্যবহারকারীকে চেক করুন যে এই ধরনের একজন ব্যবহারকারী বিদ্যমান, একটি pk কী তৈরি করুন, এটিকে অংশে বিভক্ত করুন এবং বিভিন্ন জায়গায় সংরক্ষণ করুন।

অ্যাক্সেস মঞ্জুর করুন

 pub async fn keys_grant_handler(req: HttpRequest, app_data: web::Data<AppData>) -> HttpResponse { // Check if the request has a master key header let Some(Ok(master_key)) = req.headers().get(MASTER_KEY).map(|header| header.to_str()) else { return HttpResponse::Unauthorized().finish(); }; // Check if a user with the master key exists let user = match get_user_by_secret(&master_key, app_data.get_db_connection()).await { Ok(user) => user, Err(UserErrors::NotFound(_)) => { return HttpResponse::Unauthorized().finish(); } Err(e) => { return HttpResponse::InternalServerError().body(format!("Error getting user: {}", e)); } }; // Check if the request has a secret key header let Some(Ok(secret_key)) = req.headers().get(SECRET_KEY).map(|header| header.to_str()) else { return HttpResponse::Unauthorized().finish(); }; let Ok(share) = STANDARD.decode(secret_key) else { return HttpResponse::Unauthorized().finish(); }; // Check if the share exists let share_value = hex::encode(share); let share = match get_share_by_secret(&share_value, app_data.get_db_connection()).await { Ok(share) => share, Err(ShareErrors::NotFound(_)) => return HttpResponse::NotFound().finish(), Err(_) => { return HttpResponse::Unauthorized().finish(); } }; if !matches!(share.status, SharesStatus::Granted) { return HttpResponse::Unauthorized().finish(); } // Get original key with necessary information let key = match get_key_by_id(&share.key_id, app_data.get_db_connection()).await { Ok(key) => key, Err(KeyErrors::NotFound(_)) => return HttpResponse::NotFound().finish(), Err(_) => { return HttpResponse::Unauthorized().finish(); } }; // Check if the key belongs to the user if key.user_id != user.id { return HttpResponse::Unauthorized().finish(); } // Get the first part of the key from Vault let Ok(cloud_secret) = kv2::read::<ShareStore>( app_data.get_vault_client().as_ref(), "secret", &key.cloud_key, ) .await else { return HttpResponse::InternalServerError().finish(); }; // Combine the shares let shares = vec![ Share { x: BigUint::from_str_radix(&cloud_secret.x, 16).expect("Error parsing local index"), y: BigUint::from_str_radix(&cloud_secret.y, 16).expect("Error parsing local key"), }, Share { x: BigUint::from_str_radix(&key.local_index, 16).expect("Error parsing local index"), y: BigUint::from_str_radix(&key.local_key, 16).expect("Error parsing local key"), }, Share { x: BigUint::from_str_radix(&share.user_index, 16).expect("Error parsing user index"), y: BigUint::from_str_radix(&share_value, 16).expect("Error parsing user key"), }, ]; let sss = Polynomial::new(); // Create a new share let new_share = ShareStore::from(sss.add_share(&shares)); // Store new share into database let share = match create_share( CreateOrUpdateShare { secret: new_share.y.to_string(), key_id: key.id, user_index: new_share.x.to_string(), owner: SharesOwner::Guest, }, app_data.get_db_connection(), ) .await { Ok(share) => share, Err(err) => { return HttpResponse::InternalServerError() .body(format!("Error creating share: {}", err)); } }; let Ok(user_key) = hex::decode(&new_share.y).map(|k| STANDARD.encode(k)) else { return HttpResponse::InternalServerError().finish(); }; // Store log let _ = create_log( CreateLog { key_id: key.id, action: "grant".to_string(), data: serde_json::json!({ "user_id": user.id, "share_id": share.id, }), message: None, }, app_data.get_db_connection(), ) .await; // Return the key and share the identifier HttpResponse::Ok().json(KeysGenerateResponse { key: user_key, id: share.id, }) }

এই ফাংশনটির পরিচালনার প্রক্রিয়াটি নিম্নরূপ:


আমরা যাচাই করি যে অ্যাক্সেস অনুরোধকারীর শেয়ারের সমস্ত অধিকার রয়েছে৷


আমাদের এখানে একটি খুব সাধারণ কারণে গোপন কী দরকার, এটি ছাড়া আমরা আসল pk কী পুনরুদ্ধার করতে পারি না। একটি অতিরিক্ত শেয়ার তৈরি করুন এবং ব্যবহারকারীকে দিন।

অ্যাক্সেস প্রত্যাহার

 pub async fn keys_revoke_handler( req: HttpRequest, app_data: web::Data<AppData>, body: web::Json<KeysRevokeRequest>, ) -> HttpResponse { let Some(Ok(master_key)) = req.headers().get(MASTER_KEY).map(|header| header.to_str()) else { return HttpResponse::Unauthorized().finish(); }; let user = match get_user_by_secret(&master_key, app_data.get_db_connection()).await { Ok(user) => user, Err(UserErrors::NotFound(_)) => { return HttpResponse::Unauthorized().finish(); } Err(e) => { return HttpResponse::InternalServerError().body(format!("Error getting user: {}", e)); } }; let share = match get_share_by_id(&body.id, app_data.get_db_connection()).await { Ok(share) => share, Err(ShareErrors::NotFound(_)) => return HttpResponse::NotFound().finish(), Err(_) => { return HttpResponse::Unauthorized().finish(); } }; let key = match get_key_by_id(&share.key_id, app_data.get_db_connection()).await { Ok(key) => key, Err(KeyErrors::NotFound(_)) => return HttpResponse::NotFound().finish(), Err(_) => { return HttpResponse::Unauthorized().finish(); } }; if key.user_id != user.id { return HttpResponse::Unauthorized().finish(); } if revoke_share_by_id(&share.id, app_data.get_db_connection()) .await .is_err() { return HttpResponse::InternalServerError().finish(); } let _ = create_log( CreateLog { key_id: key.id, action: "revoke".to_string(), data: serde_json::json!({ "user_id": user.id, "share_id": share.id, }), message: None, }, app_data.get_db_connection(), ) .await; HttpResponse::Ok().finish() }

এখানে, আমাদের শুধুমাত্র সেই শেয়ারের শনাক্তকারীকে জানতে হবে যার অ্যাক্সেস আমরা প্রত্যাহার করছি। ভবিষ্যতে, আমি যদি একটি ওয়েব ইন্টারফেস তৈরি করি, এটির সাথে কাজ করা সহজ হবে। আমাদের এখানে আমাদের sk কী দরকার নেই কারণ আমরা এখানে প্রাইভেট কী পুনরুদ্ধার করছি না।

সাইন মেসেজ

 #[derive(Deserialize, Serialize, Debug)] pub struct SignMessageRequest { pub message: String, } #[derive(Deserialize, Serialize, Debug)] pub struct SignMessageResponse { pub signature: String, } pub async fn sign_message_handler( app_data: web::Data<AppData>, req: HttpRequest, body: web::Json<SignMessageRequest>, ) -> HttpResponse { // Get the `sk` key from the request headers let Some(Ok(secret_key)) = req.headers().get(SECRET_KEY).map(|header| header.to_str()) else { return HttpResponse::Unauthorized().finish(); }; // restore shares let (shares, key_id, share_id) = match restore_shares(secret_key, &app_data).await { Ok(shares) => shares, Err(e) => { return HttpResponse::BadRequest().json(json!({"error": e.to_string()})); } }; let sss = Polynomial::new(); // restore `pk` key let private_key = sss.reconstruct_secret(&shares); //sign message let Ok(signer) = PrivateKeySigner::from_slice(private_key.to_bytes_be().as_slice()) else { return HttpResponse::InternalServerError().finish(); }; let Ok(signature) = signer.sign_message(body.message.as_bytes()).await else { return HttpResponse::InternalServerError().finish(); }; // create log let _ = create_log( CreateLog { key_id, action: "sign_message".to_string(), data: json!({ "share_id": share_id, }), message: Some(body.message.clone()), }, app_data.get_db_connection(), ) .await; // return signature HttpResponse::Ok().json(SignMessageResponse { signature: hex::encode(signature.as_bytes()), }) }

বার্তাটি পেয়েছি, যদি সবকিছু ঠিকঠাক থাকে, ব্যক্তিগত কীটি পুনরুদ্ধার করুন এবং এটির সাথে বার্তাটিতে স্বাক্ষর করুন৷


মূলত, আমাদের অ্যাপ্লিকেশনের প্রধান পদ্ধতিগুলি বর্ণনা করা হয়েছে; আমি করুণা করার সিদ্ধান্ত নিয়েছি এবং এখানে পুরো কোডটি রাখব না। এর জন্য গিটহাব রয়েছে এবং সমস্ত কোড সেখানে উপলব্ধ হবে =)

উপসংহার

যদিও এটি এখনও অ্যাপ্লিকেশনের একটি খসড়া, এটি মনে রাখা গুরুত্বপূর্ণ যে এটি শুধুমাত্র একটি ধারণা নয়। এটি একটি কার্যকরী খসড়া যা প্রতিশ্রুতি দেখায়। এটি কীভাবে কাজ করে তা বোঝার জন্য সংগ্রহস্থলে ইন্টিগ্রেশন পরীক্ষাও রয়েছে। নিম্নলিখিত অংশগুলিতে, আমি লেনদেনের স্বাক্ষর যুক্ত করার এবং তাদের ব্যবহারের সুযোগ সীমিত করা সম্ভব করার পরিকল্পনা করছি। আমি তখন একটি ওয়েব ইন্টারফেস তৈরি করতে পারি এবং এই প্রকল্পটিকে গড় ব্যক্তির জন্য বন্ধুত্বপূর্ণ করতে পারি।


এই বিকাশের ব্যবহারের জন্য যথেষ্ট সম্ভাবনা রয়েছে এবং আমি আশা করি যে আমি এটির অন্তত একটি অংশ প্রকাশ করতে পারি; আমি এই পৃষ্ঠাগুলির কোড এবং বিক্ষিপ্ত মন্তব্যের জন্য ক্ষমাপ্রার্থী। আমি উপরে যা লিখেছি তা ব্যাখ্যা করার চেয়ে আমি কোড লিখতে পছন্দ করি। আমি ভাল হওয়ার চেষ্টা করব, তবে এটি এখন আমার চেয়ে শক্তিশালী।


এছাড়াও, যদি ইচ্ছা হয় তবে কোড এবং PR-এ মন্তব্যকে স্বাগত জানাই =)


সকলের জন্য শুভকামনা, এবং আপনার ব্যক্তিগত কীগুলি সুরক্ষিত রাখুন।