প্রোগ্রামগতভাবে মূল উপাদান রপ্তানি করুন

টিঙ্ক কীগুলির সাথে সম্পর্কিত খারাপ অনুশীলনগুলিকে নিরুৎসাহিত করে, যেমন:

  • গোপন কী উপাদানে ব্যবহারকারীর অ্যাক্সেস - পরিবর্তে, গোপন কীগুলিকে একটি কেএমএসে সংরক্ষণ করা উচিত যখনই সম্ভব পূর্বনির্ধারিত উপায়গুলির মধ্যে একটি ব্যবহার করে যেখানে টিঙ্ক এই ধরনের সিস্টেমগুলিকে সমর্থন করে৷
  • কীগুলির অংশগুলিতে ব্যবহারকারীর অ্যাক্সেস - এটি করার ফলে প্রায়শই সামঞ্জস্যপূর্ণ বাগ হয়।

বাস্তবে, এমন কিছু ক্ষেত্রে রয়েছে যা এই নীতিগুলি লঙ্ঘন করতে হবে। Tink নিরাপদে করতে সক্ষম হওয়ার প্রক্রিয়া প্রদান করে যা নিম্নলিখিত বিভাগে বর্ণিত হয়েছে।

গোপন কী অ্যাক্সেস টোকেন

গোপন কী উপাদান অ্যাক্সেস করার জন্য, ব্যবহারকারীদের একটি টোকেন থাকতে হবে (যা সাধারণত কোনো কোনো কার্যকারিতা ছাড়াই কোনো কোনো শ্রেণীর বস্তু)। টোকেনটি সাধারণত একটি পদ্ধতি দ্বারা প্রদান করা হয় যেমন InsecureSecretKeyAccess.get() । Google-এর মধ্যে, ব্যাজেল বিল্ড ভিজিবিলিটি ব্যবহার করে ব্যবহারকারীদের এই ফাংশনটি ব্যবহার করতে বাধা দেওয়া হয়। Google এর বাইরে, নিরাপত্তা পর্যালোচকরা এই ফাংশনের ব্যবহারের জন্য তাদের কোডবেস অনুসন্ধান করতে পারেন।

এই টোকেনগুলির একটি দরকারী বৈশিষ্ট্য হ'ল এগুলি প্রেরণ করা যেতে পারে। উদাহরণস্বরূপ, ধরুন আপনার কাছে একটি ফাংশন রয়েছে যা একটি নির্বিচারে টিঙ্ক কীকে সিরিয়ালাইজ করে:

String serializeKey(Key key, @Nullable SecretKeyAccess secretKeyAccess);

গোপন কী উপাদান আছে এমন কীগুলির জন্য, এই ফাংশনের জন্য secretKeyAccess অবজেক্টটিকে নন-নাল হতে হবে এবং একটি প্রকৃত SecretKeyAccess টোকেন সংরক্ষণ করতে হবে। কোন গোপন উপাদান নেই এমন কীগুলির জন্য, secretKeyAccess উপেক্ষা করা হয়।

এই ধরনের একটি ফাংশন দেওয়া, একটি ফাংশন লেখা সম্ভব যা একটি সম্পূর্ণ কীসেটকে সিরিয়ালাইজ করে:

String serializeKeyset(KeysetHandle keyset, @Nullable SecretKeyAccess
secretKeyAccess);

এই ফাংশনটি অভ্যন্তরীণভাবে কীসেটের প্রতিটি কী-এর জন্য serializeKey কল করে এবং অন্তর্নিহিত ফাংশনে প্রদত্ত secretKeyAccess পাস করে। ব্যবহারকারীরা যারা গোপন কী উপাদান সিরিয়ালাইজ করার প্রয়োজন ছাড়াই serializeKeyset কল করে তারা দ্বিতীয় যুক্তি হিসাবে null ব্যবহার করতে পারে। যে ব্যবহারকারীদের গোপন কী উপাদান সিরিয়ালাইজ করতে হবে তাদের InsecureSecretKeyAccess.get() ব্যবহার করা উচিত।

একটি চাবির অংশ অ্যাক্সেস

টিঙ্ক কীগুলিতে কেবল কাঁচা কী উপাদান থাকে না, মেটাডেটাও থাকে যা নির্দিষ্ট করে কীভাবে কীটি ব্যবহার করা উচিত (এবং, এটি অন্য কোনও উপায়ে ব্যবহার করা উচিত নয়)। উদাহরণস্বরূপ, Tink-এ একটি RSA SSA PSS কী নির্দিষ্ট করে যে এই RSA কী শুধুমাত্র নির্দিষ্ট হ্যাশ ফাংশন এবং নির্দিষ্ট লবণের দৈর্ঘ্য ব্যবহার করে PSS স্বাক্ষর অ্যালগরিদমের সাথে ব্যবহার করা যেতে পারে।

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

