From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-22.6 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER, INCLUDES_PATCH,MAILING_LIST_MULTI,MENTIONS_GIT_HOSTING,NICE_REPLY_A, SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED,USER_AGENT_SANE_1 autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 9711EC433F5 for ; Mon, 13 Sep 2021 14:34:12 +0000 (UTC) Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 4940060E08 for ; Mon, 13 Sep 2021 14:34:12 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.4.1 mail.kernel.org 4940060E08 Authentication-Results: mail.kernel.org; dmarc=fail (p=none dis=none) header.from=suse.de Authentication-Results: mail.kernel.org; spf=none smtp.mailfrom=lists.infradead.org DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20210309; h=Sender: Content-Transfer-Encoding:Content-Type:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:In-Reply-To:MIME-Version:Date: Message-ID:Subject:From:References:Cc:To:Reply-To:Content-ID: Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc :Resent-Message-ID:List-Owner; bh=jyO3GoSZr06lmTWJ56fJsBcWEnvIpjpA80/EBTTCYhQ=; b=AQ316UfmdWeKMbzLP8KdzYMGXM ZEfAm+PzWfMKAZAXsY15s4N4e70qhEm/I7MR34qCeT0XOWBJUsh++ybzomAsyNCADcqF7M6B7QhtX 3xvJ2kSOtLps1rpfZYeysvQmW6ZRTMuCfc1ALaFTXONu0YZEgyqaC4NctwvEWL7gH7OrMJNybCmZ+ AJN2Zw3hRk1/k4SVRiy/yTarLYqyRBPs6NrH/MlW9URP4LtpXjeFGIhP8OoXy3MgXXALjj7NocwUH PmZr1/rlg1sfZy1k1LSf+iHy6vuxmbg9wGlBI8QqTzbewPxXjcHBSNaAlisnCcIg2e4umP+00csuo WUiikrQQ==; Received: from localhost ([::1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.94.2 #2 (Red Hat Linux)) id 1mPn22-0024XF-So; Mon, 13 Sep 2021 14:34:02 +0000 Received: from smtp-out2.suse.de ([195.135.220.29]) by bombadil.infradead.org with esmtps (Exim 4.94.2 #2 (Red Hat Linux)) id 1mPn1v-0024W4-W1 for linux-nvme@lists.infradead.org; Mon, 13 Sep 2021 14:34:00 +0000 Received: from imap2.suse-dmz.suse.de (imap2.suse-dmz.suse.de [192.168.254.74]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (P-521) server-digest SHA512) (No client certificate requested) by smtp-out2.suse.de (Postfix) with ESMTPS id 6B7E61FFF3; Mon, 13 Sep 2021 14:33:49 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=suse.de; s=susede2_rsa; t=1631543629; h=from:from:reply-to:date:date:message-id:message-id:to:to:cc:cc: mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=m+cKYDqbN1hfJpxPVdUZwY4A4uTNJ4ohno+8htWkd/Q=; b=uHrDxLgGlWP2uf5nSgEmqGZb59v/LyptRLeMWN5mAbkNnty5aX/Mg/4FkZ+krWgGhjauNo LLiWBob6/6IZWl+Su9UJ7fLO0wmqCWahnO4LIDLcoNyzbvzmJRRiPXvT1CpGKmTmVvX3LM G/FIGxqPxmoQEq/PF+IO5N1oz/ug9s8= DKIM-Signature: v=1; a=ed25519-sha256; c=relaxed/relaxed; d=suse.de; s=susede2_ed25519; t=1631543629; h=from:from:reply-to:date:date:message-id:message-id:to:to:cc:cc: mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=m+cKYDqbN1hfJpxPVdUZwY4A4uTNJ4ohno+8htWkd/Q=; b=1d5S6Wch0ovgUBHK0pyzW4uEPB8xbbO2obMowCdtXqgaZtu8F7x/mKy6felDN4P5NaiXjz CAapje14+qoi/PAg== Received: from imap2.suse-dmz.suse.de (imap2.suse-dmz.suse.de [192.168.254.74]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (P-521) server-digest SHA512) (No client certificate requested) by imap2.suse-dmz.suse.de (Postfix) with ESMTPS id 49A3313AB9; Mon, 13 Sep 2021 14:33:49 +0000 (UTC) Received: from dovecot-director2.suse.de ([192.168.254.65]) by imap2.suse-dmz.suse.de with ESMTPSA id 6IS3EU1hP2H8ZwAAMHmgww (envelope-from ); Mon, 13 Sep 2021 14:33:49 +0000 To: Sagi Grimberg , Christoph Hellwig Cc: Keith Busch , Herbert Xu , "David S . Miller" , linux-nvme@lists.infradead.org, linux-crypto@vger.kernel.org References: <20210910064322.67705-1-hare@suse.de> <20210910064322.67705-8-hare@suse.de> <99cbf790-c276-b3d0-6140-1f5bfa8665eb@grimberg.me> From: Hannes Reinecke Subject: Re: [PATCH 07/12] nvme: Implement In-Band authentication Message-ID: <8bff9a88-a5d4-d7bb-8ce9-81d30438bfbb@suse.de> Date: Mon, 13 Sep 2021 16:33:48 +0200 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Thunderbird/78.12.0 MIME-Version: 1.0 In-Reply-To: <99cbf790-c276-b3d0-6140-1f5bfa8665eb@grimberg.me> Content-Language: en-US X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20210913_073356_555438_351B6627 X-CRM114-Status: GOOD ( 34.41 ) X-BeenThere: linux-nvme@lists.infradead.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: base64 Sender: "Linux-nvme" Errors-To: linux-nvme-bounces+linux-nvme=archiver.kernel.org@lists.infradead.org T24gOS8xMy8yMSAzOjU1IFBNLCBTYWdpIEdyaW1iZXJnIHdyb3RlOgo+IAo+IAo+IE9uIDkvMTAv MjEgOTo0MyBBTSwgSGFubmVzIFJlaW5lY2tlIHdyb3RlOgo+PiBJbXBsZW1lbnQgTlZNZS1vRiBJ bi1CYW5kIGF1dGhlbnRpY2F0aW9uIGFjY29yZGluZyB0byBOVk1lIFRQQVIgODAwNi4KPj4gVGhp cyBwYXRjaCBhZGRzIHR3byBuZXcgZmFicmljIG9wdGlvbnMgJ2RoY2hhcF9zZWNyZXQnIHRvIHNw ZWNpZnkgdGhlCj4+IHByZS1zaGFyZWQga2V5IChpbiBBU0NJSSByZXNwcmVzZW50YXRpb24gYWNj b3JkaW5nIHRvIE5WTWUgMi4wIHNlY3Rpb24KPj4gOC4xMy41LjggJ1NlY3JldCByZXByZXNlbnRh dGlvbicpIGFuZCAnZGhjaGFwX2JpZGknIHRvIHJlcXVlc3QKPj4gYmktZGlyZWN0aW9uYWwKPj4g YXV0aGVudGljYXRpb24gb2YgYm90aCB0aGUgaG9zdCBhbmQgdGhlIGNvbnRyb2xsZXIuCj4+IFJl LWF1dGhlbnRpY2F0aW9uIGNhbiBiZSB0cmlnZ2VyZWQgYnkgd3JpdGluZyB0aGUgUFNLIGludG8g dGhlIG5ldwo+PiBjb250cm9sbGVyIHN5c2ZzIGF0dHJpYnV0ZSAnZGhjaGFwX3NlY3JldCcuCj4+ Cj4+IFNpZ25lZC1vZmYtYnk6IEhhbm5lcyBSZWluZWNrZSA8aGFyZUBzdXNlLmRlPgo+PiAtLS0K Pj4gwqAgZHJpdmVycy9udm1lL2hvc3QvS2NvbmZpZ8KgwqAgfMKgwqAgMTIgKwo+PiDCoCBkcml2 ZXJzL252bWUvaG9zdC9NYWtlZmlsZcKgIHzCoMKgwqAgMSArCj4+IMKgIGRyaXZlcnMvbnZtZS9o b3N0L2F1dGguY8KgwqDCoCB8IDEyODUgKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysr KysKPj4gwqAgZHJpdmVycy9udm1lL2hvc3QvYXV0aC5owqDCoMKgIHzCoMKgIDI1ICsKPj4gwqAg ZHJpdmVycy9udm1lL2hvc3QvY29yZS5jwqDCoMKgIHzCoMKgIDc5ICsrLQo+PiDCoCBkcml2ZXJz L252bWUvaG9zdC9mYWJyaWNzLmMgfMKgwqAgNzMgKy0KPj4gwqAgZHJpdmVycy9udm1lL2hvc3Qv ZmFicmljcy5oIHzCoMKgwqAgNiArCj4+IMKgIGRyaXZlcnMvbnZtZS9ob3N0L252bWUuaMKgwqDC oCB8wqDCoCAzMCArCj4+IMKgIGRyaXZlcnMvbnZtZS9ob3N0L3RyYWNlLmPCoMKgIHzCoMKgIDMy ICsKPj4gwqAgOSBmaWxlcyBjaGFuZ2VkLCAxNTM3IGluc2VydGlvbnMoKyksIDYgZGVsZXRpb25z KC0pCj4+IMKgIGNyZWF0ZSBtb2RlIDEwMDY0NCBkcml2ZXJzL252bWUvaG9zdC9hdXRoLmMKPj4g wqAgY3JlYXRlIG1vZGUgMTAwNjQ0IGRyaXZlcnMvbnZtZS9ob3N0L2F1dGguaAo+Pgo+PiBkaWZm IC0tZ2l0IGEvZHJpdmVycy9udm1lL2hvc3QvS2NvbmZpZyBiL2RyaXZlcnMvbnZtZS9ob3N0L0tj b25maWcKPj4gaW5kZXggZGMwNDUwY2EyM2EzLi45N2U4NDEyZGM0MmQgMTAwNjQ0Cj4+IC0tLSBh L2RyaXZlcnMvbnZtZS9ob3N0L0tjb25maWcKPj4gKysrIGIvZHJpdmVycy9udm1lL2hvc3QvS2Nv bmZpZwo+PiBAQCAtODMsMyArODMsMTUgQEAgY29uZmlnIE5WTUVfVENQCj4+IMKgwqDCoMKgwqDC oMKgIGZyb20gaHR0cHM6Ly9naXRodWIuY29tL2xpbnV4LW52bWUvbnZtZS1jbGkuCj4+IMKgIMKg wqDCoMKgwqDCoMKgIElmIHVuc3VyZSwgc2F5IE4uCj4+ICsKPj4gK2NvbmZpZyBOVk1FX0FVVEgK Pj4gK8KgwqDCoCBib29sICJOVk0gRXhwcmVzcyBvdmVyIEZhYnJpY3MgSW4tQmFuZCBBdXRoZW50 aWNhdGlvbiIKPj4gK8KgwqDCoCBkZXBlbmRzIG9uIE5WTUVfQ09SRQo+PiArwqDCoMKgIHNlbGVj dCBDUllQVE9fSE1BQwo+PiArwqDCoMKgIHNlbGVjdCBDUllQVE9fU0hBMjU2Cj4+ICvCoMKgwqAg c2VsZWN0IENSWVBUT19TSEE1MTIKPj4gK8KgwqDCoCBoZWxwCj4+ICvCoMKgwqDCoMKgIFRoaXMg cHJvdmlkZXMgc3VwcG9ydCBmb3IgTlZNZSBvdmVyIEZhYnJpY3MgSW4tQmFuZCBBdXRoZW50aWNh dGlvbgo+PiArwqDCoMKgwqDCoCBmb3IgdGhlIE5WTWUgb3ZlciBUQ1AgdHJhbnNwb3J0Lgo+IAo+ IE5vdCB0Y3Agc3BlY2lmaWMuLi4KPiAKPj4gZGlmZiAtLWdpdCBhL2RyaXZlcnMvbnZtZS9ob3N0 L2F1dGguYyBiL2RyaXZlcnMvbnZtZS9ob3N0L2F1dGguYwo+PiBuZXcgZmlsZSBtb2RlIDEwMDY0 NAo+PiBpbmRleCAwMDAwMDAwMDAwMDAuLjUzOTNhYzE2YTAwMgo+PiAtLS0gL2Rldi9udWxsCj4+ ICsrKyBiL2RyaXZlcnMvbnZtZS9ob3N0L2F1dGguYwo+PiBAQCAtMCwwICsxLDEyODUgQEAKPj4g Ky8vIFNQRFgtTGljZW5zZS1JZGVudGlmaWVyOiBHUEwtMi4wCj4+ICsvKgo+PiArICogQ29weXJp Z2h0IChjKSAyMDIwIEhhbm5lcyBSZWluZWNrZSwgU1VTRSBMaW51eAo+PiArICovCj4+ICsKPj4g KyNpbmNsdWRlIDxsaW51eC9jcmMzMi5oPgo+PiArI2luY2x1ZGUgPGxpbnV4L2Jhc2U2NC5oPgo+ PiArI2luY2x1ZGUgPGFzbS91bmFsaWduZWQuaD4KPj4gKyNpbmNsdWRlIDxjcnlwdG8vaGFzaC5o Pgo+PiArI2luY2x1ZGUgPGNyeXB0by9kaC5oPgo+PiArI2luY2x1ZGUgPGNyeXB0by9mZmRoZS5o Pgo+PiArI2luY2x1ZGUgIm52bWUuaCIKPj4gKyNpbmNsdWRlICJmYWJyaWNzLmgiCj4+ICsjaW5j bHVkZSAiYXV0aC5oIgo+PiArCj4+ICtzdGF0aWMgdTMyIG52bWVfZGhjaGFwX3NlcW51bTsKPj4g Kwo+PiArc3RydWN0IG52bWVfZGhjaGFwX3F1ZXVlX2NvbnRleHQgewo+PiArwqDCoMKgIHN0cnVj dCBsaXN0X2hlYWQgZW50cnk7Cj4+ICvCoMKgwqAgc3RydWN0IHdvcmtfc3RydWN0IGF1dGhfd29y azsKPj4gK8KgwqDCoCBzdHJ1Y3QgbnZtZV9jdHJsICpjdHJsOwo+PiArwqDCoMKgIHN0cnVjdCBj cnlwdG9fc2hhc2ggKnNoYXNoX3RmbTsKPj4gK8KgwqDCoCBzdHJ1Y3QgY3J5cHRvX2twcCAqZGhf dGZtOwo+PiArwqDCoMKgIHZvaWQgKmJ1ZjsKPj4gK8KgwqDCoCBzaXplX3QgYnVmX3NpemU7Cj4+ ICvCoMKgwqAgaW50IHFpZDsKPj4gK8KgwqDCoCBpbnQgZXJyb3I7Cj4+ICvCoMKgwqAgdTMyIHMx Owo+PiArwqDCoMKgIHUzMiBzMjsKPj4gK8KgwqDCoCB1MTYgdHJhbnNhY3Rpb247Cj4+ICvCoMKg wqAgdTggc3RhdHVzOwo+PiArwqDCoMKgIHU4IGhhc2hfaWQ7Cj4+ICvCoMKgwqAgdTggaGFzaF9s ZW47Cj4+ICvCoMKgwqAgdTggZGhncm91cF9pZDsKPj4gK8KgwqDCoCB1OCBjMVs2NF07Cj4+ICvC oMKgwqAgdTggYzJbNjRdOwo+PiArwqDCoMKgIHU4IHJlc3BvbnNlWzY0XTsKPj4gK8KgwqDCoCB1 OCAqaG9zdF9yZXNwb25zZTsKPj4gK307Cj4+ICsKPj4gK3N0YXRpYyBzdHJ1Y3QgbnZtZV9hdXRo X2RoZ3JvdXBfbWFwIHsKPj4gK8KgwqDCoCBpbnQgaWQ7Cj4+ICvCoMKgwqAgY29uc3QgY2hhciBu YW1lWzE2XTsKPj4gK8KgwqDCoCBjb25zdCBjaGFyIGtwcFsxNl07Cj4+ICvCoMKgwqAgaW50IHBy aXZrZXlfc2l6ZTsKPj4gK8KgwqDCoCBpbnQgcHVia2V5X3NpemU7Cj4+ICt9IGRoZ3JvdXBfbWFw W10gPSB7Cj4+ICvCoMKgwqAgeyAuaWQgPSBOVk1FX0FVVEhfREhDSEFQX0RIR1JPVVBfTlVMTCwK Pj4gK8KgwqDCoMKgwqAgLm5hbWUgPSAiTlVMTCIsIC5rcHAgPSAiTlVMTCIsCj4gCj4gTml0LCBu byBuZWVkIGZvciBhbGwtY2FwcywgY2FuIGRvICJudWxsIgo+IApSaWdodC4gV2lsbCBiZSBkb2lu ZyBzby4KClsgLi4gXQo+PiArdW5zaWduZWQgY2hhciAqbnZtZV9hdXRoX2V4dHJhY3Rfc2VjcmV0 KHVuc2lnbmVkIGNoYXIgKnNlY3JldCwgc2l6ZV90Cj4+ICpvdXRfbGVuKQo+PiArewo+PiArwqDC oMKgIHVuc2lnbmVkIGNoYXIgKmtleTsKPj4gK8KgwqDCoCB1MzIgY3JjOwo+PiArwqDCoMKgIGlu dCBrZXlfbGVuOwo+PiArwqDCoMKgIHNpemVfdCBhbGxvY2F0ZWRfbGVuOwo+PiArCj4+ICvCoMKg wqAgYWxsb2NhdGVkX2xlbiA9IHN0cmxlbihzZWNyZXQpOwo+IAo+IENhbiBtb3ZlIHRvIGRlY2xh cmF0aW9uIGluaXRpYWxpemVyLgo+IApTdXJlLgoKPj4gK8KgwqDCoCBrZXkgPSBremFsbG9jKGFs bG9jYXRlZF9sZW4sIEdGUF9LRVJORUwpOwo+PiArwqDCoMKgIGlmICgha2V5KQo+PiArwqDCoMKg wqDCoMKgwqAgcmV0dXJuIEVSUl9QVFIoLUVOT01FTSk7Cj4+ICsKPj4gK8KgwqDCoCBrZXlfbGVu ID0gYmFzZTY0X2RlY29kZShzZWNyZXQsIGFsbG9jYXRlZF9sZW4sIGtleSk7Cj4+ICvCoMKgwqAg aWYgKGtleV9sZW4gIT0gMzYgJiYga2V5X2xlbiAhPSA1MiAmJgo+PiArwqDCoMKgwqDCoMKgwqAg a2V5X2xlbiAhPSA2OCkgewo+PiArwqDCoMKgwqDCoMKgwqAgcHJfZGVidWcoIkludmFsaWQgREgt SE1BQy1DSEFQIGtleSBsZW4gJWRcbiIsCj4+ICvCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqAga2V5 X2xlbik7Cj4+ICvCoMKgwqDCoMKgwqDCoCBrZnJlZV9zZW5zaXRpdmUoa2V5KTsKPj4gK8KgwqDC oMKgwqDCoMKgIHJldHVybiBFUlJfUFRSKC1FSU5WQUwpOwo+PiArwqDCoMKgIH0KPj4gKwo+PiAr wqDCoMKgIC8qIFRoZSBsYXN0IGZvdXIgYnl0ZXMgaXMgdGhlIENSQyBpbiBsaXR0bGUtZW5kaWFu IGZvcm1hdCAqLwo+PiArwqDCoMKgIGtleV9sZW4gLT0gNDsKPj4gK8KgwqDCoCAvKgo+PiArwqDC oMKgwqAgKiBUaGUgbGludXggaW1wbGVtZW50YXRpb24gZG9lc24ndCBkbyBwcmUtIGFuZCBwb3N0 LWluY3JlbWVudHMsCj4+ICvCoMKgwqDCoCAqIHNvIHdlIGhhdmUgdG8gZG8gaXQgbWFudWFsbHku Cj4+ICvCoMKgwqDCoCAqLwo+PiArwqDCoMKgIGNyYyA9IH5jcmMzMih+MCwga2V5LCBrZXlfbGVu KTsKPj4gKwo+PiArwqDCoMKgIGlmIChnZXRfdW5hbGlnbmVkX2xlMzIoa2V5ICsga2V5X2xlbikg IT0gY3JjKSB7Cj4+ICvCoMKgwqDCoMKgwqDCoCBwcl9kZWJ1ZygiREgtSE1BQy1DSEFQIGtleSBj cmMgbWlzbWF0Y2ggKGtleSAlMDh4LCBjcmMgJTA4eClcbiIsCj4+ICvCoMKgwqDCoMKgwqDCoMKg wqDCoMKgwqDCoMKgIGdldF91bmFsaWduZWRfbGUzMihrZXkgKyBrZXlfbGVuKSwgY3JjKTsKPj4g K8KgwqDCoMKgwqDCoMKgIGtmcmVlX3NlbnNpdGl2ZShrZXkpOwo+PiArwqDCoMKgwqDCoMKgwqAg cmV0dXJuIEVSUl9QVFIoLUVLRVlSRUpFQ1RFRCk7Cj4+ICvCoMKgwqAgfQo+PiArwqDCoMKgICpv dXRfbGVuID0ga2V5X2xlbjsKPj4gK8KgwqDCoCByZXR1cm4ga2V5Owo+PiArfQo+PiArRVhQT1JU X1NZTUJPTF9HUEwobnZtZV9hdXRoX2V4dHJhY3Rfc2VjcmV0KTsKPj4gKwo+PiArdTggKm52bWVf YXV0aF90cmFuc2Zvcm1fa2V5KHU4ICprZXksIHNpemVfdCBrZXlfbGVuLCB1OCBrZXlfaGFzaCwK Pj4gY2hhciAqbnFuKQo+PiArewo+PiArwqDCoMKgIGNvbnN0IGNoYXIgKmhtYWNfbmFtZSA9IG52 bWVfYXV0aF9obWFjX25hbWUoa2V5X2hhc2gpOwo+PiArwqDCoMKgIHN0cnVjdCBjcnlwdG9fc2hh c2ggKmtleV90Zm07Cj4+ICvCoMKgwqAgc3RydWN0IHNoYXNoX2Rlc2MgKnNoYXNoOwo+PiArwqDC oMKgIHU4ICp0cmFuc2Zvcm1lZF9rZXk7Cj4+ICvCoMKgwqAgaW50IHJldDsKPj4gKwo+PiArwqDC oMKgIC8qIE5vIGtleSB0cmFuc2Zvcm1hdGlvbiByZXF1aXJlZCAqLwo+PiArwqDCoMKgIGlmIChr ZXlfaGFzaCA9PSAwKQo+PiArwqDCoMKgwqDCoMKgwqAgcmV0dXJuIDA7Cj4+ICsKPj4gK8KgwqDC oCBobWFjX25hbWUgPSBudm1lX2F1dGhfaG1hY19uYW1lKGtleV9oYXNoKTsKPj4gK8KgwqDCoCBp ZiAoIWhtYWNfbmFtZSkgewo+PiArwqDCoMKgwqDCoMKgwqAgcHJfd2FybigiSW52YWxpZCBrZXkg aGFzaCBpZCAlZFxuIiwga2V5X2hhc2gpOwo+PiArwqDCoMKgwqDCoMKgwqAgcmV0dXJuIEVSUl9Q VFIoLUVLRVlSRUpFQ1RFRCk7Cj4+ICvCoMKgwqAgfQo+IAo+IG5ld2xpbmUgaGVyZS4KPiAKPj4g K8KgwqDCoCBrZXlfdGZtID0gY3J5cHRvX2FsbG9jX3NoYXNoKGhtYWNfbmFtZSwgMCwgMCk7Cj4+ ICvCoMKgwqAgaWYgKElTX0VSUihrZXlfdGZtKSkKPj4gK8KgwqDCoMKgwqDCoMKgIHJldHVybiAo dTggKilrZXlfdGZtOwo+PiArCj4+ICvCoMKgwqAgc2hhc2ggPSBrbWFsbG9jKHNpemVvZihzdHJ1 Y3Qgc2hhc2hfZGVzYykgKwo+PiArwqDCoMKgwqDCoMKgwqDCoMKgwqDCoCBjcnlwdG9fc2hhc2hf ZGVzY3NpemUoa2V5X3RmbSksCj4+ICvCoMKgwqDCoMKgwqDCoMKgwqDCoMKgIEdGUF9LRVJORUwp Owo+PiArwqDCoMKgIGlmICghc2hhc2gpIHsKPj4gK8KgwqDCoMKgwqDCoMKgIGNyeXB0b19mcmVl X3NoYXNoKGtleV90Zm0pOwo+PiArwqDCoMKgwqDCoMKgwqAgcmV0dXJuIEVSUl9QVFIoLUVOT01F TSk7Cj4+ICvCoMKgwqAgfQo+IAo+IG5ld2xpbmUgaGVyZS4KPiAKPj4gK8KgwqDCoCB0cmFuc2Zv cm1lZF9rZXkgPSBremFsbG9jKGNyeXB0b19zaGFzaF9kaWdlc3RzaXplKGtleV90Zm0pLAo+PiBH RlBfS0VSTkVMKTsKPj4gK8KgwqDCoCBpZiAoIXRyYW5zZm9ybWVkX2tleSkgewo+PiArwqDCoMKg wqDCoMKgwqAgcmV0ID0gLUVOT01FTTsKPj4gK8KgwqDCoMKgwqDCoMKgIGdvdG8gb3V0X2ZyZWVf c2hhc2g7Cj4+ICvCoMKgwqAgfQo+PiArCj4+ICvCoMKgwqAgc2hhc2gtPnRmbSA9IGtleV90Zm07 Cj4+ICvCoMKgwqAgcmV0ID0gY3J5cHRvX3NoYXNoX3NldGtleShrZXlfdGZtLCBrZXksIGtleV9s ZW4pOwo+PiArwqDCoMKgIGlmIChyZXQgPCAwKQo+PiArwqDCoMKgwqDCoMKgwqAgZ290byBvdXRf ZnJlZV9zaGFzaDsKPj4gK8KgwqDCoCByZXQgPSBjcnlwdG9fc2hhc2hfaW5pdChzaGFzaCk7Cj4+ ICvCoMKgwqAgaWYgKHJldCA8IDApCj4+ICvCoMKgwqDCoMKgwqDCoCBnb3RvIG91dF9mcmVlX3No YXNoOwo+PiArwqDCoMKgIHJldCA9IGNyeXB0b19zaGFzaF91cGRhdGUoc2hhc2gsIG5xbiwgc3Ry bGVuKG5xbikpOwo+PiArwqDCoMKgIGlmIChyZXQgPCAwKQo+PiArwqDCoMKgwqDCoMKgwqAgZ290 byBvdXRfZnJlZV9zaGFzaDsKPj4gK8KgwqDCoCByZXQgPSBjcnlwdG9fc2hhc2hfdXBkYXRlKHNo YXNoLCAiTlZNZS1vdmVyLUZhYnJpY3MiLCAxNyk7Cj4+ICvCoMKgwqAgaWYgKHJldCA8IDApCj4+ ICvCoMKgwqDCoMKgwqDCoCBnb3RvIG91dF9mcmVlX3NoYXNoOwo+PiArwqDCoMKgIHJldCA9IGNy eXB0b19zaGFzaF9maW5hbChzaGFzaCwgdHJhbnNmb3JtZWRfa2V5KTsKPj4gK291dF9mcmVlX3No YXNoOgo+PiArwqDCoMKgIGtmcmVlKHNoYXNoKTsKPj4gK8KgwqDCoCBjcnlwdG9fZnJlZV9zaGFz aChrZXlfdGZtKTsKPj4gK8KgwqDCoCBpZiAocmV0IDwgMCkgewo+PiArwqDCoMKgwqDCoMKgwqAg a2ZyZWVfc2Vuc2l0aXZlKHRyYW5zZm9ybWVkX2tleSk7Cj4+ICvCoMKgwqDCoMKgwqDCoCByZXR1 cm4gRVJSX1BUUihyZXQpOwo+PiArwqDCoMKgIH0KPiAKPiBBbnkgcmVhc29uIHdoeSB0aGlzIGlz IG5vdCBhIHJldmVyc2UgY2xlYW51cCB3aXRoIGdvdG8gY2FsbC1zaXRlcwo+IHN0YW5kYXJkIHN0 eWxlPwo+IApOb25lIGluIHBhcnRpY3VsYXIuCldpbGwgYmUgZG9pbmcgc28uCgo+PiArwqDCoMKg IHJldHVybiB0cmFuc2Zvcm1lZF9rZXk7Cj4+ICt9Cj4+ICtFWFBPUlRfU1lNQk9MX0dQTChudm1l X2F1dGhfdHJhbnNmb3JtX2tleSk7Cj4+ICsKPj4gK3N0YXRpYyBpbnQgbnZtZV9hdXRoX2hhc2hf c2tleShpbnQgaG1hY19pZCwgdTggKnNrZXksIHNpemVfdAo+PiBza2V5X2xlbiwgdTggKmhrZXkp Cj4+ICt7Cj4+ICvCoMKgwqAgY29uc3QgY2hhciAqZGlnZXN0X25hbWU7Cj4+ICvCoMKgwqAgc3Ry dWN0IGNyeXB0b19zaGFzaCAqdGZtOwo+PiArwqDCoMKgIGludCByZXQ7Cj4+ICsKPj4gK8KgwqDC oCBkaWdlc3RfbmFtZSA9IG52bWVfYXV0aF9kaWdlc3RfbmFtZShobWFjX2lkKTsKPj4gK8KgwqDC oCBpZiAoIWRpZ2VzdF9uYW1lKSB7Cj4+ICvCoMKgwqDCoMKgwqDCoCBwcl9kZWJ1ZygiJXM6IGZh aWxlZCB0byBnZXQgZGlnZXN0IGZvciAlZFxuIiwgX19mdW5jX18sCj4+ICvCoMKgwqDCoMKgwqDC oMKgwqDCoMKgwqAgaG1hY19pZCk7Cj4+ICvCoMKgwqDCoMKgwqDCoCByZXR1cm4gLUVJTlZBTDsK Pj4gK8KgwqDCoCB9Cj4+ICvCoMKgwqAgdGZtID0gY3J5cHRvX2FsbG9jX3NoYXNoKGRpZ2VzdF9u YW1lLCAwLCAwKTsKPj4gK8KgwqDCoCBpZiAoSVNfRVJSKHRmbSkpCj4+ICvCoMKgwqDCoMKgwqDC oCByZXR1cm4gLUVOT01FTTsKPj4gKwo+PiArwqDCoMKgIHJldCA9IGNyeXB0b19zaGFzaF90Zm1f ZGlnZXN0KHRmbSwgc2tleSwgc2tleV9sZW4sIGhrZXkpOwo+PiArwqDCoMKgIGlmIChyZXQgPCAw KQo+PiArwqDCoMKgwqDCoMKgwqAgcHJfZGVidWcoIiVzOiBGYWlsZWQgdG8gaGFzaCBkaWdlc3Qg bGVuICV6dVxuIiwgX19mdW5jX18sCj4+ICvCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqAgc2tleV9s ZW4pOwo+PiArCj4+ICvCoMKgwqAgY3J5cHRvX2ZyZWVfc2hhc2godGZtKTsKPj4gK8KgwqDCoCBy ZXR1cm4gcmV0Owo+PiArfQo+PiArCj4+ICtpbnQgbnZtZV9hdXRoX2F1Z21lbnRlZF9jaGFsbGVu Z2UodTggaG1hY19pZCwgdTggKnNrZXksIHNpemVfdCBza2V5X2xlbiwKPj4gK8KgwqDCoMKgwqDC oMKgIHU4ICpjaGFsbGVuZ2UsIHU4ICphdWcsIHNpemVfdCBobGVuKQo+PiArewo+PiArwqDCoMKg IHN0cnVjdCBjcnlwdG9fc2hhc2ggKnRmbTsKPj4gK8KgwqDCoCBzdHJ1Y3Qgc2hhc2hfZGVzYyAq ZGVzYzsKPj4gK8KgwqDCoCB1OCAqaGFzaGVkX2tleTsKPj4gK8KgwqDCoCBjb25zdCBjaGFyICpo bWFjX25hbWU7Cj4+ICvCoMKgwqAgaW50IHJldDsKPj4gKwo+PiArwqDCoMKgIGhhc2hlZF9rZXkg PSBrbWFsbG9jKGhsZW4sIEdGUF9LRVJORUwpOwo+PiArwqDCoMKgIGlmICghaGFzaGVkX2tleSkK Pj4gK8KgwqDCoMKgwqDCoMKgIHJldHVybiAtRU5PTUVNOwo+PiArCj4+ICvCoMKgwqAgcmV0ID0g bnZtZV9hdXRoX2hhc2hfc2tleShobWFjX2lkLCBza2V5LAo+PiArwqDCoMKgwqDCoMKgwqDCoMKg wqDCoMKgwqDCoMKgwqDCoCBza2V5X2xlbiwgaGFzaGVkX2tleSk7Cj4+ICvCoMKgwqAgaWYgKHJl dCA8IDApCj4+ICvCoMKgwqDCoMKgwqDCoCBnb3RvIG91dF9mcmVlX2tleTsKPj4gKwo+PiArwqDC oMKgIGhtYWNfbmFtZSA9IG52bWVfYXV0aF9obWFjX25hbWUoaG1hY19pZCk7Cj4+ICvCoMKgwqAg aWYgKCFobWFjX25hbWUpIHsKPj4gK8KgwqDCoMKgwqDCoMKgIHByX3dhcm4oIiVzOiBpbnZhbGlk IGhhc2ggYWxnb3JpdG0gJWRcbiIsCj4+ICvCoMKgwqDCoMKgwqDCoMKgwqDCoMKgIF9fZnVuY19f LCBobWFjX2lkKTsKPj4gK8KgwqDCoMKgwqDCoMKgIHJldCA9IC1FSU5WQUw7Cj4+ICvCoMKgwqDC oMKgwqDCoCBnb3RvIG91dF9mcmVlX2tleTsKPj4gK8KgwqDCoCB9Cj4gCj4gbmV3bGluZS4KPiAK Pj4gK8KgwqDCoCB0Zm0gPSBjcnlwdG9fYWxsb2Nfc2hhc2goaG1hY19uYW1lLCAwLCAwKTsKPj4g K8KgwqDCoCBpZiAoSVNfRVJSKHRmbSkpIHsKPj4gK8KgwqDCoMKgwqDCoMKgIHJldCA9IFBUUl9F UlIodGZtKTsKPj4gK8KgwqDCoMKgwqDCoMKgIGdvdG8gb3V0X2ZyZWVfa2V5Owo+PiArwqDCoMKg IH0KPiAKPiBuZXdsaW5lCj4gCj4+ICvCoMKgwqAgZGVzYyA9IGttYWxsb2Moc2l6ZW9mKHN0cnVj dCBzaGFzaF9kZXNjKSArCj4+IGNyeXB0b19zaGFzaF9kZXNjc2l6ZSh0Zm0pLAo+PiArwqDCoMKg wqDCoMKgwqDCoMKgwqDCoMKgwqDCoCBHRlBfS0VSTkVMKTsKPj4gK8KgwqDCoCBpZiAoIWRlc2Mp IHsKPj4gK8KgwqDCoMKgwqDCoMKgIHJldCA9IC1FTk9NRU07Cj4+ICvCoMKgwqDCoMKgwqDCoCBn b3RvIG91dF9mcmVlX2hhc2g7Cj4+ICvCoMKgwqAgfQo+PiArwqDCoMKgIGRlc2MtPnRmbSA9IHRm bTsKPj4gKwo+PiArwqDCoMKgIHJldCA9IGNyeXB0b19zaGFzaF9zZXRrZXkodGZtLCBoYXNoZWRf a2V5LCBobGVuKTsKPj4gK8KgwqDCoCBpZiAocmV0KQo+PiArwqDCoMKgwqDCoMKgwqAgZ290byBv dXRfZnJlZV9kZXNjOwo+PiArCj4+ICvCoMKgwqAgcmV0ID0gY3J5cHRvX3NoYXNoX2luaXQoZGVz Yyk7Cj4+ICvCoMKgwqAgaWYgKHJldCkKPj4gK8KgwqDCoMKgwqDCoMKgIGdvdG8gb3V0X2ZyZWVf ZGVzYzsKPj4gKwo+PiArwqDCoMKgIHJldCA9IGNyeXB0b19zaGFzaF91cGRhdGUoZGVzYywgY2hh bGxlbmdlLCBobGVuKTsKPj4gK8KgwqDCoCBpZiAocmV0KQo+PiArwqDCoMKgwqDCoMKgwqAgZ290 byBvdXRfZnJlZV9kZXNjOwo+PiArCj4+ICvCoMKgwqAgcmV0ID0gY3J5cHRvX3NoYXNoX2ZpbmFs KGRlc2MsIGF1Zyk7Cj4+ICtvdXRfZnJlZV9kZXNjOgo+PiArwqDCoMKgIGtmcmVlX3NlbnNpdGl2 ZShkZXNjKTsKPj4gK291dF9mcmVlX2hhc2g6Cj4+ICvCoMKgwqAgY3J5cHRvX2ZyZWVfc2hhc2go dGZtKTsKPj4gK291dF9mcmVlX2tleToKPj4gK8KgwqDCoCBrZnJlZV9zZW5zaXRpdmUoaGFzaGVk X2tleSk7Cj4+ICvCoMKgwqAgcmV0dXJuIHJldDsKPj4gK30KPj4gK0VYUE9SVF9TWU1CT0xfR1BM KG52bWVfYXV0aF9hdWdtZW50ZWRfY2hhbGxlbmdlKTsKPj4gKwo+PiAraW50IG52bWVfYXV0aF9n ZW5fcHJpdmtleShzdHJ1Y3QgY3J5cHRvX2twcCAqZGhfdGZtLCBpbnQgZGhfZ2lkKQo+PiArewo+ PiArwqDCoMKgIGNoYXIgKnBrZXk7Cj4+ICvCoMKgwqAgaW50IHJldCwgcGtleV9sZW47Cj4+ICsK Pj4gK8KgwqDCoCBpZiAoZGhfZ2lkID09IE5WTUVfQVVUSF9ESENIQVBfREhHUk9VUF8yMDQ4IHx8 Cj4+ICvCoMKgwqDCoMKgwqDCoCBkaF9naWQgPT0gTlZNRV9BVVRIX0RIQ0hBUF9ESEdST1VQXzMw NzIgfHwKPj4gK8KgwqDCoMKgwqDCoMKgIGRoX2dpZCA9PSBOVk1FX0FVVEhfREhDSEFQX0RIR1JP VVBfNDA5NiB8fAo+PiArwqDCoMKgwqDCoMKgwqAgZGhfZ2lkID09IE5WTUVfQVVUSF9ESENIQVBf REhHUk9VUF82MTQ0IHx8Cj4+ICvCoMKgwqDCoMKgwqDCoCBkaF9naWQgPT0gTlZNRV9BVVRIX0RI Q0hBUF9ESEdST1VQXzgxOTIpIHsKPj4gK8KgwqDCoMKgwqDCoMKgIHN0cnVjdCBkaCBwID0gezB9 Owo+PiArwqDCoMKgwqDCoMKgwqAgaW50IGJpdHMgPSBudm1lX2F1dGhfZGhncm91cF9wdWJrZXlf c2l6ZShkaF9naWQpIDw8IDM7Cj4+ICvCoMKgwqDCoMKgwqDCoCBpbnQgZGhfc2VjcmV0X2xlbiA9 IDY0Owo+PiArwqDCoMKgwqDCoMKgwqAgdTggKmRoX3NlY3JldCA9IGt6YWxsb2MoZGhfc2VjcmV0 X2xlbiwgR0ZQX0tFUk5FTCk7Cj4+ICsKPj4gK8KgwqDCoMKgwqDCoMKgIGlmICghZGhfc2VjcmV0 KQo+PiArwqDCoMKgwqDCoMKgwqDCoMKgwqDCoCByZXR1cm4gLUVOT01FTTsKPj4gKwo+PiArwqDC oMKgwqDCoMKgwqAgLyoKPj4gK8KgwqDCoMKgwqDCoMKgwqAgKiBOVk1lIGJhc2Ugc3BlYyB2Mi4w OiBUaGUgREggdmFsdWUgc2hhbGwgYmUgc2V0IHRvIHRoZSB2YWx1ZQo+PiArwqDCoMKgwqDCoMKg wqDCoCAqIG9mIGdeeCBtb2QgcCwgd2hlcmUgJ3gnIGlzIGEgcmFuZG9tIG51bWJlciBzZWxlY3Rl ZCBieSB0aGUKPj4gK8KgwqDCoMKgwqDCoMKgwqAgKiBob3N0IHRoYXQgc2hhbGwgYmUgYXQgbGVh c3QgMjU2IGJpdHMgbG9uZy4KPj4gK8KgwqDCoMKgwqDCoMKgwqAgKgo+PiArwqDCoMKgwqDCoMKg wqDCoCAqIFdlIHdpbGwgYmUgdXNpbmcgYSA1MTIgYml0IHJhbmRvbSBudW1iZXIgYXMgcHJpdmF0 ZSBrZXkuCj4+ICvCoMKgwqDCoMKgwqDCoMKgICogVGhpcyBpcyBsYXJnZSBlbm91Z2ggdG8gcHJv dmlkZSBhZGVxdWF0ZSBzZWN1cml0eSwgYnV0Cj4+ICvCoMKgwqDCoMKgwqDCoMKgICogc21hbGwg ZW5vdWdoIHN1Y2ggdGhhdCB3ZSBjYW4gdHJpdmlhbGx5IGNvbmZvcm0gdG8KPj4gK8KgwqDCoMKg wqDCoMKgwqAgKiBOSVNUIFNCODAwLTU2QSBzZWN0aW9uIDUuNi4xLjEuNCBpZgo+PiArwqDCoMKg wqDCoMKgwqDCoCAqIHdlIGd1YXJhbnRlZSB0aGF0IHRoZSByYW5kb20gbnVtYmVyIGlzIG5vdCBl aXRoZXIKPj4gK8KgwqDCoMKgwqDCoMKgwqAgKiBhbGwgMHhmZiBvciBhbGwgMHgwMC4gQnV0IHRo YXQgc2hvdWxkIGJlIGd1YXJhbnRlZWQKPj4gK8KgwqDCoMKgwqDCoMKgwqAgKiBieSB0aGUgaW4t a2VybmVsIFJORyBhbnl3YXkuCj4+ICvCoMKgwqDCoMKgwqDCoMKgICovCj4+ICvCoMKgwqDCoMKg wqDCoCBnZXRfcmFuZG9tX2J5dGVzKGRoX3NlY3JldCwgZGhfc2VjcmV0X2xlbik7Cj4+ICsKPj4g K8KgwqDCoMKgwqDCoMKgIHJldCA9IGNyeXB0b19mZmRoZV9wYXJhbXMoJnAsIGJpdHMpOwo+PiAr wqDCoMKgwqDCoMKgwqAgaWYgKHJldCkgewo+PiArwqDCoMKgwqDCoMKgwqDCoMKgwqDCoCBrZnJl ZV9zZW5zaXRpdmUoZGhfc2VjcmV0KTsKPj4gK8KgwqDCoMKgwqDCoMKgwqDCoMKgwqAgcmV0dXJu IHJldDsKPj4gK8KgwqDCoMKgwqDCoMKgIH0KPj4gKwo+PiArwqDCoMKgwqDCoMKgwqAgcC5rZXkg PSBkaF9zZWNyZXQ7Cj4+ICvCoMKgwqDCoMKgwqDCoCBwLmtleV9zaXplID0gZGhfc2VjcmV0X2xl bjsKPj4gKwo+PiArwqDCoMKgwqDCoMKgwqAgcGtleV9sZW4gPSBjcnlwdG9fZGhfa2V5X2xlbigm cCk7Cj4+ICvCoMKgwqDCoMKgwqDCoCBwa2V5ID0ga21hbGxvYyhwa2V5X2xlbiwgR0ZQX0tFUk5F TCk7Cj4+ICvCoMKgwqDCoMKgwqDCoCBpZiAoIXBrZXkpIHsKPj4gK8KgwqDCoMKgwqDCoMKgwqDC oMKgwqAga2ZyZWVfc2Vuc2l0aXZlKGRoX3NlY3JldCk7Cj4+ICvCoMKgwqDCoMKgwqDCoMKgwqDC oMKgIHJldHVybiAtRU5PTUVNOwo+PiArwqDCoMKgwqDCoMKgwqAgfQo+PiArCj4+ICvCoMKgwqDC oMKgwqDCoCBnZXRfcmFuZG9tX2J5dGVzKHBrZXksIHBrZXlfbGVuKTsKPj4gK8KgwqDCoMKgwqDC oMKgIHJldCA9IGNyeXB0b19kaF9lbmNvZGVfa2V5KHBrZXksIHBrZXlfbGVuLCAmcCk7Cj4+ICvC oMKgwqDCoMKgwqDCoCBpZiAocmV0KSB7Cj4+ICvCoMKgwqDCoMKgwqDCoMKgwqDCoMKgIHByX2Rl YnVnKCJmYWlsZWQgdG8gZW5jb2RlIHByaXZhdGUga2V5LCBlcnJvciAlZFxuIiwKPj4gK8KgwqDC oMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgIHJldCk7Cj4+ICvCoMKgwqDCoMKgwqDCoMKgwqDC oMKgIGtmcmVlX3NlbnNpdGl2ZShkaF9zZWNyZXQpOwo+PiArwqDCoMKgwqDCoMKgwqDCoMKgwqDC oCBnb3RvIG91dDsKPj4gK8KgwqDCoMKgwqDCoMKgIH0KPj4gK8KgwqDCoCB9IGVsc2Ugewo+PiAr wqDCoMKgwqDCoMKgwqAgcHJfd2FybigiaW52YWxpZCBkaCBncm91cCAlZFxuIiwgZGhfZ2lkKTsK Pj4gK8KgwqDCoMKgwqDCoMKgIHJldHVybiAtRUlOVkFMOwo+PiArwqDCoMKgIH0KPj4gK8KgwqDC oCByZXQgPSBjcnlwdG9fa3BwX3NldF9zZWNyZXQoZGhfdGZtLCBwa2V5LCBwa2V5X2xlbik7Cj4+ ICvCoMKgwqAgaWYgKHJldCkKPj4gK8KgwqDCoMKgwqDCoMKgIHByX2RlYnVnKCJmYWlsZWQgdG8g c2V0IHByaXZhdGUga2V5LCBlcnJvciAlZFxuIiwgcmV0KTsKPj4gK291dDoKPj4gK8KgwqDCoCBr ZnJlZV9zZW5zaXRpdmUocGtleSk7Cj4gCj4gcGtleSBjYW4gYmUgdW5zZXQgaGVyZS4KPiAKT2th eS4KCj4+ICvCoMKgwqAgcmV0dXJuIHJldDsKPj4gK30KPj4gK0VYUE9SVF9TWU1CT0xfR1BMKG52 bWVfYXV0aF9nZW5fcHJpdmtleSk7Cj4+ICsKPj4gK2ludCBudm1lX2F1dGhfZ2VuX3B1YmtleShz dHJ1Y3QgY3J5cHRvX2twcCAqZGhfdGZtLAo+PiArwqDCoMKgwqDCoMKgwqAgdTggKmhvc3Rfa2V5 LCBzaXplX3QgaG9zdF9rZXlfbGVuKQo+PiArewo+PiArwqDCoMKgIHN0cnVjdCBrcHBfcmVxdWVz dCAqcmVxOwo+PiArwqDCoMKgIHN0cnVjdCBjcnlwdG9fd2FpdCB3YWl0Owo+PiArwqDCoMKgIHN0 cnVjdCBzY2F0dGVybGlzdCBkc3Q7Cj4+ICvCoMKgwqAgaW50IHJldDsKPj4gKwo+PiArwqDCoMKg IHJlcSA9IGtwcF9yZXF1ZXN0X2FsbG9jKGRoX3RmbSwgR0ZQX0tFUk5FTCk7Cj4+ICvCoMKgwqAg aWYgKCFyZXEpCj4+ICvCoMKgwqDCoMKgwqDCoCByZXR1cm4gLUVOT01FTTsKPj4gKwo+PiArwqDC oMKgIGNyeXB0b19pbml0X3dhaXQoJndhaXQpOwo+PiArwqDCoMKgIGtwcF9yZXF1ZXN0X3NldF9p bnB1dChyZXEsIE5VTEwsIDApOwo+PiArwqDCoMKgIHNnX2luaXRfb25lKCZkc3QsIGhvc3Rfa2V5 LCBob3N0X2tleV9sZW4pOwo+PiArwqDCoMKgIGtwcF9yZXF1ZXN0X3NldF9vdXRwdXQocmVxLCAm ZHN0LCBob3N0X2tleV9sZW4pOwo+PiArwqDCoMKgIGtwcF9yZXF1ZXN0X3NldF9jYWxsYmFjayhy ZXEsIENSWVBUT19URk1fUkVRX01BWV9CQUNLTE9HLAo+PiArwqDCoMKgwqDCoMKgwqDCoMKgwqDC oMKgwqDCoMKgwqAgY3J5cHRvX3JlcV9kb25lLCAmd2FpdCk7Cj4+ICsKPj4gK8KgwqDCoCByZXQg PSBjcnlwdG9fd2FpdF9yZXEoY3J5cHRvX2twcF9nZW5lcmF0ZV9wdWJsaWNfa2V5KHJlcSksICZ3 YWl0KTsKPj4gKwo+IAo+IG5vIG5lZWQgZm9yIHRoaXMgbmV3bGluZQo+IAo+PiArwqDCoMKgIGtw cF9yZXF1ZXN0X2ZyZWUocmVxKTsKPj4gK8KgwqDCoCByZXR1cm4gcmV0Owo+PiArfQo+PiArRVhQ T1JUX1NZTUJPTF9HUEwobnZtZV9hdXRoX2dlbl9wdWJrZXkpOwo+PiArCj4+ICtpbnQgbnZtZV9h dXRoX2dlbl9zaGFyZWRfc2VjcmV0KHN0cnVjdCBjcnlwdG9fa3BwICpkaF90Zm0sCj4+ICvCoMKg wqDCoMKgwqDCoCB1OCAqY3RybF9rZXksIHNpemVfdCBjdHJsX2tleV9sZW4sCj4+ICvCoMKgwqDC oMKgwqDCoCB1OCAqc2Vzc19rZXksIHNpemVfdCBzZXNzX2tleV9sZW4pCj4+ICt7Cj4+ICvCoMKg wqAgc3RydWN0IGtwcF9yZXF1ZXN0ICpyZXE7Cj4+ICvCoMKgwqAgc3RydWN0IGNyeXB0b193YWl0 IHdhaXQ7Cj4+ICvCoMKgwqAgc3RydWN0IHNjYXR0ZXJsaXN0IHNyYywgZHN0Owo+PiArwqDCoMKg IGludCByZXQ7Cj4+ICsKPj4gK8KgwqDCoCByZXEgPSBrcHBfcmVxdWVzdF9hbGxvYyhkaF90Zm0s IEdGUF9LRVJORUwpOwo+PiArwqDCoMKgIGlmICghcmVxKQo+PiArwqDCoMKgwqDCoMKgwqAgcmV0 dXJuIC1FTk9NRU07Cj4+ICsKPj4gK8KgwqDCoCBjcnlwdG9faW5pdF93YWl0KCZ3YWl0KTsKPj4g K8KgwqDCoCBzZ19pbml0X29uZSgmc3JjLCBjdHJsX2tleSwgY3RybF9rZXlfbGVuKTsKPj4gK8Kg wqDCoCBrcHBfcmVxdWVzdF9zZXRfaW5wdXQocmVxLCAmc3JjLCBjdHJsX2tleV9sZW4pOwo+PiAr wqDCoMKgIHNnX2luaXRfb25lKCZkc3QsIHNlc3Nfa2V5LCBzZXNzX2tleV9sZW4pOwo+PiArwqDC oMKgIGtwcF9yZXF1ZXN0X3NldF9vdXRwdXQocmVxLCAmZHN0LCBzZXNzX2tleV9sZW4pOwo+PiAr wqDCoMKgIGtwcF9yZXF1ZXN0X3NldF9jYWxsYmFjayhyZXEsIENSWVBUT19URk1fUkVRX01BWV9C QUNLTE9HLAo+PiArwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqAgY3J5cHRvX3JlcV9k b25lLCAmd2FpdCk7Cj4+ICsKPj4gK8KgwqDCoCByZXQgPSBjcnlwdG9fd2FpdF9yZXEoY3J5cHRv X2twcF9jb21wdXRlX3NoYXJlZF9zZWNyZXQocmVxKSwgJndhaXQpOwo+PiArCj4+ICvCoMKgwqAg a3BwX3JlcXVlc3RfZnJlZShyZXEpOwo+PiArwqDCoMKgIHJldHVybiByZXQ7Cj4+ICt9Cj4+ICtF WFBPUlRfU1lNQk9MX0dQTChudm1lX2F1dGhfZ2VuX3NoYXJlZF9zZWNyZXQpOwo+PiArCj4+ICtz dGF0aWMgaW50IG52bWVfYXV0aF9zZW5kKHN0cnVjdCBudm1lX2N0cmwgKmN0cmwsIGludCBxaWQs Cj4+ICvCoMKgwqDCoMKgwqDCoCB2b2lkICpkYXRhLCBzaXplX3QgdGwpCj4+ICt7Cj4+ICvCoMKg wqAgc3RydWN0IG52bWVfY29tbWFuZCBjbWQgPSB7fTsKPj4gK8KgwqDCoCBibGtfbXFfcmVxX2Zs YWdzX3QgZmxhZ3MgPSBxaWQgPT0gTlZNRV9RSURfQU5ZID8KPj4gK8KgwqDCoMKgwqDCoMKgIDAg OiBCTEtfTVFfUkVRX05PV0FJVCB8IEJMS19NUV9SRVFfUkVTRVJWRUQ7Cj4+ICvCoMKgwqAgc3Ry dWN0IHJlcXVlc3RfcXVldWUgKnEgPSBxaWQgPT0gTlZNRV9RSURfQU5ZID8KPj4gK8KgwqDCoMKg wqDCoMKgIGN0cmwtPmZhYnJpY3NfcSA6IGN0cmwtPmNvbm5lY3RfcTsKPj4gK8KgwqDCoCBpbnQg cmV0Owo+PiArCj4+ICvCoMKgwqAgY21kLmF1dGhfc2VuZC5vcGNvZGUgPSBudm1lX2ZhYnJpY3Nf Y29tbWFuZDsKPj4gK8KgwqDCoCBjbWQuYXV0aF9zZW5kLmZjdHlwZSA9IG52bWVfZmFicmljc190 eXBlX2F1dGhfc2VuZDsKPj4gK8KgwqDCoCBjbWQuYXV0aF9zZW5kLnNlY3AgPSBOVk1FX0FVVEhf REhDSEFQX1BST1RPQ09MX0lERU5USUZJRVI7Cj4+ICvCoMKgwqAgY21kLmF1dGhfc2VuZC5zcHNw MCA9IDB4MDE7Cj4+ICvCoMKgwqAgY21kLmF1dGhfc2VuZC5zcHNwMSA9IDB4MDE7Cj4+ICvCoMKg wqAgY21kLmF1dGhfc2VuZC50bCA9IHRsOwo+PiArCj4+ICvCoMKgwqAgcmV0ID0gX19udm1lX3N1 Ym1pdF9zeW5jX2NtZChxLCAmY21kLCBOVUxMLCBkYXRhLCB0bCwgMCwgcWlkLAo+PiArwqDCoMKg wqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoCAwLCBmbGFncyk7Cj4+ICvCoMKgwqAg aWYgKHJldCA+IDApCj4+ICvCoMKgwqDCoMKgwqDCoCBkZXZfZGJnKGN0cmwtPmRldmljZSwKPj4g K8KgwqDCoMKgwqDCoMKgwqDCoMKgwqAgIiVzOiBxaWQgJWQgbnZtZSBzdGF0dXMgJWRcbiIsIF9f ZnVuY19fLCBxaWQsIHJldCk7Cj4+ICvCoMKgwqAgZWxzZSBpZiAocmV0IDwgMCkKPj4gK8KgwqDC oMKgwqDCoMKgIGRldl9kYmcoY3RybC0+ZGV2aWNlLAo+PiArwqDCoMKgwqDCoMKgwqDCoMKgwqDC oCAiJXM6IHFpZCAlZCBlcnJvciAlZFxuIiwgX19mdW5jX18sIHFpZCwgcmV0KTsKPj4gK8KgwqDC oCByZXR1cm4gcmV0Owo+PiArfQo+PiArCj4+ICtzdGF0aWMgaW50IG52bWVfYXV0aF9yZWNlaXZl KHN0cnVjdCBudm1lX2N0cmwgKmN0cmwsIGludCBxaWQsCj4+ICvCoMKgwqDCoMKgwqDCoCB2b2lk ICpidWYsIHNpemVfdCBhbCkKPj4gK3sKPj4gK8KgwqDCoCBzdHJ1Y3QgbnZtZV9jb21tYW5kIGNt ZCA9IHt9Owo+PiArwqDCoMKgIGJsa19tcV9yZXFfZmxhZ3NfdCBmbGFncyA9IHFpZCA9PSBOVk1F X1FJRF9BTlkgPwo+PiArwqDCoMKgwqDCoMKgwqAgMCA6IEJMS19NUV9SRVFfTk9XQUlUIHwgQkxL X01RX1JFUV9SRVNFUlZFRDsKPj4gK8KgwqDCoCBzdHJ1Y3QgcmVxdWVzdF9xdWV1ZSAqcSA9IHFp ZCA9PSBOVk1FX1FJRF9BTlkgPwo+PiArwqDCoMKgwqDCoMKgwqAgY3RybC0+ZmFicmljc19xIDog Y3RybC0+Y29ubmVjdF9xOwo+PiArwqDCoMKgIGludCByZXQ7Cj4+ICsKPj4gK8KgwqDCoCBjbWQu YXV0aF9yZWNlaXZlLm9wY29kZSA9IG52bWVfZmFicmljc19jb21tYW5kOwo+PiArwqDCoMKgIGNt ZC5hdXRoX3JlY2VpdmUuZmN0eXBlID0gbnZtZV9mYWJyaWNzX3R5cGVfYXV0aF9yZWNlaXZlOwo+ PiArwqDCoMKgIGNtZC5hdXRoX3JlY2VpdmUuc2VjcCA9IE5WTUVfQVVUSF9ESENIQVBfUFJPVE9D T0xfSURFTlRJRklFUjsKPj4gK8KgwqDCoCBjbWQuYXV0aF9yZWNlaXZlLnNwc3AwID0gMHgwMTsK Pj4gK8KgwqDCoCBjbWQuYXV0aF9yZWNlaXZlLnNwc3AxID0gMHgwMTsKPj4gK8KgwqDCoCBjbWQu YXV0aF9yZWNlaXZlLmFsID0gYWw7Cj4+ICsKPj4gK8KgwqDCoCByZXQgPSBfX252bWVfc3VibWl0 X3N5bmNfY21kKHEsICZjbWQsIE5VTEwsIGJ1ZiwgYWwsIDAsIHFpZCwKPj4gK8KgwqDCoMKgwqDC oMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqAgMCwgZmxhZ3MpOwo+PiArwqDCoMKgIGlmIChy ZXQgPiAwKSB7Cj4+ICvCoMKgwqDCoMKgwqDCoCBkZXZfZGJnKGN0cmwtPmRldmljZSwgIiVzOiBx aWQgJWQgbnZtZSBzdGF0dXMgJXhcbiIsCj4+ICvCoMKgwqDCoMKgwqDCoMKgwqDCoMKgIF9fZnVu Y19fLCBxaWQsIHJldCk7Cj4+ICvCoMKgwqDCoMKgwqDCoCByZXQgPSAtRUlPOwo+IAo+IFdoeSBF SU8/Cj4gClNlZSBuZXh0IGNvbW1lbnQuCgo+PiArwqDCoMKgIH0KPj4gK8KgwqDCoCBpZiAocmV0 IDwgMCkgewo+PiArwqDCoMKgwqDCoMKgwqAgZGV2X2RiZyhjdHJsLT5kZXZpY2UsICIlczogcWlk ICVkIGVycm9yICVkXG4iLAo+PiArwqDCoMKgwqDCoMKgwqDCoMKgwqDCoCBfX2Z1bmNfXywgcWlk LCByZXQpOwo+PiArwqDCoMKgwqDCoMKgwqAgcmV0dXJuIHJldDsKPj4gK8KgwqDCoCB9Cj4gCj4g V2h5IGRpZCB5b3UgY2hvb3NlIHRvIGRvIHRoZXNlIGVycm9yIGNvbmRpdGlvbmFscyBkaWZmZXJl bnRseSBmb3IgdGhlCj4gc2VuZCBhbmQgcmVjZWl2ZSBmdW5jdGlvbnM/Cj4gCkJlY2F1c2Ugd2Ug aGF2ZSBfdGhyZWVfIGtpbmRzIG9mIGVycm9ycyBoZXJlOiBlcnJvciBjb2RlcywgTlZNZSBzdGF0 dXMsCmFuZCBhdXRoZW50aWNhdGlvbiBzdGF0dXMuCkFuZCBvZiBjb3Vyc2UgdGhlIGF1dGhlbnRp Y2F0aW9uIHN0YXR1cyBpcyBfbm90XyBhbiBOVk1lIHN0YXR1cywgc28gd2UKY2FuJ3QgZWFzaWx5 IG92ZXJsb2FkIGJvdGggaW50byBhIHNpbmdsZSB2YWx1ZS4KQXMgdGhlIGF1dGhlbnRpY2F0aW9u IHN0YXR1cyB3aWxsIGJlIHNldCBmcm9tIHRoZSByZWNlaXZlZCBkYXRhIEkgY2hvc2UKdG8gZm9s ZCBhbGwgTlZNZSBzdGF0dXMgb250byAtRUlPLCBsZWF2aW5nIHRoZSBwb3NpdGl2ZSB2YWx1ZSBm cmVlIGZvcgphdXRoZW50aWNhdGlvbiBzdGF0dXMuCgo+PiArCj4+ICvCoMKgwqAgcmV0dXJuIDA7 Cj4+ICt9Cj4+ICsKPj4gK3N0YXRpYyBpbnQgbnZtZV9hdXRoX3JlY2VpdmVfdmFsaWRhdGUoc3Ry dWN0IG52bWVfY3RybCAqY3RybCwgaW50IHFpZCwKPj4gK8KgwqDCoMKgwqDCoMKgIHN0cnVjdCBu dm1mX2F1dGhfZGhjaGFwX2ZhaWx1cmVfZGF0YSAqZGF0YSwKPj4gK8KgwqDCoMKgwqDCoMKgIHUx NiB0cmFuc2FjdGlvbiwgdTggZXhwZWN0ZWRfbXNnKQo+PiArewo+PiArwqDCoMKgIGRldl9kYmco Y3RybC0+ZGV2aWNlLCAiJXM6IHFpZCAlZCBhdXRoX3R5cGUgJWQgYXV0aF9pZCAleFxuIiwKPj4g K8KgwqDCoMKgwqDCoMKgIF9fZnVuY19fLCBxaWQsIGRhdGEtPmF1dGhfdHlwZSwgZGF0YS0+YXV0 aF9pZCk7Cj4+ICsKPj4gK8KgwqDCoCBpZiAoZGF0YS0+YXV0aF90eXBlID09IE5WTUVfQVVUSF9D T01NT05fTUVTU0FHRVMgJiYKPj4gK8KgwqDCoMKgwqDCoMKgIGRhdGEtPmF1dGhfaWQgPT0gTlZN RV9BVVRIX0RIQ0hBUF9NRVNTQUdFX0ZBSUxVUkUxKSB7Cj4+ICvCoMKgwqDCoMKgwqDCoCByZXR1 cm4gZGF0YS0+cmVzY29kZV9leHA7Cj4+ICvCoMKgwqAgfQo+PiArwqDCoMKgIGlmIChkYXRhLT5h dXRoX3R5cGUgIT0gTlZNRV9BVVRIX0RIQ0hBUF9NRVNTQUdFUyB8fAo+PiArwqDCoMKgwqDCoMKg wqAgZGF0YS0+YXV0aF9pZCAhPSBleHBlY3RlZF9tc2cpIHsKPj4gK8KgwqDCoMKgwqDCoMKgIGRl dl93YXJuKGN0cmwtPmRldmljZSwKPj4gK8KgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoCAicWlkICVk IGludmFsaWQgbWVzc2FnZSAlMDJ4LyUwMnhcbiIsCj4+ICvCoMKgwqDCoMKgwqDCoMKgwqDCoMKg wqAgcWlkLCBkYXRhLT5hdXRoX3R5cGUsIGRhdGEtPmF1dGhfaWQpOwo+PiArwqDCoMKgwqDCoMKg wqAgcmV0dXJuIE5WTUVfQVVUSF9ESENIQVBfRkFJTFVSRV9JTkNPUlJFQ1RfTUVTU0FHRTsKPj4g K8KgwqDCoCB9Cj4+ICvCoMKgwqAgaWYgKGxlMTZfdG9fY3B1KGRhdGEtPnRfaWQpICE9IHRyYW5z YWN0aW9uKSB7Cj4+ICvCoMKgwqDCoMKgwqDCoCBkZXZfd2FybihjdHJsLT5kZXZpY2UsCj4+ICvC oMKgwqDCoMKgwqDCoMKgwqDCoMKgwqAgInFpZCAlZCBpbnZhbGlkIHRyYW5zYWN0aW9uIElEICVk XG4iLAo+PiArwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgIHFpZCwgbGUxNl90b19jcHUoZGF0YS0+ dF9pZCkpOwo+PiArwqDCoMKgwqDCoMKgwqAgcmV0dXJuIE5WTUVfQVVUSF9ESENIQVBfRkFJTFVS RV9JTkNPUlJFQ1RfTUVTU0FHRTsKPj4gK8KgwqDCoCB9Cj4+ICvCoMKgwqAgcmV0dXJuIDA7Cj4+ ICt9Cj4+ICsKPj4gK3N0YXRpYyBpbnQgbnZtZV9hdXRoX3NldF9kaGNoYXBfbmVnb3RpYXRlX2Rh dGEoc3RydWN0IG52bWVfY3RybCAqY3RybCwKPj4gK8KgwqDCoMKgwqDCoMKgIHN0cnVjdCBudm1l X2RoY2hhcF9xdWV1ZV9jb250ZXh0ICpjaGFwKQo+PiArewo+PiArwqDCoMKgIHN0cnVjdCBudm1m X2F1dGhfZGhjaGFwX25lZ290aWF0ZV9kYXRhICpkYXRhID0gY2hhcC0+YnVmOwo+PiArwqDCoMKg IHNpemVfdCBzaXplID0gc2l6ZW9mKCpkYXRhKSArIHNpemVvZih1bmlvbiBudm1mX2F1dGhfcHJv dG9jb2wpOwo+PiArCj4+ICvCoMKgwqAgaWYgKGNoYXAtPmJ1Zl9zaXplIDwgc2l6ZSkgewo+PiAr wqDCoMKgwqDCoMKgwqAgY2hhcC0+c3RhdHVzID0gTlZNRV9BVVRIX0RIQ0hBUF9GQUlMVVJFX0lO Q09SUkVDVF9QQVlMT0FEOwo+IAo+IElzIHRoaXMgYW4gaW50ZXJuYWwgZXJyb3I/IG5vdCBzdXJl IEkgdW5kZXJzdGFuZCBzZXR0aW5nIG9mIHRoaXMgc3RhdHVzCj4gCkFzIG1lbnRpb25lZCBhYm92 ZSwgd2Ugbm93IGhhdmUgdGhyZWUgcG9zc2libGUgc3RhdHVzIGNvZGVzIHRvIGNvbnRlbnQKd2l0 aC4gU28geWVzLCB0aGlzIGlzIGFuIGludGVybmFsIGVycm9yLCBleHByZXNzZWQgYXMgYXV0aGVu dGljYXRpb24KZXJyb3IgY29kZS4KClRoZSBzcGVjIGluc2lzdHMgb24gdXNpbmcgYW4gYXV0aGVu dGljYXRpb24gZXJyb3IgaGVyZTsgaXQgd291bGQgYmUKcG9zc2libGUgdG8gdXNlIGEgbm9ybWFs IE5WTWUgc3RhdHVzLCBidXQgdGhhdCdzIG5vdCB3aGF0IHRoZSBzcGVjIHdhbnRzIC4uLgoKPj4g K8KgwqDCoMKgwqDCoMKgIHJldHVybiAtRUlOVkFMOwo+PiArwqDCoMKgIH0KPj4gK8KgwqDCoCBt ZW1zZXQoKHU4ICopY2hhcC0+YnVmLCAwLCBzaXplKTsKPj4gK8KgwqDCoCBkYXRhLT5hdXRoX3R5 cGUgPSBOVk1FX0FVVEhfQ09NTU9OX01FU1NBR0VTOwo+PiArwqDCoMKgIGRhdGEtPmF1dGhfaWQg PSBOVk1FX0FVVEhfREhDSEFQX01FU1NBR0VfTkVHT1RJQVRFOwo+PiArwqDCoMKgIGRhdGEtPnRf aWQgPSBjcHVfdG9fbGUxNihjaGFwLT50cmFuc2FjdGlvbik7Cj4+ICvCoMKgwqAgZGF0YS0+c2Nf YyA9IDA7IC8qIE5vIHNlY3VyZSBjaGFubmVsIGNvbmNhdGVuYXRpb24gKi8KPj4gK8KgwqDCoCBk YXRhLT5uYXBkID0gMTsKPj4gK8KgwqDCoCBkYXRhLT5hdXRoX3Byb3RvY29sWzBdLmRoY2hhcC5h dXRoaWQgPSBOVk1FX0FVVEhfREhDSEFQX0FVVEhfSUQ7Cj4+ICvCoMKgwqAgZGF0YS0+YXV0aF9w cm90b2NvbFswXS5kaGNoYXAuaGFsZW4gPSAzOwo+PiArwqDCoMKgIGRhdGEtPmF1dGhfcHJvdG9j b2xbMF0uZGhjaGFwLmRobGVuID0gNjsKPj4gK8KgwqDCoCBkYXRhLT5hdXRoX3Byb3RvY29sWzBd LmRoY2hhcC5pZGxpc3RbMF0gPSBOVk1FX0FVVEhfREhDSEFQX1NIQTI1NjsKPj4gK8KgwqDCoCBk YXRhLT5hdXRoX3Byb3RvY29sWzBdLmRoY2hhcC5pZGxpc3RbMV0gPSBOVk1FX0FVVEhfREhDSEFQ X1NIQTM4NDsKPj4gK8KgwqDCoCBkYXRhLT5hdXRoX3Byb3RvY29sWzBdLmRoY2hhcC5pZGxpc3Rb Ml0gPSBOVk1FX0FVVEhfREhDSEFQX1NIQTUxMjsKPj4gK8KgwqDCoCBkYXRhLT5hdXRoX3Byb3Rv Y29sWzBdLmRoY2hhcC5pZGxpc3RbM10gPQo+PiBOVk1FX0FVVEhfREhDSEFQX0RIR1JPVVBfTlVM TDsKPj4gK8KgwqDCoCBkYXRhLT5hdXRoX3Byb3RvY29sWzBdLmRoY2hhcC5pZGxpc3RbNF0gPQo+ PiBOVk1FX0FVVEhfREhDSEFQX0RIR1JPVVBfMjA0ODsKPj4gK8KgwqDCoCBkYXRhLT5hdXRoX3By b3RvY29sWzBdLmRoY2hhcC5pZGxpc3RbNV0gPQo+PiBOVk1FX0FVVEhfREhDSEFQX0RIR1JPVVBf MzA3MjsKPj4gK8KgwqDCoCBkYXRhLT5hdXRoX3Byb3RvY29sWzBdLmRoY2hhcC5pZGxpc3RbNl0g PQo+PiBOVk1FX0FVVEhfREhDSEFQX0RIR1JPVVBfNDA5NjsKPj4gK8KgwqDCoCBkYXRhLT5hdXRo X3Byb3RvY29sWzBdLmRoY2hhcC5pZGxpc3RbN10gPQo+PiBOVk1FX0FVVEhfREhDSEFQX0RIR1JP VVBfNjE0NDsKPj4gK8KgwqDCoCBkYXRhLT5hdXRoX3Byb3RvY29sWzBdLmRoY2hhcC5pZGxpc3Rb OF0gPQo+PiBOVk1FX0FVVEhfREhDSEFQX0RIR1JPVVBfODE5MjsKPj4gKwo+PiArwqDCoMKgIHJl dHVybiBzaXplOwo+PiArfQo+PiArCj4+ICtzdGF0aWMgaW50IG52bWVfYXV0aF9wcm9jZXNzX2Ro Y2hhcF9jaGFsbGVuZ2Uoc3RydWN0IG52bWVfY3RybCAqY3RybCwKPj4gK8KgwqDCoMKgwqDCoMKg IHN0cnVjdCBudm1lX2RoY2hhcF9xdWV1ZV9jb250ZXh0ICpjaGFwKQo+PiArewo+PiArwqDCoMKg IHN0cnVjdCBudm1mX2F1dGhfZGhjaGFwX2NoYWxsZW5nZV9kYXRhICpkYXRhID0gY2hhcC0+YnVm Owo+PiArwqDCoMKgIHNpemVfdCBzaXplID0gc2l6ZW9mKCpkYXRhKSArIGRhdGEtPmhsICsgZGF0 YS0+ZGh2bGVuOwo+PiArwqDCoMKgIGNvbnN0IGNoYXIgKmhtYWNfbmFtZTsKPj4gKwo+PiArwqDC oMKgIGlmIChjaGFwLT5idWZfc2l6ZSA8IHNpemUpIHsKPj4gK8KgwqDCoMKgwqDCoMKgIGNoYXAt PnN0YXR1cyA9IE5WTUVfQVVUSF9ESENIQVBfRkFJTFVSRV9JTkNPUlJFQ1RfUEFZTE9BRDsKPj4g K8KgwqDCoMKgwqDCoMKgIHJldHVybiBOVk1FX1NDX0lOVkFMSURfRklFTEQ7Cj4+ICvCoMKgwqAg fQo+PiArCj4+ICvCoMKgwqAgaG1hY19uYW1lID0gbnZtZV9hdXRoX2htYWNfbmFtZShkYXRhLT5o YXNoaWQpOwo+PiArwqDCoMKgIGlmICghaG1hY19uYW1lKSB7Cj4+ICvCoMKgwqDCoMKgwqDCoCBk ZXZfd2FybihjdHJsLT5kZXZpY2UsCj4+ICvCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqAgInFpZCAl ZDogaW52YWxpZCBIQVNIIElEICVkXG4iLAo+PiArwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgIGNo YXAtPnFpZCwgZGF0YS0+aGFzaGlkKTsKPj4gK8KgwqDCoMKgwqDCoMKgIGNoYXAtPnN0YXR1cyA9 IE5WTUVfQVVUSF9ESENIQVBfRkFJTFVSRV9IQVNIX1VOVVNBQkxFOwo+PiArwqDCoMKgwqDCoMKg wqAgcmV0dXJuIC1FUFJPVE87Cj4+ICvCoMKgwqAgfQo+PiArwqDCoMKgIGlmIChjaGFwLT5oYXNo X2lkID09IGRhdGEtPmhhc2hpZCAmJiBjaGFwLT5zaGFzaF90Zm0gJiYKPj4gK8KgwqDCoMKgwqDC oMKgICFzdHJjbXAoY3J5cHRvX3NoYXNoX2FsZ19uYW1lKGNoYXAtPnNoYXNoX3RmbSksIGhtYWNf bmFtZSkgJiYKPj4gK8KgwqDCoMKgwqDCoMKgIGNyeXB0b19zaGFzaF9kaWdlc3RzaXplKGNoYXAt PnNoYXNoX3RmbSkgPT0gZGF0YS0+aGwpIHsKPj4gK8KgwqDCoMKgwqDCoMKgIGRldl9kYmcoY3Ry bC0+ZGV2aWNlLAo+PiArwqDCoMKgwqDCoMKgwqDCoMKgwqDCoCAicWlkICVkOiByZXVzZSBleGlz dGluZyBoYXNoICVzXG4iLAo+PiArwqDCoMKgwqDCoMKgwqDCoMKgwqDCoCBjaGFwLT5xaWQsIGht YWNfbmFtZSk7Cj4+ICvCoMKgwqDCoMKgwqDCoCBnb3RvIHNlbGVjdF9rcHA7Cj4+ICvCoMKgwqAg fQo+IAo+IG5ld2xpbmUKPiAKPj4gK8KgwqDCoCBpZiAoY2hhcC0+c2hhc2hfdGZtKSB7Cj4+ICvC oMKgwqDCoMKgwqDCoCBjcnlwdG9fZnJlZV9zaGFzaChjaGFwLT5zaGFzaF90Zm0pOwo+PiArwqDC oMKgwqDCoMKgwqAgY2hhcC0+aGFzaF9pZCA9IDA7Cj4+ICvCoMKgwqDCoMKgwqDCoCBjaGFwLT5o YXNoX2xlbiA9IDA7Cj4+ICvCoMKgwqAgfQo+IAo+IG5ld2xpbmUKPiAKPj4gK8KgwqDCoCBjaGFw LT5zaGFzaF90Zm0gPSBjcnlwdG9fYWxsb2Nfc2hhc2goaG1hY19uYW1lLCAwLAo+PiArwqDCoMKg wqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgIENSWVBUT19BTEdfQUxM T0NBVEVTX01FTU9SWSk7Cj4+ICvCoMKgwqAgaWYgKElTX0VSUihjaGFwLT5zaGFzaF90Zm0pKSB7 Cj4+ICvCoMKgwqDCoMKgwqDCoCBkZXZfd2FybihjdHJsLT5kZXZpY2UsCj4+ICvCoMKgwqDCoMKg wqDCoMKgwqDCoMKgwqAgInFpZCAlZDogZmFpbGVkIHRvIGFsbG9jYXRlIGhhc2ggJXMsIGVycm9y ICVsZFxuIiwKPj4gK8KgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoCBjaGFwLT5xaWQsIGhtYWNfbmFt ZSwgUFRSX0VSUihjaGFwLT5zaGFzaF90Zm0pKTsKPj4gK8KgwqDCoMKgwqDCoMKgIGNoYXAtPnNo YXNoX3RmbSA9IE5VTEw7Cj4+ICvCoMKgwqDCoMKgwqDCoCBjaGFwLT5zdGF0dXMgPSBOVk1FX0FV VEhfREhDSEFQX0ZBSUxVUkVfRkFJTEVEOwo+PiArwqDCoMKgwqDCoMKgwqAgcmV0dXJuIE5WTUVf U0NfQVVUSF9SRVFVSVJFRDsKPj4gK8KgwqDCoCB9Cj4gCj4gbmV3bGluZQo+IAo+PiArwqDCoMKg IGlmIChjcnlwdG9fc2hhc2hfZGlnZXN0c2l6ZShjaGFwLT5zaGFzaF90Zm0pICE9IGRhdGEtPmhs KSB7Cj4+ICvCoMKgwqDCoMKgwqDCoCBkZXZfd2FybihjdHJsLT5kZXZpY2UsCj4+ICvCoMKgwqDC oMKgwqDCoMKgwqDCoMKgwqAgInFpZCAlZDogaW52YWxpZCBoYXNoIGxlbmd0aCAlZFxuIiwKPj4g K8KgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoCBjaGFwLT5xaWQsIGRhdGEtPmhsKTsKPj4gK8KgwqDC oMKgwqDCoMKgIGNyeXB0b19mcmVlX3NoYXNoKGNoYXAtPnNoYXNoX3RmbSk7Cj4+ICvCoMKgwqDC oMKgwqDCoCBjaGFwLT5zaGFzaF90Zm0gPSBOVUxMOwo+PiArwqDCoMKgwqDCoMKgwqAgY2hhcC0+ c3RhdHVzID0gTlZNRV9BVVRIX0RIQ0hBUF9GQUlMVVJFX0hBU0hfVU5VU0FCTEU7Cj4+ICvCoMKg wqDCoMKgwqDCoCByZXR1cm4gTlZNRV9TQ19BVVRIX1JFUVVJUkVEOwo+PiArwqDCoMKgIH0KPiAK PiBuZXdsaW5lCj4gCj4+ICvCoMKgwqAgaWYgKGNoYXAtPmhhc2hfaWQgIT0gZGF0YS0+aGFzaGlk KSB7Cj4+ICvCoMKgwqDCoMKgwqDCoCBrZnJlZShjaGFwLT5ob3N0X3Jlc3BvbnNlKTsKPiAKPiBr ZnJlZV9zZW5zaXRpdmU/IGFsc28gd2h5IGlzIGlzIGZyZWVkIGhlcmU/IHdoZXJlIHdhcyBpdCBh bGxvY2F0ZWQ/Cj4gClRoaXMgaXMgZ2VuZXJhdGVkIHdoZW4gY2FsY3VsYXRpbmcgdGhlIGhvc3Qg cmVzcG9uc2UgaW4KbnZtZV9hdXRoX2RoY2hhcF9ob3N0X3Jlc3BvbnNlKCkuCgo+PiArwqDCoMKg wqDCoMKgwqAgY2hhcC0+aG9zdF9yZXNwb25zZSA9IE5VTEw7Cj4+ICvCoMKgwqAgfQo+PiArwqDC oMKgIGNoYXAtPmhhc2hfaWQgPSBkYXRhLT5oYXNoaWQ7Cj4+ICvCoMKgwqAgY2hhcC0+aGFzaF9s ZW4gPSBkYXRhLT5obDsKPj4gK8KgwqDCoCBkZXZfZGJnKGN0cmwtPmRldmljZSwgInFpZCAlZDog c2VsZWN0ZWQgaGFzaCAlc1xuIiwKPj4gK8KgwqDCoMKgwqDCoMKgIGNoYXAtPnFpZCwgaG1hY19u YW1lKTsKPj4gKwo+PiArwqDCoMKgIGdpZF9uYW1lID0gbnZtZV9hdXRoX2RoZ3JvdXBfa3BwKGRh dGEtPmRoZ2lkKTsKPj4gK8KgwqDCoCBpZiAoIWdpZF9uYW1lKSB7Cj4+ICvCoMKgwqDCoMKgwqDC oCBkZXZfd2FybihjdHJsLT5kZXZpY2UsCj4+ICvCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqAgInFp ZCAlZDogaW52YWxpZCBESCBncm91cCBpZCAlZFxuIiwKPj4gK8KgwqDCoMKgwqDCoMKgwqDCoMKg wqDCoCBjaGFwLT5xaWQsIGRhdGEtPmRoZ2lkKTsKPj4gK8KgwqDCoMKgwqDCoMKgIGNoYXAtPnN0 YXR1cyA9IE5WTUVfQVVUSF9ESENIQVBfRkFJTFVSRV9ESEdST1VQX1VOVVNBQkxFOwo+PiArwqDC oMKgwqDCoMKgwqAgcmV0dXJuIC1FUFJPVE87Cj4gCj4gTm8gbmVlZCBmb3IgYWxsIHRoZSBwcmV2 aW91cyBmcmVlcz8KPiBNYXliZSB3ZSBjYW4gcmV3b3JrIHRoZXNlIHN1Y2ggdGhhdCB3ZSBmaXJz dCBkbyBhbGwgdGhlIGNoZWNrcyBhbmQgdGhlbgo+IGdvIGFuZCBhbGxvY2F0ZSBzdHVmZj8KPiAK CkhtbS4gV2lsbCBoYXZlIGEgbG9vayBpZiB0aGF0IGlzIGZlYXNpYmxlLgoKPj4gK8KgwqDCoCB9 Cj4+ICsKPj4gK8KgwqDCoCBpZiAoZGF0YS0+ZGhnaWQgIT0gTlZNRV9BVVRIX0RIQ0hBUF9ESEdS T1VQX05VTEwpIHsKPj4gK8KgwqDCoMKgwqDCoMKgIGlmIChkYXRhLT5kaHZsZW4gPT0gMCkgewo+ PiArwqDCoMKgwqDCoMKgwqDCoMKgwqDCoCBkZXZfd2FybihjdHJsLT5kZXZpY2UsCj4+ICvCoMKg wqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoCAicWlkICVkOiBlbXB0eSBESCB2YWx1ZVxuIiwK Pj4gK8KgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgIGNoYXAtPnFpZCk7Cj4+ICvCoMKg wqDCoMKgwqDCoMKgwqDCoMKgIGNoYXAtPnN0YXR1cyA9IE5WTUVfQVVUSF9ESENIQVBfRkFJTFVS RV9ESEdST1VQX1VOVVNBQkxFOwo+PiArwqDCoMKgwqDCoMKgwqDCoMKgwqDCoCByZXR1cm4gLUVQ Uk9UTzsKPj4gK8KgwqDCoMKgwqDCoMKgIH0KPj4gK8KgwqDCoMKgwqDCoMKgIGNoYXAtPmRoX3Rm bSA9IGNyeXB0b19hbGxvY19rcHAoZ2lkX25hbWUsIDAsIDApOwo+PiArwqDCoMKgwqDCoMKgwqAg aWYgKElTX0VSUihjaGFwLT5kaF90Zm0pKSB7Cj4+ICvCoMKgwqDCoMKgwqDCoMKgwqDCoMKgIGlu dCByZXQgPSBQVFJfRVJSKGNoYXAtPmRoX3RmbSk7Cj4+ICsKPj4gK8KgwqDCoMKgwqDCoMKgwqDC oMKgwqAgZGV2X3dhcm4oY3RybC0+ZGV2aWNlLAo+PiArwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKg wqDCoMKgwqAgInFpZCAlZDogZmFpbGVkIHRvIGluaXRpYWxpemUgJXNcbiIsCj4+ICvCoMKgwqDC oMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoCBjaGFwLT5xaWQsIGdpZF9uYW1lKTsKPj4gK8KgwqDC oMKgwqDCoMKgwqDCoMKgwqAgY2hhcC0+c3RhdHVzID0gTlZNRV9BVVRIX0RIQ0hBUF9GQUlMVVJF X0RIR1JPVVBfVU5VU0FCTEU7Cj4+ICvCoMKgwqDCoMKgwqDCoMKgwqDCoMKgIGNoYXAtPmRoX3Rm bSA9IE5VTEw7Cj4+ICvCoMKgwqDCoMKgwqDCoMKgwqDCoMKgIHJldHVybiByZXQ7Cj4+ICvCoMKg wqDCoMKgwqDCoCB9Cj4+ICvCoMKgwqDCoMKgwqDCoCBjaGFwLT5kaGdyb3VwX2lkID0gZGF0YS0+ ZGhnaWQ7Cj4+ICvCoMKgwqAgfSBlbHNlIGlmIChkYXRhLT5kaHZsZW4gIT0gMCkgewo+PiArwqDC oMKgwqDCoMKgwqAgZGV2X3dhcm4oY3RybC0+ZGV2aWNlLAo+PiArwqDCoMKgwqDCoMKgwqDCoMKg wqDCoMKgICJxaWQgJWQ6IGludmFsaWQgREggdmFsdWUgZm9yIE5VTEwgREhcbiIsCj4+ICvCoMKg wqDCoMKgwqDCoMKgwqDCoMKgIGNoYXAtPnFpZCk7Cj4+ICvCoMKgwqDCoMKgwqDCoCBjaGFwLT5z dGF0dXMgPSBOVk1FX0FVVEhfREhDSEFQX0ZBSUxVUkVfREhHUk9VUF9VTlVTQUJMRTsKPj4gK8Kg wqDCoMKgwqDCoMKgIHJldHVybiAtRVBST1RPOwo+PiArwqDCoMKgIH0KPj4gK8KgwqDCoCBkZXZf ZGJnKGN0cmwtPmRldmljZSwgInFpZCAlZDogc2VsZWN0ZWQgREggZ3JvdXAgJXNcbiIsCj4+ICvC oMKgwqDCoMKgwqDCoCBjaGFwLT5xaWQsIGdpZF9uYW1lKTsKPj4gKwo+PiArc2VsZWN0X2twcDoK Pj4gK8KgwqDCoCBjaGFwLT5zMSA9IGxlMzJfdG9fY3B1KGRhdGEtPnNlcW51bSk7Cj4+ICvCoMKg wqAgbWVtY3B5KGNoYXAtPmMxLCBkYXRhLT5jdmFsLCBjaGFwLT5oYXNoX2xlbik7Cj4+ICsKPj4g K8KgwqDCoCByZXR1cm4gMDsKPj4gK30KPj4gKwo+PiArc3RhdGljIGludCBudm1lX2F1dGhfc2V0 X2RoY2hhcF9yZXBseV9kYXRhKHN0cnVjdCBudm1lX2N0cmwgKmN0cmwsCj4+ICvCoMKgwqDCoMKg wqDCoCBzdHJ1Y3QgbnZtZV9kaGNoYXBfcXVldWVfY29udGV4dCAqY2hhcCkKPj4gK3sKPj4gK8Kg wqDCoCBzdHJ1Y3QgbnZtZl9hdXRoX2RoY2hhcF9yZXBseV9kYXRhICpkYXRhID0gY2hhcC0+YnVm Owo+PiArwqDCoMKgIHNpemVfdCBzaXplID0gc2l6ZW9mKCpkYXRhKTsKPj4gKwo+PiArwqDCoMKg IHNpemUgKz0gMiAqIGNoYXAtPmhhc2hfbGVuOwo+PiArwqDCoMKgIGlmIChjdHJsLT5vcHRzLT5k aGNoYXBfYmlkaSkgewo+PiArwqDCoMKgwqDCoMKgwqAgZ2V0X3JhbmRvbV9ieXRlcyhjaGFwLT5j MiwgY2hhcC0+aGFzaF9sZW4pOwo+PiArwqDCoMKgwqDCoMKgwqAgY2hhcC0+czIgPSBudm1lX2Ro Y2hhcF9zZXFudW0rKzsKPiAKPiBBbnkgc2VyaWFsaXphdGlvbiBuZWVkZWQgb24gbnZtZV9kaGNo YXBfc2VxbnVtPwo+IAoKTWF5YmU7IHdpbGwgYmUgc3dpdGNoaW5nIHRvIGF0b21pYyBoZXJlLgpI YXZlIGJlZW4gbGF6eSAuLi4KCj4+ICvCoMKgwqAgfSBlbHNlCj4+ICvCoMKgwqDCoMKgwqDCoCBt ZW1zZXQoY2hhcC0+YzIsIDAsIGNoYXAtPmhhc2hfbGVuKTsKPj4gKwo+PiArCj4+ICvCoMKgwqAg aWYgKGNoYXAtPmJ1Zl9zaXplIDwgc2l6ZSkgewo+PiArwqDCoMKgwqDCoMKgwqAgY2hhcC0+c3Rh dHVzID0gTlZNRV9BVVRIX0RIQ0hBUF9GQUlMVVJFX0lOQ09SUkVDVF9QQVlMT0FEOwo+PiArwqDC oMKgwqDCoMKgwqAgcmV0dXJuIC1FSU5WQUw7Cj4+ICvCoMKgwqAgfQo+PiArwqDCoMKgIG1lbXNl dChjaGFwLT5idWYsIDAsIHNpemUpOwo+PiArwqDCoMKgIGRhdGEtPmF1dGhfdHlwZSA9IE5WTUVf QVVUSF9ESENIQVBfTUVTU0FHRVM7Cj4+ICvCoMKgwqAgZGF0YS0+YXV0aF9pZCA9IE5WTUVfQVVU SF9ESENIQVBfTUVTU0FHRV9SRVBMWTsKPj4gK8KgwqDCoCBkYXRhLT50X2lkID0gY3B1X3RvX2xl MTYoY2hhcC0+dHJhbnNhY3Rpb24pOwo+PiArwqDCoMKgIGRhdGEtPmhsID0gY2hhcC0+aGFzaF9s ZW47Cj4+ICvCoMKgwqAgZGF0YS0+ZGh2bGVuID0gMDsKPj4gK8KgwqDCoCBkYXRhLT5zZXFudW0g PSBjcHVfdG9fbGUzMihjaGFwLT5zMik7Cj4+ICvCoMKgwqAgbWVtY3B5KGRhdGEtPnJ2YWwsIGNo YXAtPnJlc3BvbnNlLCBjaGFwLT5oYXNoX2xlbik7Cj4+ICvCoMKgwqAgaWYgKGN0cmwtPm9wdHMt PmRoY2hhcF9iaWRpKSB7Cj4gCj4gQ2FuIHdlIHVuaXRlIHRoZSAiaWYgKGN0cmwtPm9wdHMtPmRo Y2hhcF9iaWRpKSIKPiBjb25kaXRpb25hbHM/Cj4gCgpTdXJlLgoKWyAuLiBdCj4+ICtpbnQgbnZt ZV9hdXRoX25lZ290aWF0ZShzdHJ1Y3QgbnZtZV9jdHJsICpjdHJsLCBpbnQgcWlkKQo+PiArewo+ PiArwqDCoMKgIHN0cnVjdCBudm1lX2RoY2hhcF9xdWV1ZV9jb250ZXh0ICpjaGFwOwo+PiArCj4+ ICvCoMKgwqAgaWYgKCFjdHJsLT5kaGNoYXBfa2V5IHx8ICFjdHJsLT5kaGNoYXBfa2V5X2xlbikg ewo+PiArwqDCoMKgwqDCoMKgwqAgZGV2X3dhcm4oY3RybC0+ZGV2aWNlLCAicWlkICVkOiBubyBr ZXlcbiIsIHFpZCk7Cj4+ICvCoMKgwqDCoMKgwqDCoCByZXR1cm4gLUVOT0tFWTsKPj4gK8KgwqDC oCB9Cj4+ICsKPj4gK8KgwqDCoCBtdXRleF9sb2NrKCZjdHJsLT5kaGNoYXBfYXV0aF9tdXRleCk7 Cj4+ICvCoMKgwqAgLyogQ2hlY2sgaWYgdGhlIGNvbnRleHQgaXMgYWxyZWFkeSBxdWV1ZWQgKi8K Pj4gK8KgwqDCoCBsaXN0X2Zvcl9lYWNoX2VudHJ5KGNoYXAsICZjdHJsLT5kaGNoYXBfYXV0aF9s aXN0LCBlbnRyeSkgewo+PiArwqDCoMKgwqDCoMKgwqAgaWYgKGNoYXAtPnFpZCA9PSBxaWQpIHsK Pj4gK8KgwqDCoMKgwqDCoMKgwqDCoMKgwqAgbXV0ZXhfdW5sb2NrKCZjdHJsLT5kaGNoYXBfYXV0 aF9tdXRleCk7Cj4+ICvCoMKgwqDCoMKgwqDCoMKgwqDCoMKgIHF1ZXVlX3dvcmsobnZtZV93cSwg JmNoYXAtPmF1dGhfd29yayk7Cj4+ICvCoMKgwqDCoMKgwqDCoMKgwqDCoMKgIHJldHVybiAwOwo+ PiArwqDCoMKgwqDCoMKgwqAgfQo+PiArwqDCoMKgIH0KPj4gK8KgwqDCoCBjaGFwID0ga3phbGxv YyhzaXplb2YoKmNoYXApLCBHRlBfS0VSTkVMKTsKPj4gK8KgwqDCoCBpZiAoIWNoYXApIHsKPj4g K8KgwqDCoMKgwqDCoMKgIG11dGV4X3VubG9jaygmY3RybC0+ZGhjaGFwX2F1dGhfbXV0ZXgpOwo+ PiArwqDCoMKgwqDCoMKgwqAgcmV0dXJuIC1FTk9NRU07Cj4+ICvCoMKgwqAgfQo+PiArwqDCoMKg IGNoYXAtPnFpZCA9IHFpZDsKPj4gK8KgwqDCoCBjaGFwLT5jdHJsID0gY3RybDsKPj4gKwo+PiAr wqDCoMKgIC8qCj4+ICvCoMKgwqDCoCAqIEFsbG9jYXRlIGEgbGFyZ2UgZW5vdWdoIGJ1ZmZlciBm b3IgdGhlIGVudGlyZSBuZWdvdGlhdGlvbjoKPj4gK8KgwqDCoMKgICogNGsgc2hvdWxkIGJlIGVu b3VnaCB0byBmZmRoZTgxOTIuCj4+ICvCoMKgwqDCoCAqLwo+PiArwqDCoMKgIGNoYXAtPmJ1Zl9z aXplID0gNDA5NjsKPj4gK8KgwqDCoCBjaGFwLT5idWYgPSBremFsbG9jKGNoYXAtPmJ1Zl9zaXpl LCBHRlBfS0VSTkVMKTsKPj4gK8KgwqDCoCBpZiAoIWNoYXAtPmJ1Zikgewo+PiArwqDCoMKgwqDC oMKgwqAgbXV0ZXhfdW5sb2NrKCZjdHJsLT5kaGNoYXBfYXV0aF9tdXRleCk7Cj4+ICvCoMKgwqDC oMKgwqDCoCBrZnJlZShjaGFwKTsKPj4gK8KgwqDCoMKgwqDCoMKgIHJldHVybiAtRU5PTUVNOwo+ PiArwqDCoMKgIH0KPj4gKwo+PiArwqDCoMKgIElOSVRfV09SSygmY2hhcC0+YXV0aF93b3JrLCBf X252bWVfYXV0aF93b3JrKTsKPj4gK8KgwqDCoCBsaXN0X2FkZCgmY2hhcC0+ZW50cnksICZjdHJs LT5kaGNoYXBfYXV0aF9saXN0KTsKPj4gK8KgwqDCoCBtdXRleF91bmxvY2soJmN0cmwtPmRoY2hh cF9hdXRoX211dGV4KTsKPj4gK8KgwqDCoCBxdWV1ZV93b3JrKG52bWVfd3EsICZjaGFwLT5hdXRo X3dvcmspOwo+IAo+IFdoeSBpcyB0aGUgYXV0aCBpbiBhIHdvcms/IGUuZy4gaXQgd29uJ3QgZmFp bCB0aGUgY29ubmVjdD8KPiAKRm9yIHJlLWF1dGhlbnRpY2F0aW9uLgpSZS1hdXRoZW50aWNhdGlv biBzaG91bGQgX25vdF8gZmFpbCB0aGUgY29ubmVjdGlvbiBpZiBpdCBzdG9wcyBpbiBzb21lCmlu dGVybWVkaWF0ZSBzdGVwLCBvbmx5IG9uY2UgdGhlIHRoZSBwcm90b2NvbCByYW4gdG8gY29tcGxl dGlvbiB0aGUKc3RhdHVzIGlzIHVwZGF0ZWQuCk1lYW5pbmcgdGhhdCB3ZSB3aWxsIGhhdmUgYWRk aXRpb25hbCBJL08gb25nb2luZyB3aGlsZSByZS1hdXRoZW50aWNhdGlvbgppcyBpbiBwcm9ncmVz cywgc28gd2UgY2FuJ3Qgc3RvcCBhbGwgSS9PIGhlcmUgYnV0IHJhdGhlciBuZWVkIHRvIHNoaWZ0 Cml0IG9udG8gYSB3b3JrcXVldWUuCgo+PiArwqDCoMKgIHJldHVybiAwOwo+PiArfQo+PiArRVhQ T1JUX1NZTUJPTF9HUEwobnZtZV9hdXRoX25lZ290aWF0ZSk7Cj4+ICsKPj4gK2ludCBudm1lX2F1 dGhfd2FpdChzdHJ1Y3QgbnZtZV9jdHJsICpjdHJsLCBpbnQgcWlkKQo+PiArewo+PiArwqDCoMKg IHN0cnVjdCBudm1lX2RoY2hhcF9xdWV1ZV9jb250ZXh0ICpjaGFwOwo+PiArwqDCoMKgIGludCBy ZXQ7Cj4+ICsKPj4gK8KgwqDCoCBtdXRleF9sb2NrKCZjdHJsLT5kaGNoYXBfYXV0aF9tdXRleCk7 Cj4+ICvCoMKgwqAgbGlzdF9mb3JfZWFjaF9lbnRyeShjaGFwLCAmY3RybC0+ZGhjaGFwX2F1dGhf bGlzdCwgZW50cnkpIHsKPj4gK8KgwqDCoMKgwqDCoMKgIGlmIChjaGFwLT5xaWQgIT0gcWlkKQo+ PiArwqDCoMKgwqDCoMKgwqDCoMKgwqDCoCBjb250aW51ZTsKPj4gK8KgwqDCoMKgwqDCoMKgIG11 dGV4X3VubG9jaygmY3RybC0+ZGhjaGFwX2F1dGhfbXV0ZXgpOwo+PiArwqDCoMKgwqDCoMKgwqAg Zmx1c2hfd29yaygmY2hhcC0+YXV0aF93b3JrKTsKPj4gK8KgwqDCoMKgwqDCoMKgIHJldCA9IGNo YXAtPmVycm9yOwo+PiArwqDCoMKgwqDCoMKgwqAgbnZtZV9hdXRoX3Jlc2V0KGNoYXApOwo+PiAr wqDCoMKgwqDCoMKgwqAgcmV0dXJuIHJldDsKPj4gK8KgwqDCoCB9Cj4+ICvCoMKgwqAgbXV0ZXhf dW5sb2NrKCZjdHJsLT5kaGNoYXBfYXV0aF9tdXRleCk7Cj4+ICvCoMKgwqAgcmV0dXJuIC1FTlhJ TzsKPj4gK30KPj4gK0VYUE9SVF9TWU1CT0xfR1BMKG52bWVfYXV0aF93YWl0KTsKPj4gKwo+PiAr LyogQXNzdW1lcyB0aGF0IHRoZSBjb250cm9sbGVyIGlzIGluIHN0YXRlIFJFU0VUVElORyAqLwo+ PiArc3RhdGljIHZvaWQgbnZtZV9kaGNoYXBfYXV0aF93b3JrKHN0cnVjdCB3b3JrX3N0cnVjdCAq d29yaykKPj4gK3sKPj4gK8KgwqDCoCBzdHJ1Y3QgbnZtZV9jdHJsICpjdHJsID0KPj4gK8KgwqDC oMKgwqDCoMKgIGNvbnRhaW5lcl9vZih3b3JrLCBzdHJ1Y3QgbnZtZV9jdHJsLCBkaGNoYXBfYXV0 aF93b3JrKTsKPj4gK8KgwqDCoCBpbnQgcmV0LCBxOwo+PiArCj4+ICvCoMKgwqAgbnZtZV9zdG9w X3F1ZXVlcyhjdHJsKTsKPj4gK8KgwqDCoCAvKiBBdXRoZW50aWNhdGUgYWRtaW4gcXVldWUgZmly c3QgKi8KPj4gK8KgwqDCoCByZXQgPSBudm1lX2F1dGhfbmVnb3RpYXRlKGN0cmwsIE5WTUVfUUlE X0FOWSk7Cj4+ICvCoMKgwqAgaWYgKHJldCkgewo+PiArwqDCoMKgwqDCoMKgwqAgZGV2X3dhcm4o Y3RybC0+ZGV2aWNlLAo+PiArwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgICJxaWQgMDogZXJyb3Ig JWQgc2V0dGluZyB1cCBhdXRoZW50aWNhdGlvblxuIiwgcmV0KTsKPj4gK8KgwqDCoMKgwqDCoMKg IGdvdG8gb3V0Owo+PiArwqDCoMKgIH0KPj4gK8KgwqDCoCByZXQgPSBudm1lX2F1dGhfd2FpdChj dHJsLCBOVk1FX1FJRF9BTlkpOwo+PiArwqDCoMKgIGlmIChyZXQpIHsKPj4gK8KgwqDCoMKgwqDC oMKgIGRldl93YXJuKGN0cmwtPmRldmljZSwKPj4gK8KgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoCAi cWlkIDA6IGF1dGhlbnRpY2F0aW9uIGZhaWxlZFxuIik7Cj4+ICvCoMKgwqDCoMKgwqDCoCBnb3Rv IG91dDsKPj4gK8KgwqDCoCB9Cj4+ICvCoMKgwqAgZGV2X2luZm8oY3RybC0+ZGV2aWNlLCAicWlk IDA6IGF1dGhlbnRpY2F0ZWRcbiIpOwo+PiArCj4+ICvCoMKgwqAgZm9yIChxID0gMTsgcSA8IGN0 cmwtPnF1ZXVlX2NvdW50OyBxKyspIHsKPj4gK8KgwqDCoMKgwqDCoMKgIHJldCA9IG52bWVfYXV0 aF9uZWdvdGlhdGUoY3RybCwgcSk7Cj4+ICvCoMKgwqDCoMKgwqDCoCBpZiAocmV0KSB7Cj4+ICvC oMKgwqDCoMKgwqDCoMKgwqDCoMKgIGRldl93YXJuKGN0cmwtPmRldmljZSwKPj4gK8KgwqDCoMKg wqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgICJxaWQgJWQ6IGVycm9yICVkIHNldHRpbmcgdXAgYXV0 aGVudGljYXRpb25cbiIsCj4+ICvCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoCBxLCBy ZXQpOwo+PiArwqDCoMKgwqDCoMKgwqDCoMKgwqDCoCBnb3RvIG91dDsKPj4gK8KgwqDCoMKgwqDC oMKgIH0KPj4gK8KgwqDCoCB9Cj4+ICtvdXQ6Cj4+ICvCoMKgwqAgLyoKPj4gK8KgwqDCoMKgICog RmFpbHVyZSBpcyBhIHNvZnQtc3RhdGU7IGNyZWRlbnRpYWxzIHJlbWFpbiB2YWxpZCB1bnRpbAo+ PiArwqDCoMKgwqAgKiB0aGUgY29udHJvbGxlciB0ZXJtaW5hdGVzIHRoZSBjb25uZWN0aW9uLgo+ PiArwqDCoMKgwqAgKi8KPj4gK8KgwqDCoCBpZiAobnZtZV9jaGFuZ2VfY3RybF9zdGF0ZShjdHJs LCBOVk1FX0NUUkxfTElWRSkpCj4+ICvCoMKgwqDCoMKgwqDCoCBudm1lX3N0YXJ0X3F1ZXVlcyhj dHJsKTsKPj4gK30KPj4gKwo+PiArdm9pZCBudm1lX2F1dGhfaW5pdF9jdHJsKHN0cnVjdCBudm1l X2N0cmwgKmN0cmwpCj4+ICt7Cj4+ICvCoMKgwqAgSU5JVF9MSVNUX0hFQUQoJmN0cmwtPmRoY2hh cF9hdXRoX2xpc3QpOwo+PiArwqDCoMKgIElOSVRfV09SSygmY3RybC0+ZGhjaGFwX2F1dGhfd29y aywgbnZtZV9kaGNoYXBfYXV0aF93b3JrKTsKPj4gK8KgwqDCoCBtdXRleF9pbml0KCZjdHJsLT5k aGNoYXBfYXV0aF9tdXRleCk7Cj4+ICvCoMKgwqAgbnZtZV9hdXRoX2dlbmVyYXRlX2tleShjdHJs KTsKPj4gK30KPj4gK0VYUE9SVF9TWU1CT0xfR1BMKG52bWVfYXV0aF9pbml0X2N0cmwpOwo+PiAr Cj4+ICt2b2lkIG52bWVfYXV0aF9zdG9wKHN0cnVjdCBudm1lX2N0cmwgKmN0cmwpCj4+ICt7Cj4+ ICvCoMKgwqAgc3RydWN0IG52bWVfZGhjaGFwX3F1ZXVlX2NvbnRleHQgKmNoYXAgPSBOVUxMLCAq dG1wOwo+PiArCj4+ICvCoMKgwqAgY2FuY2VsX3dvcmtfc3luYygmY3RybC0+ZGhjaGFwX2F1dGhf d29yayk7Cj4+ICvCoMKgwqAgbXV0ZXhfbG9jaygmY3RybC0+ZGhjaGFwX2F1dGhfbXV0ZXgpOwo+ PiArwqDCoMKgIGxpc3RfZm9yX2VhY2hfZW50cnlfc2FmZShjaGFwLCB0bXAsICZjdHJsLT5kaGNo YXBfYXV0aF9saXN0LCBlbnRyeSkKPj4gK8KgwqDCoMKgwqDCoMKgIGNhbmNlbF93b3JrX3N5bmMo JmNoYXAtPmF1dGhfd29yayk7Cj4+ICvCoMKgwqAgbXV0ZXhfdW5sb2NrKCZjdHJsLT5kaGNoYXBf YXV0aF9tdXRleCk7Cj4+ICt9Cj4+ICtFWFBPUlRfU1lNQk9MX0dQTChudm1lX2F1dGhfc3RvcCk7 Cj4+ICsKPj4gK3ZvaWQgbnZtZV9hdXRoX2ZyZWUoc3RydWN0IG52bWVfY3RybCAqY3RybCkKPj4g K3sKPj4gK8KgwqDCoCBzdHJ1Y3QgbnZtZV9kaGNoYXBfcXVldWVfY29udGV4dCAqY2hhcCA9IE5V TEwsICp0bXA7Cj4+ICsKPj4gK8KgwqDCoCBtdXRleF9sb2NrKCZjdHJsLT5kaGNoYXBfYXV0aF9t dXRleCk7Cj4+ICvCoMKgwqAgbGlzdF9mb3JfZWFjaF9lbnRyeV9zYWZlKGNoYXAsIHRtcCwgJmN0 cmwtPmRoY2hhcF9hdXRoX2xpc3QsCj4+IGVudHJ5KSB7Cj4+ICvCoMKgwqDCoMKgwqDCoCBsaXN0 X2RlbF9pbml0KCZjaGFwLT5lbnRyeSk7Cj4+ICvCoMKgwqDCoMKgwqDCoCBmbHVzaF93b3JrKCZj aGFwLT5hdXRoX3dvcmspOwo+PiArwqDCoMKgwqDCoMKgwqAgX19udm1lX2F1dGhfZnJlZShjaGFw KTsKPj4gK8KgwqDCoCB9Cj4+ICvCoMKgwqAgbXV0ZXhfdW5sb2NrKCZjdHJsLT5kaGNoYXBfYXV0 aF9tdXRleCk7Cj4+ICvCoMKgwqAga2ZyZWUoY3RybC0+ZGhjaGFwX2tleSk7Cj4+ICvCoMKgwqAg Y3RybC0+ZGhjaGFwX2tleSA9IE5VTEw7Cj4+ICvCoMKgwqAgY3RybC0+ZGhjaGFwX2tleV9sZW4g PSAwOwo+PiArfQo+PiArRVhQT1JUX1NZTUJPTF9HUEwobnZtZV9hdXRoX2ZyZWUpOwo+PiBkaWZm IC0tZ2l0IGEvZHJpdmVycy9udm1lL2hvc3QvYXV0aC5oIGIvZHJpdmVycy9udm1lL2hvc3QvYXV0 aC5oCj4+IG5ldyBmaWxlIG1vZGUgMTAwNjQ0Cj4+IGluZGV4IDAwMDAwMDAwMDAwMC4uY2YxMjU1 ZjlkYjZkCj4+IC0tLSAvZGV2L251bGwKPj4gKysrIGIvZHJpdmVycy9udm1lL2hvc3QvYXV0aC5o Cj4+IEBAIC0wLDAgKzEsMjUgQEAKPj4gKy8qIFNQRFgtTGljZW5zZS1JZGVudGlmaWVyOiBHUEwt Mi4wICovCj4+ICsvKgo+PiArICogQ29weXJpZ2h0IChjKSAyMDIxIEhhbm5lcyBSZWluZWNrZSwg U1VTRSBTb2Z0d2FyZSBTb2x1dGlvbnMKPj4gKyAqLwo+PiArCj4+ICsjaWZuZGVmIF9OVk1FX0FV VEhfSAo+PiArI2RlZmluZSBfTlZNRV9BVVRIX0gKPj4gKwo+PiArI2luY2x1ZGUgPGNyeXB0by9r cHAuaD4KPj4gKwo+PiArY29uc3QgY2hhciAqbnZtZV9hdXRoX2RoZ3JvdXBfbmFtZShpbnQgZGhn cm91cF9pZCk7Cj4+ICtpbnQgbnZtZV9hdXRoX2RoZ3JvdXBfcHVia2V5X3NpemUoaW50IGRoZ3Jv dXBfaWQpOwo+PiAraW50IG52bWVfYXV0aF9kaGdyb3VwX3ByaXZrZXlfc2l6ZShpbnQgZGhncm91 cF9pZCk7Cj4+ICtjb25zdCBjaGFyICpudm1lX2F1dGhfZGhncm91cF9rcHAoaW50IGRoZ3JvdXBf aWQpOwo+PiAraW50IG52bWVfYXV0aF9kaGdyb3VwX2lkKGNvbnN0IGNoYXIgKmRoZ3JvdXBfbmFt ZSk7Cj4+ICsKPj4gK2NvbnN0IGNoYXIgKm52bWVfYXV0aF9obWFjX25hbWUoaW50IGhtYWNfaWQp Owo+PiArY29uc3QgY2hhciAqbnZtZV9hdXRoX2RpZ2VzdF9uYW1lKGludCBobWFjX2lkKTsKPj4g K2ludCBudm1lX2F1dGhfaG1hY19pZChjb25zdCBjaGFyICpobWFjX25hbWUpOwo+PiArCj4+ICt1 bnNpZ25lZCBjaGFyICpudm1lX2F1dGhfZXh0cmFjdF9zZWNyZXQodW5zaWduZWQgY2hhciAqZGhj aGFwX3NlY3JldCwKPj4gK8KgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgIHNp emVfdCAqZGhjaGFwX2tleV9sZW4pOwo+PiArdTggKm52bWVfYXV0aF90cmFuc2Zvcm1fa2V5KHU4 ICprZXksIHNpemVfdCBrZXlfbGVuLCB1OCBrZXlfaGFzaCwKPj4gY2hhciAqbnFuKTsKPj4gKwo+ PiArI2VuZGlmIC8qIF9OVk1FX0FVVEhfSCAqLwo+PiBkaWZmIC0tZ2l0IGEvZHJpdmVycy9udm1l L2hvc3QvY29yZS5jIGIvZHJpdmVycy9udm1lL2hvc3QvY29yZS5jCj4+IGluZGV4IDdlZmIzMWI4 N2YzNy4uZjY2OWIwNTQ3OTBiIDEwMDY0NAo+PiAtLS0gYS9kcml2ZXJzL252bWUvaG9zdC9jb3Jl LmMKPj4gKysrIGIvZHJpdmVycy9udm1lL2hvc3QvY29yZS5jCj4+IEBAIC0yNCw2ICsyNCw3IEBA Cj4+IMKgIMKgICNpbmNsdWRlICJudm1lLmgiCj4+IMKgICNpbmNsdWRlICJmYWJyaWNzLmgiCj4+ ICsjaW5jbHVkZSAiYXV0aC5oIgo+PiDCoCDCoCAjZGVmaW5lIENSRUFURV9UUkFDRV9QT0lOVFMK Pj4gwqAgI2luY2x1ZGUgInRyYWNlLmgiCj4+IEBAIC0zMjIsNiArMzIzLDcgQEAgZW51bSBudm1l X2Rpc3Bvc2l0aW9uIHsKPj4gwqDCoMKgwqDCoCBDT01QTEVURSwKPj4gwqDCoMKgwqDCoCBSRVRS WSwKPj4gwqDCoMKgwqDCoCBGQUlMT1ZFUiwKPj4gK8KgwqDCoCBBVVRIRU5USUNBVEUsCj4+IMKg IH07Cj4+IMKgIMKgIHN0YXRpYyBpbmxpbmUgZW51bSBudm1lX2Rpc3Bvc2l0aW9uIG52bWVfZGVj aWRlX2Rpc3Bvc2l0aW9uKHN0cnVjdAo+PiByZXF1ZXN0ICpyZXEpCj4+IEBAIC0zMjksNiArMzMx LDkgQEAgc3RhdGljIGlubGluZSBlbnVtIG52bWVfZGlzcG9zaXRpb24KPj4gbnZtZV9kZWNpZGVf ZGlzcG9zaXRpb24oc3RydWN0IHJlcXVlc3QgKnJlcSkKPj4gwqDCoMKgwqDCoCBpZiAobGlrZWx5 KG52bWVfcmVxKHJlcSktPnN0YXR1cyA9PSAwKSkKPj4gwqDCoMKgwqDCoMKgwqDCoMKgIHJldHVy biBDT01QTEVURTsKPj4gwqAgK8KgwqDCoCBpZiAoKG52bWVfcmVxKHJlcSktPnN0YXR1cyAmIDB4 N2ZmKSA9PSBOVk1FX1NDX0FVVEhfUkVRVUlSRUQpCj4+ICvCoMKgwqDCoMKgwqDCoCByZXR1cm4g QVVUSEVOVElDQVRFOwo+PiArCj4+IMKgwqDCoMKgwqAgaWYgKGJsa19ub3JldHJ5X3JlcXVlc3Qo cmVxKSB8fAo+PiDCoMKgwqDCoMKgwqDCoMKgwqAgKG52bWVfcmVxKHJlcSktPnN0YXR1cyAmIE5W TUVfU0NfRE5SKSB8fAo+PiDCoMKgwqDCoMKgwqDCoMKgwqAgbnZtZV9yZXEocmVxKS0+cmV0cmll cyA+PSBudm1lX21heF9yZXRyaWVzKQo+PiBAQCAtMzYxLDExICszNjYsMTMgQEAgc3RhdGljIGlu bGluZSB2b2lkIG52bWVfZW5kX3JlcShzdHJ1Y3QgcmVxdWVzdAo+PiAqcmVxKQo+PiDCoCDCoCB2 b2lkIG52bWVfY29tcGxldGVfcnEoc3RydWN0IHJlcXVlc3QgKnJlcSkKPj4gwqAgewo+PiArwqDC oMKgIHN0cnVjdCBudm1lX2N0cmwgKmN0cmwgPSBudm1lX3JlcShyZXEpLT5jdHJsOwo+PiArCj4+ IMKgwqDCoMKgwqAgdHJhY2VfbnZtZV9jb21wbGV0ZV9ycShyZXEpOwo+PiDCoMKgwqDCoMKgIG52 bWVfY2xlYW51cF9jbWQocmVxKTsKPj4gwqAgLcKgwqDCoCBpZiAobnZtZV9yZXEocmVxKS0+Y3Ry bC0+a2FzKQo+PiAtwqDCoMKgwqDCoMKgwqAgbnZtZV9yZXEocmVxKS0+Y3RybC0+Y29tcF9zZWVu ID0gdHJ1ZTsKPj4gK8KgwqDCoCBpZiAoY3RybC0+a2FzKQo+PiArwqDCoMKgwqDCoMKgwqAgY3Ry bC0+Y29tcF9zZWVuID0gdHJ1ZTsKPj4gwqAgwqDCoMKgwqDCoCBzd2l0Y2ggKG52bWVfZGVjaWRl X2Rpc3Bvc2l0aW9uKHJlcSkpIHsKPj4gwqDCoMKgwqDCoCBjYXNlIENPTVBMRVRFOgo+PiBAQCAt Mzc3LDYgKzM4NCwxNSBAQCB2b2lkIG52bWVfY29tcGxldGVfcnEoc3RydWN0IHJlcXVlc3QgKnJl cSkKPj4gwqDCoMKgwqDCoCBjYXNlIEZBSUxPVkVSOgo+PiDCoMKgwqDCoMKgwqDCoMKgwqAgbnZt ZV9mYWlsb3Zlcl9yZXEocmVxKTsKPj4gwqDCoMKgwqDCoMKgwqDCoMKgIHJldHVybjsKPj4gK8Kg wqDCoCBjYXNlIEFVVEhFTlRJQ0FURToKPj4gKyNpZmRlZiBDT05GSUdfTlZNRV9BVVRICj4+ICvC oMKgwqDCoMKgwqDCoCBpZiAobnZtZV9jaGFuZ2VfY3RybF9zdGF0ZShjdHJsLCBOVk1FX0NUUkxf UkVTRVRUSU5HKSkKPj4gK8KgwqDCoMKgwqDCoMKgwqDCoMKgwqAgcXVldWVfd29yayhudm1lX3dx LCAmY3RybC0+ZGhjaGFwX2F1dGhfd29yayk7Cj4gCj4gV2h5IGlzIHRoZSBzdGF0ZSBjaGFuZ2Ug aGVyZSBhbmQgbm90IGluIG52bWVfZGhjaGFwX2F1dGhfd29yaz8KPiAKQmVjYXVzZSBzd2l0Y2hp bmcgdG8gJ3Jlc2V0dGluZycgaXMgYW4gZWFzeSB3YXkgdG8gc3luY2hyb25pemUgd2l0aCB0aGUK YWRtaW4gcXVldWUuCgo+PiArwqDCoMKgwqDCoMKgwqAgbnZtZV9yZXRyeV9yZXEocmVxKTsKPj4g KyNlbHNlCj4+ICvCoMKgwqDCoMKgwqDCoCBudm1lX2VuZF9yZXEocmVxKTsKPj4gKyNlbmRpZgo+ PiArwqDCoMKgwqDCoMKgwqAgcmV0dXJuOwo+PiDCoMKgwqDCoMKgIH0KPj4gwqAgfQo+PiDCoCBF WFBPUlRfU1lNQk9MX0dQTChudm1lX2NvbXBsZXRlX3JxKTsKPj4gQEAgLTcwNyw3ICs3MjMsOSBA QCBib29sIF9fbnZtZV9jaGVja19yZWFkeShzdHJ1Y3QgbnZtZV9jdHJsICpjdHJsLAo+PiBzdHJ1 Y3QgcmVxdWVzdCAqcnEsCj4+IMKgwqDCoMKgwqDCoMKgwqDCoCBzd2l0Y2ggKGN0cmwtPnN0YXRl KSB7Cj4+IMKgwqDCoMKgwqDCoMKgwqDCoCBjYXNlIE5WTUVfQ1RSTF9DT05ORUNUSU5HOgo+PiDC oMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoCBpZiAoYmxrX3JxX2lzX3Bhc3N0aHJvdWdoKHJxKSAm Jgo+PiBudm1lX2lzX2ZhYnJpY3MocmVxLT5jbWQpICYmCj4+IC3CoMKgwqDCoMKgwqDCoMKgwqDC oMKgwqDCoMKgwqAgcmVxLT5jbWQtPmZhYnJpY3MuZmN0eXBlID09IG52bWVfZmFicmljc190eXBl X2Nvbm5lY3QpCj4+ICvCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqAgKHJlcS0+Y21kLT5m YWJyaWNzLmZjdHlwZSA9PQo+PiBudm1lX2ZhYnJpY3NfdHlwZV9jb25uZWN0IHx8Cj4+ICvCoMKg wqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoCByZXEtPmNtZC0+ZmFicmljcy5mY3R5cGUgPT0K Pj4gbnZtZV9mYWJyaWNzX3R5cGVfYXV0aF9zZW5kIHx8Cj4+ICvCoMKgwqDCoMKgwqDCoMKgwqDC oMKgwqDCoMKgwqDCoCByZXEtPmNtZC0+ZmFicmljcy5mY3R5cGUgPT0KPj4gbnZtZV9mYWJyaWNz X3R5cGVfYXV0aF9yZWNlaXZlKSkKPiAKPiBXaGF0IGhhcHBlbnMgaWYgdGhlIGF1dGggY29tbWFu ZCBjb21lcyBiZWZvcmUgdGhlIGNvbm5lY3QgKHNheSBpbiBjYXNlCj4gb2YgY3RybCByZXNldCB3 aGVuIGF1dGggd2FzIGFscmVhZHkgcXVldWVkIGJ1dCBub3QgeWV0IGV4ZWN1dGVkPwo+IApTZWUg YmVsb3cuCgo+PiDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgIHJldHVybiB0cnVl Owo+PiDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoCBicmVhazsKPj4gwqDCoMKgwqDCoMKgwqDC oMKgIGRlZmF1bHQ6Cj4+IEBAIC0zNDU4LDYgKzM0NzYsNTEgQEAgc3RhdGljIHNzaXplX3QKPj4g bnZtZV9jdHJsX2Zhc3RfaW9fZmFpbF90bW9fc3RvcmUoc3RydWN0IGRldmljZSAqZGV2LAo+PiDC oCBzdGF0aWMgREVWSUNFX0FUVFIoZmFzdF9pb19mYWlsX3RtbywgU19JUlVHTyB8IFNfSVdVU1Is Cj4+IMKgwqDCoMKgwqAgbnZtZV9jdHJsX2Zhc3RfaW9fZmFpbF90bW9fc2hvdywgbnZtZV9jdHJs X2Zhc3RfaW9fZmFpbF90bW9fc3RvcmUpOwo+PiDCoCArI2lmZGVmIENPTkZJR19OVk1FX0FVVEgK Pj4gK3N0YXRpYyBzc2l6ZV90IG52bWVfY3RybF9kaGNoYXBfc2VjcmV0X3Nob3coc3RydWN0IGRl dmljZSAqZGV2LAo+PiArwqDCoMKgwqDCoMKgwqAgc3RydWN0IGRldmljZV9hdHRyaWJ1dGUgKmF0 dHIsIGNoYXIgKmJ1ZikKPj4gK3sKPj4gK8KgwqDCoCBzdHJ1Y3QgbnZtZV9jdHJsICpjdHJsID0g ZGV2X2dldF9kcnZkYXRhKGRldik7Cj4+ICvCoMKgwqAgc3RydWN0IG52bWZfY3RybF9vcHRpb25z ICpvcHRzID0gY3RybC0+b3B0czsKPj4gKwo+PiArwqDCoMKgIGlmICghb3B0cy0+ZGhjaGFwX3Nl Y3JldCkKPj4gK8KgwqDCoMKgwqDCoMKgIHJldHVybiBzeXNmc19lbWl0KGJ1ZiwgIm5vbmVcbiIp Owo+PiArwqDCoMKgIHJldHVybiBzeXNmc19lbWl0KGJ1ZiwgIiVzXG4iLCBvcHRzLT5kaGNoYXBf c2VjcmV0KTsKPiAKPiBTaG91bGQgd2UgYWN0dWFsbHkgc2hvdyB0aGlzPyBkb24ndCBrbm93IGVu b3VnaCBob3cgbXVjaCB0aGUgc2VjcmV0Cj4gc2hvdWxkIGJlIGtlcHQgYSBzZWNyZXQuLi4KPiAK SSBmb3VuZCBpdCBsb2dpY2FsLCBhcyB3ZSBuZWVkIHRoZSAnc3RvcmUnIGZ1bmN0aW9uYWxpdHkg dG8gdHJpZ2dlcgpyZS1hdXRoZW50aWNhdGlvbi4KQnV0IHN1cmUsIHdlIGNhbiBtYWtlIHRoaXMg YSB3cml0ZS1vbmx5IGF0dHJpYnV0ZS4KCj4+ICt9Cj4+ICsKPj4gK3N0YXRpYyBzc2l6ZV90IG52 bWVfY3RybF9kaGNoYXBfc2VjcmV0X3N0b3JlKHN0cnVjdCBkZXZpY2UgKmRldiwKPj4gK8KgwqDC oMKgwqDCoMKgIHN0cnVjdCBkZXZpY2VfYXR0cmlidXRlICphdHRyLCBjb25zdCBjaGFyICpidWYs IHNpemVfdCBjb3VudCkKPj4gK3sKPj4gK8KgwqDCoCBzdHJ1Y3QgbnZtZV9jdHJsICpjdHJsID0g ZGV2X2dldF9kcnZkYXRhKGRldik7Cj4+ICvCoMKgwqAgc3RydWN0IG52bWZfY3RybF9vcHRpb25z ICpvcHRzID0gY3RybC0+b3B0czsKPj4gK8KgwqDCoCBjaGFyICpkaGNoYXBfc2VjcmV0Owo+PiAr Cj4+ICvCoMKgwqAgaWYgKCFjdHJsLT5vcHRzLT5kaGNoYXBfc2VjcmV0KQo+PiArwqDCoMKgwqDC oMKgwqAgcmV0dXJuIC1FSU5WQUw7Cj4+ICvCoMKgwqAgaWYgKGNvdW50IDwgNykKPj4gK8KgwqDC oMKgwqDCoMKgIHJldHVybiAtRUlOVkFMOwo+PiArwqDCoMKgIGlmIChtZW1jbXAoYnVmLCAiREhI Qy0xOiIsIDcpKQo+PiArwqDCoMKgwqDCoMKgwqAgcmV0dXJuIC1FSU5WQUw7Cj4+ICsKPj4gK8Kg wqDCoCBkaGNoYXBfc2VjcmV0ID0ga3phbGxvYyhjb3VudCArIDEsIEdGUF9LRVJORUwpOwo+PiAr wqDCoMKgIGlmICghZGhjaGFwX3NlY3JldCkKPj4gK8KgwqDCoMKgwqDCoMKgIHJldHVybiAtRU5P TUVNOwo+PiArwqDCoMKgIG1lbWNweShkaGNoYXBfc2VjcmV0LCBidWYsIGNvdW50KTsKPj4gK8Kg wqDCoCBpZiAoc3RyY21wKGRoY2hhcF9zZWNyZXQsIG9wdHMtPmRoY2hhcF9zZWNyZXQpKSB7Cj4+ ICvCoMKgwqDCoMKgwqDCoCBrZnJlZShvcHRzLT5kaGNoYXBfc2VjcmV0KTsKPj4gK8KgwqDCoMKg wqDCoMKgIG9wdHMtPmRoY2hhcF9zZWNyZXQgPSBkaGNoYXBfc2VjcmV0Owo+PiArwqDCoMKgwqDC oMKgwqAgLyogS2V5IGhhcyBjaGFuZ2VkOyByZXNldCBhdXRoZW50aWNhdGlvbiBkYXRhICovCj4+ ICvCoMKgwqDCoMKgwqDCoCBudm1lX2F1dGhfZnJlZShjdHJsKTsKPj4gK8KgwqDCoMKgwqDCoMKg IG52bWVfYXV0aF9nZW5lcmF0ZV9rZXkoY3RybCk7Cj4+ICvCoMKgwqAgfQo+IAo+IE5pY2UsIHdv cnRoIGEgY29tbWVudCAiLyogUmUtYXV0aGVudGljYXRpb24gd2l0aCBuZXcgc2VjcmV0ICovIgo+ IApSaWdodCwgd2lsbCBkby4KClRoYW5rcyBmb3IgdGhlIHJldmlldyEKCkNoZWVycywKCkhhbm5l cwotLSAKRHIuIEhhbm5lcyBSZWluZWNrZQkJICAgICAgICAgICBLZXJuZWwgU3RvcmFnZSBBcmNo aXRlY3QKaGFyZUBzdXNlLmRlCQkJICAgICAgICAgICAgICAgICAgKzQ5IDkxMSA3NDA1MyA2ODgK U1VTRSBTb2Z0d2FyZSBTb2x1dGlvbnMgR2VybWFueSBHbWJILCBNYXhmZWxkc3RyLiA1LCA5MDQw OSBOw7xybmJlcmcKSFJCIDM2ODA5IChBRyBOw7xybmJlcmcpLCBHRjogRmVsaXggSW1lbmTDtnJm ZmVyCgpfX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fXwpMaW51 eC1udm1lIG1haWxpbmcgbGlzdApMaW51eC1udm1lQGxpc3RzLmluZnJhZGVhZC5vcmcKaHR0cDov L2xpc3RzLmluZnJhZGVhZC5vcmcvbWFpbG1hbi9saXN0aW5mby9saW51eC1udm1lCg== From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-24.2 required=3.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER, INCLUDES_PATCH,MAILING_LIST_MULTI,MENTIONS_GIT_HOSTING,NICE_REPLY_A, SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED,USER_AGENT_SANE_1 autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id AEB6EC433FE for ; Mon, 13 Sep 2021 14:35:41 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 8C89060E08 for ; Mon, 13 Sep 2021 14:35:41 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1344643AbhIMOgz (ORCPT ); Mon, 13 Sep 2021 10:36:55 -0400 Received: from smtp-out2.suse.de ([195.135.220.29]:36814 "EHLO smtp-out2.suse.de" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1347620AbhIMOfI (ORCPT ); Mon, 13 Sep 2021 10:35:08 -0400 Received: from imap2.suse-dmz.suse.de (imap2.suse-dmz.suse.de [192.168.254.74]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (P-521) server-digest SHA512) (No client certificate requested) by smtp-out2.suse.de (Postfix) with ESMTPS id 6B7E61FFF3; Mon, 13 Sep 2021 14:33:49 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=suse.de; s=susede2_rsa; t=1631543629; h=from:from:reply-to:date:date:message-id:message-id:to:to:cc:cc: mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=m+cKYDqbN1hfJpxPVdUZwY4A4uTNJ4ohno+8htWkd/Q=; b=uHrDxLgGlWP2uf5nSgEmqGZb59v/LyptRLeMWN5mAbkNnty5aX/Mg/4FkZ+krWgGhjauNo LLiWBob6/6IZWl+Su9UJ7fLO0wmqCWahnO4LIDLcoNyzbvzmJRRiPXvT1CpGKmTmVvX3LM G/FIGxqPxmoQEq/PF+IO5N1oz/ug9s8= DKIM-Signature: v=1; a=ed25519-sha256; c=relaxed/relaxed; d=suse.de; s=susede2_ed25519; t=1631543629; h=from:from:reply-to:date:date:message-id:message-id:to:to:cc:cc: mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=m+cKYDqbN1hfJpxPVdUZwY4A4uTNJ4ohno+8htWkd/Q=; b=1d5S6Wch0ovgUBHK0pyzW4uEPB8xbbO2obMowCdtXqgaZtu8F7x/mKy6felDN4P5NaiXjz CAapje14+qoi/PAg== Received: from imap2.suse-dmz.suse.de (imap2.suse-dmz.suse.de [192.168.254.74]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (P-521) server-digest SHA512) (No client certificate requested) by imap2.suse-dmz.suse.de (Postfix) with ESMTPS id 49A3313AB9; Mon, 13 Sep 2021 14:33:49 +0000 (UTC) Received: from dovecot-director2.suse.de ([192.168.254.65]) by imap2.suse-dmz.suse.de with ESMTPSA id 6IS3EU1hP2H8ZwAAMHmgww (envelope-from ); Mon, 13 Sep 2021 14:33:49 +0000 To: Sagi Grimberg , Christoph Hellwig Cc: Keith Busch , Herbert Xu , "David S . Miller" , linux-nvme@lists.infradead.org, linux-crypto@vger.kernel.org References: <20210910064322.67705-1-hare@suse.de> <20210910064322.67705-8-hare@suse.de> <99cbf790-c276-b3d0-6140-1f5bfa8665eb@grimberg.me> From: Hannes Reinecke Subject: Re: [PATCH 07/12] nvme: Implement In-Band authentication Message-ID: <8bff9a88-a5d4-d7bb-8ce9-81d30438bfbb@suse.de> Date: Mon, 13 Sep 2021 16:33:48 +0200 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Thunderbird/78.12.0 MIME-Version: 1.0 In-Reply-To: <99cbf790-c276-b3d0-6140-1f5bfa8665eb@grimberg.me> Content-Type: text/plain; charset=utf-8 Content-Language: en-US Content-Transfer-Encoding: 8bit Precedence: bulk List-ID: X-Mailing-List: linux-crypto@vger.kernel.org On 9/13/21 3:55 PM, Sagi Grimberg wrote: > > > On 9/10/21 9:43 AM, Hannes Reinecke wrote: >> Implement NVMe-oF In-Band authentication according to NVMe TPAR 8006. >> This patch adds two new fabric options 'dhchap_secret' to specify the >> pre-shared key (in ASCII respresentation according to NVMe 2.0 section >> 8.13.5.8 'Secret representation') and 'dhchap_bidi' to request >> bi-directional >> authentication of both the host and the controller. >> Re-authentication can be triggered by writing the PSK into the new >> controller sysfs attribute 'dhchap_secret'. >> >> Signed-off-by: Hannes Reinecke >> --- >>   drivers/nvme/host/Kconfig   |   12 + >>   drivers/nvme/host/Makefile  |    1 + >>   drivers/nvme/host/auth.c    | 1285 +++++++++++++++++++++++++++++++++++ >>   drivers/nvme/host/auth.h    |   25 + >>   drivers/nvme/host/core.c    |   79 ++- >>   drivers/nvme/host/fabrics.c |   73 +- >>   drivers/nvme/host/fabrics.h |    6 + >>   drivers/nvme/host/nvme.h    |   30 + >>   drivers/nvme/host/trace.c   |   32 + >>   9 files changed, 1537 insertions(+), 6 deletions(-) >>   create mode 100644 drivers/nvme/host/auth.c >>   create mode 100644 drivers/nvme/host/auth.h >> >> diff --git a/drivers/nvme/host/Kconfig b/drivers/nvme/host/Kconfig >> index dc0450ca23a3..97e8412dc42d 100644 >> --- a/drivers/nvme/host/Kconfig >> +++ b/drivers/nvme/host/Kconfig >> @@ -83,3 +83,15 @@ config NVME_TCP >>         from https://github.com/linux-nvme/nvme-cli. >>           If unsure, say N. >> + >> +config NVME_AUTH >> +    bool "NVM Express over Fabrics In-Band Authentication" >> +    depends on NVME_CORE >> +    select CRYPTO_HMAC >> +    select CRYPTO_SHA256 >> +    select CRYPTO_SHA512 >> +    help >> +      This provides support for NVMe over Fabrics In-Band Authentication >> +      for the NVMe over TCP transport. > > Not tcp specific... > >> diff --git a/drivers/nvme/host/auth.c b/drivers/nvme/host/auth.c >> new file mode 100644 >> index 000000000000..5393ac16a002 >> --- /dev/null >> +++ b/drivers/nvme/host/auth.c >> @@ -0,0 +1,1285 @@ >> +// SPDX-License-Identifier: GPL-2.0 >> +/* >> + * Copyright (c) 2020 Hannes Reinecke, SUSE Linux >> + */ >> + >> +#include >> +#include >> +#include >> +#include >> +#include >> +#include >> +#include "nvme.h" >> +#include "fabrics.h" >> +#include "auth.h" >> + >> +static u32 nvme_dhchap_seqnum; >> + >> +struct nvme_dhchap_queue_context { >> +    struct list_head entry; >> +    struct work_struct auth_work; >> +    struct nvme_ctrl *ctrl; >> +    struct crypto_shash *shash_tfm; >> +    struct crypto_kpp *dh_tfm; >> +    void *buf; >> +    size_t buf_size; >> +    int qid; >> +    int error; >> +    u32 s1; >> +    u32 s2; >> +    u16 transaction; >> +    u8 status; >> +    u8 hash_id; >> +    u8 hash_len; >> +    u8 dhgroup_id; >> +    u8 c1[64]; >> +    u8 c2[64]; >> +    u8 response[64]; >> +    u8 *host_response; >> +}; >> + >> +static struct nvme_auth_dhgroup_map { >> +    int id; >> +    const char name[16]; >> +    const char kpp[16]; >> +    int privkey_size; >> +    int pubkey_size; >> +} dhgroup_map[] = { >> +    { .id = NVME_AUTH_DHCHAP_DHGROUP_NULL, >> +      .name = "NULL", .kpp = "NULL", > > Nit, no need for all-caps, can do "null" > Right. Will be doing so. [ .. ] >> +unsigned char *nvme_auth_extract_secret(unsigned char *secret, size_t >> *out_len) >> +{ >> +    unsigned char *key; >> +    u32 crc; >> +    int key_len; >> +    size_t allocated_len; >> + >> +    allocated_len = strlen(secret); > > Can move to declaration initializer. > Sure. >> +    key = kzalloc(allocated_len, GFP_KERNEL); >> +    if (!key) >> +        return ERR_PTR(-ENOMEM); >> + >> +    key_len = base64_decode(secret, allocated_len, key); >> +    if (key_len != 36 && key_len != 52 && >> +        key_len != 68) { >> +        pr_debug("Invalid DH-HMAC-CHAP key len %d\n", >> +             key_len); >> +        kfree_sensitive(key); >> +        return ERR_PTR(-EINVAL); >> +    } >> + >> +    /* The last four bytes is the CRC in little-endian format */ >> +    key_len -= 4; >> +    /* >> +     * The linux implementation doesn't do pre- and post-increments, >> +     * so we have to do it manually. >> +     */ >> +    crc = ~crc32(~0, key, key_len); >> + >> +    if (get_unaligned_le32(key + key_len) != crc) { >> +        pr_debug("DH-HMAC-CHAP key crc mismatch (key %08x, crc %08x)\n", >> +               get_unaligned_le32(key + key_len), crc); >> +        kfree_sensitive(key); >> +        return ERR_PTR(-EKEYREJECTED); >> +    } >> +    *out_len = key_len; >> +    return key; >> +} >> +EXPORT_SYMBOL_GPL(nvme_auth_extract_secret); >> + >> +u8 *nvme_auth_transform_key(u8 *key, size_t key_len, u8 key_hash, >> char *nqn) >> +{ >> +    const char *hmac_name = nvme_auth_hmac_name(key_hash); >> +    struct crypto_shash *key_tfm; >> +    struct shash_desc *shash; >> +    u8 *transformed_key; >> +    int ret; >> + >> +    /* No key transformation required */ >> +    if (key_hash == 0) >> +        return 0; >> + >> +    hmac_name = nvme_auth_hmac_name(key_hash); >> +    if (!hmac_name) { >> +        pr_warn("Invalid key hash id %d\n", key_hash); >> +        return ERR_PTR(-EKEYREJECTED); >> +    } > > newline here. > >> +    key_tfm = crypto_alloc_shash(hmac_name, 0, 0); >> +    if (IS_ERR(key_tfm)) >> +        return (u8 *)key_tfm; >> + >> +    shash = kmalloc(sizeof(struct shash_desc) + >> +            crypto_shash_descsize(key_tfm), >> +            GFP_KERNEL); >> +    if (!shash) { >> +        crypto_free_shash(key_tfm); >> +        return ERR_PTR(-ENOMEM); >> +    } > > newline here. > >> +    transformed_key = kzalloc(crypto_shash_digestsize(key_tfm), >> GFP_KERNEL); >> +    if (!transformed_key) { >> +        ret = -ENOMEM; >> +        goto out_free_shash; >> +    } >> + >> +    shash->tfm = key_tfm; >> +    ret = crypto_shash_setkey(key_tfm, key, key_len); >> +    if (ret < 0) >> +        goto out_free_shash; >> +    ret = crypto_shash_init(shash); >> +    if (ret < 0) >> +        goto out_free_shash; >> +    ret = crypto_shash_update(shash, nqn, strlen(nqn)); >> +    if (ret < 0) >> +        goto out_free_shash; >> +    ret = crypto_shash_update(shash, "NVMe-over-Fabrics", 17); >> +    if (ret < 0) >> +        goto out_free_shash; >> +    ret = crypto_shash_final(shash, transformed_key); >> +out_free_shash: >> +    kfree(shash); >> +    crypto_free_shash(key_tfm); >> +    if (ret < 0) { >> +        kfree_sensitive(transformed_key); >> +        return ERR_PTR(ret); >> +    } > > Any reason why this is not a reverse cleanup with goto call-sites > standard style? > None in particular. Will be doing so. >> +    return transformed_key; >> +} >> +EXPORT_SYMBOL_GPL(nvme_auth_transform_key); >> + >> +static int nvme_auth_hash_skey(int hmac_id, u8 *skey, size_t >> skey_len, u8 *hkey) >> +{ >> +    const char *digest_name; >> +    struct crypto_shash *tfm; >> +    int ret; >> + >> +    digest_name = nvme_auth_digest_name(hmac_id); >> +    if (!digest_name) { >> +        pr_debug("%s: failed to get digest for %d\n", __func__, >> +             hmac_id); >> +        return -EINVAL; >> +    } >> +    tfm = crypto_alloc_shash(digest_name, 0, 0); >> +    if (IS_ERR(tfm)) >> +        return -ENOMEM; >> + >> +    ret = crypto_shash_tfm_digest(tfm, skey, skey_len, hkey); >> +    if (ret < 0) >> +        pr_debug("%s: Failed to hash digest len %zu\n", __func__, >> +             skey_len); >> + >> +    crypto_free_shash(tfm); >> +    return ret; >> +} >> + >> +int nvme_auth_augmented_challenge(u8 hmac_id, u8 *skey, size_t skey_len, >> +        u8 *challenge, u8 *aug, size_t hlen) >> +{ >> +    struct crypto_shash *tfm; >> +    struct shash_desc *desc; >> +    u8 *hashed_key; >> +    const char *hmac_name; >> +    int ret; >> + >> +    hashed_key = kmalloc(hlen, GFP_KERNEL); >> +    if (!hashed_key) >> +        return -ENOMEM; >> + >> +    ret = nvme_auth_hash_skey(hmac_id, skey, >> +                  skey_len, hashed_key); >> +    if (ret < 0) >> +        goto out_free_key; >> + >> +    hmac_name = nvme_auth_hmac_name(hmac_id); >> +    if (!hmac_name) { >> +        pr_warn("%s: invalid hash algoritm %d\n", >> +            __func__, hmac_id); >> +        ret = -EINVAL; >> +        goto out_free_key; >> +    } > > newline. > >> +    tfm = crypto_alloc_shash(hmac_name, 0, 0); >> +    if (IS_ERR(tfm)) { >> +        ret = PTR_ERR(tfm); >> +        goto out_free_key; >> +    } > > newline > >> +    desc = kmalloc(sizeof(struct shash_desc) + >> crypto_shash_descsize(tfm), >> +               GFP_KERNEL); >> +    if (!desc) { >> +        ret = -ENOMEM; >> +        goto out_free_hash; >> +    } >> +    desc->tfm = tfm; >> + >> +    ret = crypto_shash_setkey(tfm, hashed_key, hlen); >> +    if (ret) >> +        goto out_free_desc; >> + >> +    ret = crypto_shash_init(desc); >> +    if (ret) >> +        goto out_free_desc; >> + >> +    ret = crypto_shash_update(desc, challenge, hlen); >> +    if (ret) >> +        goto out_free_desc; >> + >> +    ret = crypto_shash_final(desc, aug); >> +out_free_desc: >> +    kfree_sensitive(desc); >> +out_free_hash: >> +    crypto_free_shash(tfm); >> +out_free_key: >> +    kfree_sensitive(hashed_key); >> +    return ret; >> +} >> +EXPORT_SYMBOL_GPL(nvme_auth_augmented_challenge); >> + >> +int nvme_auth_gen_privkey(struct crypto_kpp *dh_tfm, int dh_gid) >> +{ >> +    char *pkey; >> +    int ret, pkey_len; >> + >> +    if (dh_gid == NVME_AUTH_DHCHAP_DHGROUP_2048 || >> +        dh_gid == NVME_AUTH_DHCHAP_DHGROUP_3072 || >> +        dh_gid == NVME_AUTH_DHCHAP_DHGROUP_4096 || >> +        dh_gid == NVME_AUTH_DHCHAP_DHGROUP_6144 || >> +        dh_gid == NVME_AUTH_DHCHAP_DHGROUP_8192) { >> +        struct dh p = {0}; >> +        int bits = nvme_auth_dhgroup_pubkey_size(dh_gid) << 3; >> +        int dh_secret_len = 64; >> +        u8 *dh_secret = kzalloc(dh_secret_len, GFP_KERNEL); >> + >> +        if (!dh_secret) >> +            return -ENOMEM; >> + >> +        /* >> +         * NVMe base spec v2.0: The DH value shall be set to the value >> +         * of g^x mod p, where 'x' is a random number selected by the >> +         * host that shall be at least 256 bits long. >> +         * >> +         * We will be using a 512 bit random number as private key. >> +         * This is large enough to provide adequate security, but >> +         * small enough such that we can trivially conform to >> +         * NIST SB800-56A section 5.6.1.1.4 if >> +         * we guarantee that the random number is not either >> +         * all 0xff or all 0x00. But that should be guaranteed >> +         * by the in-kernel RNG anyway. >> +         */ >> +        get_random_bytes(dh_secret, dh_secret_len); >> + >> +        ret = crypto_ffdhe_params(&p, bits); >> +        if (ret) { >> +            kfree_sensitive(dh_secret); >> +            return ret; >> +        } >> + >> +        p.key = dh_secret; >> +        p.key_size = dh_secret_len; >> + >> +        pkey_len = crypto_dh_key_len(&p); >> +        pkey = kmalloc(pkey_len, GFP_KERNEL); >> +        if (!pkey) { >> +            kfree_sensitive(dh_secret); >> +            return -ENOMEM; >> +        } >> + >> +        get_random_bytes(pkey, pkey_len); >> +        ret = crypto_dh_encode_key(pkey, pkey_len, &p); >> +        if (ret) { >> +            pr_debug("failed to encode private key, error %d\n", >> +                 ret); >> +            kfree_sensitive(dh_secret); >> +            goto out; >> +        } >> +    } else { >> +        pr_warn("invalid dh group %d\n", dh_gid); >> +        return -EINVAL; >> +    } >> +    ret = crypto_kpp_set_secret(dh_tfm, pkey, pkey_len); >> +    if (ret) >> +        pr_debug("failed to set private key, error %d\n", ret); >> +out: >> +    kfree_sensitive(pkey); > > pkey can be unset here. > Okay. >> +    return ret; >> +} >> +EXPORT_SYMBOL_GPL(nvme_auth_gen_privkey); >> + >> +int nvme_auth_gen_pubkey(struct crypto_kpp *dh_tfm, >> +        u8 *host_key, size_t host_key_len) >> +{ >> +    struct kpp_request *req; >> +    struct crypto_wait wait; >> +    struct scatterlist dst; >> +    int ret; >> + >> +    req = kpp_request_alloc(dh_tfm, GFP_KERNEL); >> +    if (!req) >> +        return -ENOMEM; >> + >> +    crypto_init_wait(&wait); >> +    kpp_request_set_input(req, NULL, 0); >> +    sg_init_one(&dst, host_key, host_key_len); >> +    kpp_request_set_output(req, &dst, host_key_len); >> +    kpp_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG, >> +                 crypto_req_done, &wait); >> + >> +    ret = crypto_wait_req(crypto_kpp_generate_public_key(req), &wait); >> + > > no need for this newline > >> +    kpp_request_free(req); >> +    return ret; >> +} >> +EXPORT_SYMBOL_GPL(nvme_auth_gen_pubkey); >> + >> +int nvme_auth_gen_shared_secret(struct crypto_kpp *dh_tfm, >> +        u8 *ctrl_key, size_t ctrl_key_len, >> +        u8 *sess_key, size_t sess_key_len) >> +{ >> +    struct kpp_request *req; >> +    struct crypto_wait wait; >> +    struct scatterlist src, dst; >> +    int ret; >> + >> +    req = kpp_request_alloc(dh_tfm, GFP_KERNEL); >> +    if (!req) >> +        return -ENOMEM; >> + >> +    crypto_init_wait(&wait); >> +    sg_init_one(&src, ctrl_key, ctrl_key_len); >> +    kpp_request_set_input(req, &src, ctrl_key_len); >> +    sg_init_one(&dst, sess_key, sess_key_len); >> +    kpp_request_set_output(req, &dst, sess_key_len); >> +    kpp_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG, >> +                 crypto_req_done, &wait); >> + >> +    ret = crypto_wait_req(crypto_kpp_compute_shared_secret(req), &wait); >> + >> +    kpp_request_free(req); >> +    return ret; >> +} >> +EXPORT_SYMBOL_GPL(nvme_auth_gen_shared_secret); >> + >> +static int nvme_auth_send(struct nvme_ctrl *ctrl, int qid, >> +        void *data, size_t tl) >> +{ >> +    struct nvme_command cmd = {}; >> +    blk_mq_req_flags_t flags = qid == NVME_QID_ANY ? >> +        0 : BLK_MQ_REQ_NOWAIT | BLK_MQ_REQ_RESERVED; >> +    struct request_queue *q = qid == NVME_QID_ANY ? >> +        ctrl->fabrics_q : ctrl->connect_q; >> +    int ret; >> + >> +    cmd.auth_send.opcode = nvme_fabrics_command; >> +    cmd.auth_send.fctype = nvme_fabrics_type_auth_send; >> +    cmd.auth_send.secp = NVME_AUTH_DHCHAP_PROTOCOL_IDENTIFIER; >> +    cmd.auth_send.spsp0 = 0x01; >> +    cmd.auth_send.spsp1 = 0x01; >> +    cmd.auth_send.tl = tl; >> + >> +    ret = __nvme_submit_sync_cmd(q, &cmd, NULL, data, tl, 0, qid, >> +                     0, flags); >> +    if (ret > 0) >> +        dev_dbg(ctrl->device, >> +            "%s: qid %d nvme status %d\n", __func__, qid, ret); >> +    else if (ret < 0) >> +        dev_dbg(ctrl->device, >> +            "%s: qid %d error %d\n", __func__, qid, ret); >> +    return ret; >> +} >> + >> +static int nvme_auth_receive(struct nvme_ctrl *ctrl, int qid, >> +        void *buf, size_t al) >> +{ >> +    struct nvme_command cmd = {}; >> +    blk_mq_req_flags_t flags = qid == NVME_QID_ANY ? >> +        0 : BLK_MQ_REQ_NOWAIT | BLK_MQ_REQ_RESERVED; >> +    struct request_queue *q = qid == NVME_QID_ANY ? >> +        ctrl->fabrics_q : ctrl->connect_q; >> +    int ret; >> + >> +    cmd.auth_receive.opcode = nvme_fabrics_command; >> +    cmd.auth_receive.fctype = nvme_fabrics_type_auth_receive; >> +    cmd.auth_receive.secp = NVME_AUTH_DHCHAP_PROTOCOL_IDENTIFIER; >> +    cmd.auth_receive.spsp0 = 0x01; >> +    cmd.auth_receive.spsp1 = 0x01; >> +    cmd.auth_receive.al = al; >> + >> +    ret = __nvme_submit_sync_cmd(q, &cmd, NULL, buf, al, 0, qid, >> +                     0, flags); >> +    if (ret > 0) { >> +        dev_dbg(ctrl->device, "%s: qid %d nvme status %x\n", >> +            __func__, qid, ret); >> +        ret = -EIO; > > Why EIO? > See next comment. >> +    } >> +    if (ret < 0) { >> +        dev_dbg(ctrl->device, "%s: qid %d error %d\n", >> +            __func__, qid, ret); >> +        return ret; >> +    } > > Why did you choose to do these error conditionals differently for the > send and receive functions? > Because we have _three_ kinds of errors here: error codes, NVMe status, and authentication status. And of course the authentication status is _not_ an NVMe status, so we can't easily overload both into a single value. As the authentication status will be set from the received data I chose to fold all NVMe status onto -EIO, leaving the positive value free for authentication status. >> + >> +    return 0; >> +} >> + >> +static int nvme_auth_receive_validate(struct nvme_ctrl *ctrl, int qid, >> +        struct nvmf_auth_dhchap_failure_data *data, >> +        u16 transaction, u8 expected_msg) >> +{ >> +    dev_dbg(ctrl->device, "%s: qid %d auth_type %d auth_id %x\n", >> +        __func__, qid, data->auth_type, data->auth_id); >> + >> +    if (data->auth_type == NVME_AUTH_COMMON_MESSAGES && >> +        data->auth_id == NVME_AUTH_DHCHAP_MESSAGE_FAILURE1) { >> +        return data->rescode_exp; >> +    } >> +    if (data->auth_type != NVME_AUTH_DHCHAP_MESSAGES || >> +        data->auth_id != expected_msg) { >> +        dev_warn(ctrl->device, >> +             "qid %d invalid message %02x/%02x\n", >> +             qid, data->auth_type, data->auth_id); >> +        return NVME_AUTH_DHCHAP_FAILURE_INCORRECT_MESSAGE; >> +    } >> +    if (le16_to_cpu(data->t_id) != transaction) { >> +        dev_warn(ctrl->device, >> +             "qid %d invalid transaction ID %d\n", >> +             qid, le16_to_cpu(data->t_id)); >> +        return NVME_AUTH_DHCHAP_FAILURE_INCORRECT_MESSAGE; >> +    } >> +    return 0; >> +} >> + >> +static int nvme_auth_set_dhchap_negotiate_data(struct nvme_ctrl *ctrl, >> +        struct nvme_dhchap_queue_context *chap) >> +{ >> +    struct nvmf_auth_dhchap_negotiate_data *data = chap->buf; >> +    size_t size = sizeof(*data) + sizeof(union nvmf_auth_protocol); >> + >> +    if (chap->buf_size < size) { >> +        chap->status = NVME_AUTH_DHCHAP_FAILURE_INCORRECT_PAYLOAD; > > Is this an internal error? not sure I understand setting of this status > As mentioned above, we now have three possible status codes to content with. So yes, this is an internal error, expressed as authentication error code. The spec insists on using an authentication error here; it would be possible to use a normal NVMe status, but that's not what the spec wants ... >> +        return -EINVAL; >> +    } >> +    memset((u8 *)chap->buf, 0, size); >> +    data->auth_type = NVME_AUTH_COMMON_MESSAGES; >> +    data->auth_id = NVME_AUTH_DHCHAP_MESSAGE_NEGOTIATE; >> +    data->t_id = cpu_to_le16(chap->transaction); >> +    data->sc_c = 0; /* No secure channel concatenation */ >> +    data->napd = 1; >> +    data->auth_protocol[0].dhchap.authid = NVME_AUTH_DHCHAP_AUTH_ID; >> +    data->auth_protocol[0].dhchap.halen = 3; >> +    data->auth_protocol[0].dhchap.dhlen = 6; >> +    data->auth_protocol[0].dhchap.idlist[0] = NVME_AUTH_DHCHAP_SHA256; >> +    data->auth_protocol[0].dhchap.idlist[1] = NVME_AUTH_DHCHAP_SHA384; >> +    data->auth_protocol[0].dhchap.idlist[2] = NVME_AUTH_DHCHAP_SHA512; >> +    data->auth_protocol[0].dhchap.idlist[3] = >> NVME_AUTH_DHCHAP_DHGROUP_NULL; >> +    data->auth_protocol[0].dhchap.idlist[4] = >> NVME_AUTH_DHCHAP_DHGROUP_2048; >> +    data->auth_protocol[0].dhchap.idlist[5] = >> NVME_AUTH_DHCHAP_DHGROUP_3072; >> +    data->auth_protocol[0].dhchap.idlist[6] = >> NVME_AUTH_DHCHAP_DHGROUP_4096; >> +    data->auth_protocol[0].dhchap.idlist[7] = >> NVME_AUTH_DHCHAP_DHGROUP_6144; >> +    data->auth_protocol[0].dhchap.idlist[8] = >> NVME_AUTH_DHCHAP_DHGROUP_8192; >> + >> +    return size; >> +} >> + >> +static int nvme_auth_process_dhchap_challenge(struct nvme_ctrl *ctrl, >> +        struct nvme_dhchap_queue_context *chap) >> +{ >> +    struct nvmf_auth_dhchap_challenge_data *data = chap->buf; >> +    size_t size = sizeof(*data) + data->hl + data->dhvlen; >> +    const char *hmac_name; >> + >> +    if (chap->buf_size < size) { >> +        chap->status = NVME_AUTH_DHCHAP_FAILURE_INCORRECT_PAYLOAD; >> +        return NVME_SC_INVALID_FIELD; >> +    } >> + >> +    hmac_name = nvme_auth_hmac_name(data->hashid); >> +    if (!hmac_name) { >> +        dev_warn(ctrl->device, >> +             "qid %d: invalid HASH ID %d\n", >> +             chap->qid, data->hashid); >> +        chap->status = NVME_AUTH_DHCHAP_FAILURE_HASH_UNUSABLE; >> +        return -EPROTO; >> +    } >> +    if (chap->hash_id == data->hashid && chap->shash_tfm && >> +        !strcmp(crypto_shash_alg_name(chap->shash_tfm), hmac_name) && >> +        crypto_shash_digestsize(chap->shash_tfm) == data->hl) { >> +        dev_dbg(ctrl->device, >> +            "qid %d: reuse existing hash %s\n", >> +            chap->qid, hmac_name); >> +        goto select_kpp; >> +    } > > newline > >> +    if (chap->shash_tfm) { >> +        crypto_free_shash(chap->shash_tfm); >> +        chap->hash_id = 0; >> +        chap->hash_len = 0; >> +    } > > newline > >> +    chap->shash_tfm = crypto_alloc_shash(hmac_name, 0, >> +                         CRYPTO_ALG_ALLOCATES_MEMORY); >> +    if (IS_ERR(chap->shash_tfm)) { >> +        dev_warn(ctrl->device, >> +             "qid %d: failed to allocate hash %s, error %ld\n", >> +             chap->qid, hmac_name, PTR_ERR(chap->shash_tfm)); >> +        chap->shash_tfm = NULL; >> +        chap->status = NVME_AUTH_DHCHAP_FAILURE_FAILED; >> +        return NVME_SC_AUTH_REQUIRED; >> +    } > > newline > >> +    if (crypto_shash_digestsize(chap->shash_tfm) != data->hl) { >> +        dev_warn(ctrl->device, >> +             "qid %d: invalid hash length %d\n", >> +             chap->qid, data->hl); >> +        crypto_free_shash(chap->shash_tfm); >> +        chap->shash_tfm = NULL; >> +        chap->status = NVME_AUTH_DHCHAP_FAILURE_HASH_UNUSABLE; >> +        return NVME_SC_AUTH_REQUIRED; >> +    } > > newline > >> +    if (chap->hash_id != data->hashid) { >> +        kfree(chap->host_response); > > kfree_sensitive? also why is is freed here? where was it allocated? > This is generated when calculating the host response in nvme_auth_dhchap_host_response(). >> +        chap->host_response = NULL; >> +    } >> +    chap->hash_id = data->hashid; >> +    chap->hash_len = data->hl; >> +    dev_dbg(ctrl->device, "qid %d: selected hash %s\n", >> +        chap->qid, hmac_name); >> + >> +    gid_name = nvme_auth_dhgroup_kpp(data->dhgid); >> +    if (!gid_name) { >> +        dev_warn(ctrl->device, >> +             "qid %d: invalid DH group id %d\n", >> +             chap->qid, data->dhgid); >> +        chap->status = NVME_AUTH_DHCHAP_FAILURE_DHGROUP_UNUSABLE; >> +        return -EPROTO; > > No need for all the previous frees? > Maybe we can rework these such that we first do all the checks and then > go and allocate stuff? > Hmm. Will have a look if that is feasible. >> +    } >> + >> +    if (data->dhgid != NVME_AUTH_DHCHAP_DHGROUP_NULL) { >> +        if (data->dhvlen == 0) { >> +            dev_warn(ctrl->device, >> +                 "qid %d: empty DH value\n", >> +                 chap->qid); >> +            chap->status = NVME_AUTH_DHCHAP_FAILURE_DHGROUP_UNUSABLE; >> +            return -EPROTO; >> +        } >> +        chap->dh_tfm = crypto_alloc_kpp(gid_name, 0, 0); >> +        if (IS_ERR(chap->dh_tfm)) { >> +            int ret = PTR_ERR(chap->dh_tfm); >> + >> +            dev_warn(ctrl->device, >> +                 "qid %d: failed to initialize %s\n", >> +                 chap->qid, gid_name); >> +            chap->status = NVME_AUTH_DHCHAP_FAILURE_DHGROUP_UNUSABLE; >> +            chap->dh_tfm = NULL; >> +            return ret; >> +        } >> +        chap->dhgroup_id = data->dhgid; >> +    } else if (data->dhvlen != 0) { >> +        dev_warn(ctrl->device, >> +             "qid %d: invalid DH value for NULL DH\n", >> +            chap->qid); >> +        chap->status = NVME_AUTH_DHCHAP_FAILURE_DHGROUP_UNUSABLE; >> +        return -EPROTO; >> +    } >> +    dev_dbg(ctrl->device, "qid %d: selected DH group %s\n", >> +        chap->qid, gid_name); >> + >> +select_kpp: >> +    chap->s1 = le32_to_cpu(data->seqnum); >> +    memcpy(chap->c1, data->cval, chap->hash_len); >> + >> +    return 0; >> +} >> + >> +static int nvme_auth_set_dhchap_reply_data(struct nvme_ctrl *ctrl, >> +        struct nvme_dhchap_queue_context *chap) >> +{ >> +    struct nvmf_auth_dhchap_reply_data *data = chap->buf; >> +    size_t size = sizeof(*data); >> + >> +    size += 2 * chap->hash_len; >> +    if (ctrl->opts->dhchap_bidi) { >> +        get_random_bytes(chap->c2, chap->hash_len); >> +        chap->s2 = nvme_dhchap_seqnum++; > > Any serialization needed on nvme_dhchap_seqnum? > Maybe; will be switching to atomic here. Have been lazy ... >> +    } else >> +        memset(chap->c2, 0, chap->hash_len); >> + >> + >> +    if (chap->buf_size < size) { >> +        chap->status = NVME_AUTH_DHCHAP_FAILURE_INCORRECT_PAYLOAD; >> +        return -EINVAL; >> +    } >> +    memset(chap->buf, 0, size); >> +    data->auth_type = NVME_AUTH_DHCHAP_MESSAGES; >> +    data->auth_id = NVME_AUTH_DHCHAP_MESSAGE_REPLY; >> +    data->t_id = cpu_to_le16(chap->transaction); >> +    data->hl = chap->hash_len; >> +    data->dhvlen = 0; >> +    data->seqnum = cpu_to_le32(chap->s2); >> +    memcpy(data->rval, chap->response, chap->hash_len); >> +    if (ctrl->opts->dhchap_bidi) { > > Can we unite the "if (ctrl->opts->dhchap_bidi)" > conditionals? > Sure. [ .. ] >> +int nvme_auth_negotiate(struct nvme_ctrl *ctrl, int qid) >> +{ >> +    struct nvme_dhchap_queue_context *chap; >> + >> +    if (!ctrl->dhchap_key || !ctrl->dhchap_key_len) { >> +        dev_warn(ctrl->device, "qid %d: no key\n", qid); >> +        return -ENOKEY; >> +    } >> + >> +    mutex_lock(&ctrl->dhchap_auth_mutex); >> +    /* Check if the context is already queued */ >> +    list_for_each_entry(chap, &ctrl->dhchap_auth_list, entry) { >> +        if (chap->qid == qid) { >> +            mutex_unlock(&ctrl->dhchap_auth_mutex); >> +            queue_work(nvme_wq, &chap->auth_work); >> +            return 0; >> +        } >> +    } >> +    chap = kzalloc(sizeof(*chap), GFP_KERNEL); >> +    if (!chap) { >> +        mutex_unlock(&ctrl->dhchap_auth_mutex); >> +        return -ENOMEM; >> +    } >> +    chap->qid = qid; >> +    chap->ctrl = ctrl; >> + >> +    /* >> +     * Allocate a large enough buffer for the entire negotiation: >> +     * 4k should be enough to ffdhe8192. >> +     */ >> +    chap->buf_size = 4096; >> +    chap->buf = kzalloc(chap->buf_size, GFP_KERNEL); >> +    if (!chap->buf) { >> +        mutex_unlock(&ctrl->dhchap_auth_mutex); >> +        kfree(chap); >> +        return -ENOMEM; >> +    } >> + >> +    INIT_WORK(&chap->auth_work, __nvme_auth_work); >> +    list_add(&chap->entry, &ctrl->dhchap_auth_list); >> +    mutex_unlock(&ctrl->dhchap_auth_mutex); >> +    queue_work(nvme_wq, &chap->auth_work); > > Why is the auth in a work? e.g. it won't fail the connect? > For re-authentication. Re-authentication should _not_ fail the connection if it stops in some intermediate step, only once the the protocol ran to completion the status is updated. Meaning that we will have additional I/O ongoing while re-authentication is in progress, so we can't stop all I/O here but rather need to shift it onto a workqueue. >> +    return 0; >> +} >> +EXPORT_SYMBOL_GPL(nvme_auth_negotiate); >> + >> +int nvme_auth_wait(struct nvme_ctrl *ctrl, int qid) >> +{ >> +    struct nvme_dhchap_queue_context *chap; >> +    int ret; >> + >> +    mutex_lock(&ctrl->dhchap_auth_mutex); >> +    list_for_each_entry(chap, &ctrl->dhchap_auth_list, entry) { >> +        if (chap->qid != qid) >> +            continue; >> +        mutex_unlock(&ctrl->dhchap_auth_mutex); >> +        flush_work(&chap->auth_work); >> +        ret = chap->error; >> +        nvme_auth_reset(chap); >> +        return ret; >> +    } >> +    mutex_unlock(&ctrl->dhchap_auth_mutex); >> +    return -ENXIO; >> +} >> +EXPORT_SYMBOL_GPL(nvme_auth_wait); >> + >> +/* Assumes that the controller is in state RESETTING */ >> +static void nvme_dhchap_auth_work(struct work_struct *work) >> +{ >> +    struct nvme_ctrl *ctrl = >> +        container_of(work, struct nvme_ctrl, dhchap_auth_work); >> +    int ret, q; >> + >> +    nvme_stop_queues(ctrl); >> +    /* Authenticate admin queue first */ >> +    ret = nvme_auth_negotiate(ctrl, NVME_QID_ANY); >> +    if (ret) { >> +        dev_warn(ctrl->device, >> +             "qid 0: error %d setting up authentication\n", ret); >> +        goto out; >> +    } >> +    ret = nvme_auth_wait(ctrl, NVME_QID_ANY); >> +    if (ret) { >> +        dev_warn(ctrl->device, >> +             "qid 0: authentication failed\n"); >> +        goto out; >> +    } >> +    dev_info(ctrl->device, "qid 0: authenticated\n"); >> + >> +    for (q = 1; q < ctrl->queue_count; q++) { >> +        ret = nvme_auth_negotiate(ctrl, q); >> +        if (ret) { >> +            dev_warn(ctrl->device, >> +                 "qid %d: error %d setting up authentication\n", >> +                 q, ret); >> +            goto out; >> +        } >> +    } >> +out: >> +    /* >> +     * Failure is a soft-state; credentials remain valid until >> +     * the controller terminates the connection. >> +     */ >> +    if (nvme_change_ctrl_state(ctrl, NVME_CTRL_LIVE)) >> +        nvme_start_queues(ctrl); >> +} >> + >> +void nvme_auth_init_ctrl(struct nvme_ctrl *ctrl) >> +{ >> +    INIT_LIST_HEAD(&ctrl->dhchap_auth_list); >> +    INIT_WORK(&ctrl->dhchap_auth_work, nvme_dhchap_auth_work); >> +    mutex_init(&ctrl->dhchap_auth_mutex); >> +    nvme_auth_generate_key(ctrl); >> +} >> +EXPORT_SYMBOL_GPL(nvme_auth_init_ctrl); >> + >> +void nvme_auth_stop(struct nvme_ctrl *ctrl) >> +{ >> +    struct nvme_dhchap_queue_context *chap = NULL, *tmp; >> + >> +    cancel_work_sync(&ctrl->dhchap_auth_work); >> +    mutex_lock(&ctrl->dhchap_auth_mutex); >> +    list_for_each_entry_safe(chap, tmp, &ctrl->dhchap_auth_list, entry) >> +        cancel_work_sync(&chap->auth_work); >> +    mutex_unlock(&ctrl->dhchap_auth_mutex); >> +} >> +EXPORT_SYMBOL_GPL(nvme_auth_stop); >> + >> +void nvme_auth_free(struct nvme_ctrl *ctrl) >> +{ >> +    struct nvme_dhchap_queue_context *chap = NULL, *tmp; >> + >> +    mutex_lock(&ctrl->dhchap_auth_mutex); >> +    list_for_each_entry_safe(chap, tmp, &ctrl->dhchap_auth_list, >> entry) { >> +        list_del_init(&chap->entry); >> +        flush_work(&chap->auth_work); >> +        __nvme_auth_free(chap); >> +    } >> +    mutex_unlock(&ctrl->dhchap_auth_mutex); >> +    kfree(ctrl->dhchap_key); >> +    ctrl->dhchap_key = NULL; >> +    ctrl->dhchap_key_len = 0; >> +} >> +EXPORT_SYMBOL_GPL(nvme_auth_free); >> diff --git a/drivers/nvme/host/auth.h b/drivers/nvme/host/auth.h >> new file mode 100644 >> index 000000000000..cf1255f9db6d >> --- /dev/null >> +++ b/drivers/nvme/host/auth.h >> @@ -0,0 +1,25 @@ >> +/* SPDX-License-Identifier: GPL-2.0 */ >> +/* >> + * Copyright (c) 2021 Hannes Reinecke, SUSE Software Solutions >> + */ >> + >> +#ifndef _NVME_AUTH_H >> +#define _NVME_AUTH_H >> + >> +#include >> + >> +const char *nvme_auth_dhgroup_name(int dhgroup_id); >> +int nvme_auth_dhgroup_pubkey_size(int dhgroup_id); >> +int nvme_auth_dhgroup_privkey_size(int dhgroup_id); >> +const char *nvme_auth_dhgroup_kpp(int dhgroup_id); >> +int nvme_auth_dhgroup_id(const char *dhgroup_name); >> + >> +const char *nvme_auth_hmac_name(int hmac_id); >> +const char *nvme_auth_digest_name(int hmac_id); >> +int nvme_auth_hmac_id(const char *hmac_name); >> + >> +unsigned char *nvme_auth_extract_secret(unsigned char *dhchap_secret, >> +                    size_t *dhchap_key_len); >> +u8 *nvme_auth_transform_key(u8 *key, size_t key_len, u8 key_hash, >> char *nqn); >> + >> +#endif /* _NVME_AUTH_H */ >> diff --git a/drivers/nvme/host/core.c b/drivers/nvme/host/core.c >> index 7efb31b87f37..f669b054790b 100644 >> --- a/drivers/nvme/host/core.c >> +++ b/drivers/nvme/host/core.c >> @@ -24,6 +24,7 @@ >>     #include "nvme.h" >>   #include "fabrics.h" >> +#include "auth.h" >>     #define CREATE_TRACE_POINTS >>   #include "trace.h" >> @@ -322,6 +323,7 @@ enum nvme_disposition { >>       COMPLETE, >>       RETRY, >>       FAILOVER, >> +    AUTHENTICATE, >>   }; >>     static inline enum nvme_disposition nvme_decide_disposition(struct >> request *req) >> @@ -329,6 +331,9 @@ static inline enum nvme_disposition >> nvme_decide_disposition(struct request *req) >>       if (likely(nvme_req(req)->status == 0)) >>           return COMPLETE; >>   +    if ((nvme_req(req)->status & 0x7ff) == NVME_SC_AUTH_REQUIRED) >> +        return AUTHENTICATE; >> + >>       if (blk_noretry_request(req) || >>           (nvme_req(req)->status & NVME_SC_DNR) || >>           nvme_req(req)->retries >= nvme_max_retries) >> @@ -361,11 +366,13 @@ static inline void nvme_end_req(struct request >> *req) >>     void nvme_complete_rq(struct request *req) >>   { >> +    struct nvme_ctrl *ctrl = nvme_req(req)->ctrl; >> + >>       trace_nvme_complete_rq(req); >>       nvme_cleanup_cmd(req); >>   -    if (nvme_req(req)->ctrl->kas) >> -        nvme_req(req)->ctrl->comp_seen = true; >> +    if (ctrl->kas) >> +        ctrl->comp_seen = true; >>         switch (nvme_decide_disposition(req)) { >>       case COMPLETE: >> @@ -377,6 +384,15 @@ void nvme_complete_rq(struct request *req) >>       case FAILOVER: >>           nvme_failover_req(req); >>           return; >> +    case AUTHENTICATE: >> +#ifdef CONFIG_NVME_AUTH >> +        if (nvme_change_ctrl_state(ctrl, NVME_CTRL_RESETTING)) >> +            queue_work(nvme_wq, &ctrl->dhchap_auth_work); > > Why is the state change here and not in nvme_dhchap_auth_work? > Because switching to 'resetting' is an easy way to synchronize with the admin queue. >> +        nvme_retry_req(req); >> +#else >> +        nvme_end_req(req); >> +#endif >> +        return; >>       } >>   } >>   EXPORT_SYMBOL_GPL(nvme_complete_rq); >> @@ -707,7 +723,9 @@ bool __nvme_check_ready(struct nvme_ctrl *ctrl, >> struct request *rq, >>           switch (ctrl->state) { >>           case NVME_CTRL_CONNECTING: >>               if (blk_rq_is_passthrough(rq) && >> nvme_is_fabrics(req->cmd) && >> -                req->cmd->fabrics.fctype == nvme_fabrics_type_connect) >> +                (req->cmd->fabrics.fctype == >> nvme_fabrics_type_connect || >> +                 req->cmd->fabrics.fctype == >> nvme_fabrics_type_auth_send || >> +                 req->cmd->fabrics.fctype == >> nvme_fabrics_type_auth_receive)) > > What happens if the auth command comes before the connect (say in case > of ctrl reset when auth was already queued but not yet executed? > See below. >>                   return true; >>               break; >>           default: >> @@ -3458,6 +3476,51 @@ static ssize_t >> nvme_ctrl_fast_io_fail_tmo_store(struct device *dev, >>   static DEVICE_ATTR(fast_io_fail_tmo, S_IRUGO | S_IWUSR, >>       nvme_ctrl_fast_io_fail_tmo_show, nvme_ctrl_fast_io_fail_tmo_store); >>   +#ifdef CONFIG_NVME_AUTH >> +static ssize_t nvme_ctrl_dhchap_secret_show(struct device *dev, >> +        struct device_attribute *attr, char *buf) >> +{ >> +    struct nvme_ctrl *ctrl = dev_get_drvdata(dev); >> +    struct nvmf_ctrl_options *opts = ctrl->opts; >> + >> +    if (!opts->dhchap_secret) >> +        return sysfs_emit(buf, "none\n"); >> +    return sysfs_emit(buf, "%s\n", opts->dhchap_secret); > > Should we actually show this? don't know enough how much the secret > should be kept a secret... > I found it logical, as we need the 'store' functionality to trigger re-authentication. But sure, we can make this a write-only attribute. >> +} >> + >> +static ssize_t nvme_ctrl_dhchap_secret_store(struct device *dev, >> +        struct device_attribute *attr, const char *buf, size_t count) >> +{ >> +    struct nvme_ctrl *ctrl = dev_get_drvdata(dev); >> +    struct nvmf_ctrl_options *opts = ctrl->opts; >> +    char *dhchap_secret; >> + >> +    if (!ctrl->opts->dhchap_secret) >> +        return -EINVAL; >> +    if (count < 7) >> +        return -EINVAL; >> +    if (memcmp(buf, "DHHC-1:", 7)) >> +        return -EINVAL; >> + >> +    dhchap_secret = kzalloc(count + 1, GFP_KERNEL); >> +    if (!dhchap_secret) >> +        return -ENOMEM; >> +    memcpy(dhchap_secret, buf, count); >> +    if (strcmp(dhchap_secret, opts->dhchap_secret)) { >> +        kfree(opts->dhchap_secret); >> +        opts->dhchap_secret = dhchap_secret; >> +        /* Key has changed; reset authentication data */ >> +        nvme_auth_free(ctrl); >> +        nvme_auth_generate_key(ctrl); >> +    } > > Nice, worth a comment "/* Re-authentication with new secret */" > Right, will do. Thanks for the review! Cheers, Hannes -- Dr. Hannes Reinecke Kernel Storage Architect hare@suse.de +49 911 74053 688 SUSE Software Solutions Germany GmbH, Maxfeldstr. 5, 90409 Nürnberg HRB 36809 (AG Nürnberg), GF: Felix Imendörffer