প্রোগ্রাম্যাটিক্যালি এক্সপোর্ট কী মেটেরিয়াল

Tink, 'কী' সংক্রান্ত খারাপ অভ্যাসকে নিরুৎসাহিত করে, যেমন:

  • গোপন কী মেটেরিয়ালে ব্যবহারকারীর অ্যাক্সেস – এর পরিবর্তে, গোপন কী KMS-এ স্টোর করা উচিত, যখনই সম্ভব Tink-এর মাধ্যমে আগে থেকে নির্ধারিত কোনও একটি পদ্ধতি ব্যবহার করে এই ধরনের সিস্টেমকে সাপোর্ট করা হয়।
  • 'কী'-এর কিছু অংশে ব্যবহারকারীর অ্যাক্সেস – এটি করলে প্রায়ই কম্প্যাটিবিলিটি সংক্রান্ত সমস্যা দেখা দেয়।

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

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

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

এইসব টোকেনের একটি উপযোগী ফিচার হল যে এগুলি পাস করা যায়। যেমন, ধরা যাক আপনার কাছে এমন একটি ফাংশন আছে যা একটি ইচ্ছামতো Tink কী সিরিয়ালাইজ করে:

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

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

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

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

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

'কী'-এর কিছু অংশের অ্যাক্সেস

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

কখনও কখনও, Tink কী-কে বিভিন্ন ফর্ম্যাটে কনভার্ট করার প্রয়োজন হয়, যা এইসব মেটাডেটা স্পষ্টভাবে উল্লেখ নাও করতে পারে। এর অর্থ হল, 'কী' ব্যবহার করার সময় মেটাডেটা প্রদান করতে হবে। অন্যভাবে বলতে গেলে, 'কী' সবসময় একই অ্যালগরিদমের সাথে ব্যবহার করা হয় বলে ধরে নিলে, এই ধরনের 'কী'-এর ক্ষেত্রে এখনও একই মেটাডেটা থাকে, এটি শুধু অন্য জায়গায় স্টোর করা হয়।

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

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

এইসব API-এর ব্যবহারকারীরা 'কী' আবার ব্যবহার করার অ্যাটাক এবং অসামঞ্জস্যতা প্রতিরোধ করার জন্য দায়ী।

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

পেশাদার পদ্ধতি: কী এক্সপোর্ট করার সময় সব প্যারামিটার যাচাই করা

যেমন, আপনি যদি এমন একটি ফাংশন লেখেন যা HPKE সর্বজনীন কী এক্সপোর্ট করে:

সর্বজনীন কী এক্সপোর্ট করার ভুল পদ্ধতি:

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

এটি সমস্যাজনক। কী পাওয়ার পরে, থার্ড-পার্টি সেটি ব্যবহার করে কী-এর প্যারামিটার সম্পর্কে কিছু অনুমান করে: যেমন, এটি ধরে নেবে যে এই কী-এর জন্য ব্যবহৃত 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();
}

পেশাদার পদ্ধতি: কী ইমপোর্ট করার সময় যত তাড়াতাড়ি সম্ভব Tink অবজেক্ট ব্যবহার করুন

এটি 'কী' কনফিউশন অ্যাটাকের ঝুঁকি কমায়, কারণ Tink Key অবজেক্ট সঠিক অ্যালগরিদম সম্পূর্ণভাবে নির্দিষ্ট করে এবং 'কী' মেটেরিয়ালের সাথে সব মেটাডেটা একসাথে স্টোর করে।

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

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

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(TinkConfig2026.get(), PublicKeyVerify.class);
    publicKeyVerify.verify(signature, message);
}

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

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

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

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

সাজেশন: ডিস্ক বা নেটওয়ার্ক থেকে প্রথমবার কী মেটেরিয়াল পড়ার সময়, যত তাড়াতাড়ি সম্ভব সংশ্লিষ্ট Tink অবজেক্ট তৈরি করুন।

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

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 অবজেক্টে রূপান্তর করি যখন এটি পড়া হয় এবং আমরা সম্পূর্ণরূপে নির্দিষ্ট করি কোন অ্যালগরিদম ব্যবহার করা উচিত। এই পদ্ধতি 'কী' কনফিউশন অ্যাটাকের সম্ভাবনা কম করে।