আপনি যখন একটি Tink কীকে একটি ভিন্ন বিন্যাসে রূপান্তর করেন, তখন আপনাকে নিশ্চিত করতে হবে যে Tink কী-এর মেটাডেটা অন্য কী বিন্যাসের (অবশ্যই নির্দিষ্ট করা) মেটাডেটার সাথে মেলে। যদি এটি মেলে না, তবে রূপান্তর অবশ্যই ব্যর্থ হবে।

যেহেতু এই চেকগুলি প্রায়শই অনুপস্থিত বা অসম্পূর্ণ থাকে, Tink এপিআইগুলিতে অ্যাক্সেস সীমাবদ্ধ করে যা মূল উপাদানগুলিতে অ্যাক্সেস দেয় যা শুধুমাত্র আংশিক, তবে সম্পূর্ণ কী হিসাবে ভুল হতে পারে। জাভাতে, Tink এর জন্য RestrictedApi ব্যবহার করে, C++ এবং Golang-এ, এটি গোপন কী অ্যাক্সেস টোকেনের মতো টোকেন ব্যবহার করে।

এই API-এর ব্যবহারকারীরা মূল পুনঃব্যবহারের আক্রমণ এবং অসঙ্গতি উভয়ই প্রতিরোধের জন্য দায়ী।

আপনি সাধারণত এমন পদ্ধতির সম্মুখীন হন যা টিঙ্ক থেকে কী রপ্তানি বা কী আমদানির প্রসঙ্গে "আংশিক কী অ্যাক্সেস" সীমাবদ্ধ করে। নিম্নলিখিত সেরা অনুশীলনগুলি এই পরিস্থিতিতে কীভাবে নিরাপদে কাজ করতে হয় তা ব্যাখ্যা করে।

সর্বোত্তম অনুশীলন: কী রপ্তানির সমস্ত পরামিতি যাচাই করুন

উদাহরণস্বরূপ, যদি আপনি একটি ফাংশন লেখেন যা একটি HPKE পাবলিক কী রপ্তানি করে:

একটি সর্বজনীন কী রপ্তানি করার খারাপ উপায়:

/** Provide the key to our users which don't have Tink. */
byte[] exportTinkHpkeKey(HpkePublicKey key) {
    return key.getPublicKeyBytes().toByteArray();
}

এই সমস্যাযুক্ত. চাবিটি পাওয়ার পর, এটি ব্যবহারকারী তৃতীয় পক্ষ কীটির পরামিতিগুলির উপর কিছু অনুমান করে: উদাহরণস্বরূপ, এটি অনুমান করবে যে এই কী 256-বিটের জন্য ব্যবহৃত HPKE AEAD অ্যালগরিদমটি AES-GCM ছিল৷

সুপারিশ: কী রপ্তানির ক্ষেত্রে আপনি যা আশা করেন তা যাচাই করুন।

একটি সর্বজনীন কী রপ্তানি করার আরও ভাল উপায়:

/** Provide the key to our users which don't have Tink. */
byte[] exportTinkHpkeKeyForOurUsers(HpkePublicKey key) {
    // Our users assume we use KEM_P256_HKDF_SHA256 for the KEM.
    if (!key.getParameters().getKemId().equals(HpkeParameters.KemId.KEM_P256_HKDF_SHA256)) {
        throw new IllegalArgumentException("Bad parameters");
    }
    // Our users assume we use HKDF SHA256 to create the key material.
    if (!key.getParameters().getKdfId().equals(HpkeParameters.KdfId.HKDF_SHA256)) {
        throw new IllegalArgumentException("Bad parameters");
    }
    // Our users assume that we use AES GCM with 256 bit keys.
    if (!key.getParameters().getAeadId().equals(HpkeParameters.AeadId.AES_256_GCM)) {
        throw new IllegalArgumentException("Bad parameters");
    }
    // Our users assume we follow the standard and don't add a Tink style prefix
    if (!key.getParameters().getVariant().equals(HpkeParameters.Variant.NO_PREFIX)) {
        throw new IllegalArgumentException("Bad parameters");
    }
    return key.getPublicKeyBytes().toByteArray();
}

সর্বোত্তম অনুশীলন: কী আমদানিতে যত তাড়াতাড়ি সম্ভব টিঙ্ক বস্তু ব্যবহার করুন

এটি কী বিভ্রান্তির আক্রমণের ঝুঁকি কমিয়ে দেয়, কারণ টিঙ্ক কী অবজেক্টটি সঠিক অ্যালগরিদম সম্পূর্ণরূপে নির্দিষ্ট করে এবং মূল উপাদানের সাথে সমস্ত মেটাডেটা একত্রে সংরক্ষণ করে।

নিম্নলিখিত উদাহরণ বিবেচনা করুন:

অ-টাইপ ব্যবহার:

void verifyEcdsaSignature(ECPoint ecPoint, byte[] signature, byte[] message)
        throws Exception {
    EcdsaParameters parameters =
        EcdsaParameters.builder()
            .setSignatureEncoding(EcdsaParameters.SignatureEncoding.IEEE_P1363)
            .setCurveType(EcdsaParameters.CurveType.NIST_P256)
            .setHashType(EcdsaParameters.HashType.SHA256)
            .setVariant(EcdsaParameters.Variant.NO_PREFIX)
            .build();
    EcdsaPublicKey key =
        EcdsaPublicKey.builder()
            .setParameters(parameters)
            .setPublicPoint(ecPoint)
            .build();
    KeysetHandle handle = KeysetHandle.newBuilder()
       .addEntry(KeysetHandle.importKey(key).withFixedId(1).makePrimary())
       .build();
    PublicKeyVerify publicKeyVerify = handle.getPrimitive(RegistryConfiguration.get(), PublicKeyVerify.class);
    publicKeyVerify.verify(signature, message);
}

এটি ত্রুটি প্রবণ: কল সাইটে এটি ভুলে যাওয়া খুব সহজ যে আপনি অন্য অ্যালগরিদমের সাথে একই ecPoint ব্যবহার করবেন না। উদাহরণস্বরূপ, যদি encryptWithECHybridEncrypt নামে একটি অনুরূপ ফাংশন বিদ্যমান থাকে, তাহলে কলকারী একটি বার্তা এনক্রিপ্ট করতে একই কার্ভ পয়েন্ট ব্যবহার করতে পারে, যা সহজেই দুর্বলতার দিকে নিয়ে যেতে পারে।

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

পূর্ববর্তী কোডটি আরও উন্নত করা যেতে পারে। EcdsaPublicKey এ পাস করার পরিবর্তে, একটি KeysetHandle এ পাস করা ভাল। এটি কোন অতিরিক্ত কাজ ছাড়াই কী রোটেশনের জন্য কোড প্রস্তুত করে। তাই এই পছন্দ করা উচিত.

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

সুপারিশ: যখন প্রথমবার ডিস্ক বা নেটওয়ার্ক থেকে মূল উপাদান পড়া হয়, যত তাড়াতাড়ি সম্ভব সংশ্লিষ্ট টিঙ্ক বস্তু তৈরি করুন।

টাইপ করা ব্যবহার:

KeysetHandle readEcdsaKeyFromFile(Path fileWithEcdsaKey) throws Exception {
    byte[] content = Files.readAllBytes(fileWithEcdsaKey);
    BigInteger x = new BigInteger(1, Arrays.copyOfRange(content, 0, 32));
    BigInteger y = new BigInteger(1, Arrays.copyOfRange(content, 32, 64));
    ECPoint point = new ECPoint(x, y);
    EcdsaParameters parameters =
        EcdsaParameters.builder()
            .setSignatureEncoding(EcdsaParameters.SignatureEncoding.IEEE_P1363)
            .setCurveType(EcdsaParameters.CurveType.NIST_P256)
            .setHashType(EcdsaParameters.HashType.SHA256)
            .setVariant(EcdsaParameters.Variant.NO_PREFIX)
            .build();
    EcdsaPublicKey key =
        EcdsaPublicKey.builder()
            .setParameters(parameters)
            .setPublicPoint(ecPoint)
            .build();
    return KeysetHandle.newBuilder()
       .addEntry(KeysetHandle.importKey(key).withFixedId(1).makePrimary())
       .build();
}

এই ধরনের কোড ব্যবহার করে, আমরা বাইট-অ্যারেটি পড়ার সাথে সাথে একটি Tink অবজেক্টে রূপান্তর করি এবং আমরা সম্পূর্ণরূপে নির্দিষ্ট করি যে কোন অ্যালগরিদম ব্যবহার করা উচিত। এই পদ্ধতিটি মূল বিভ্রান্তির আক্রমণের সম্ভাবনা কমিয়ে দেয়।