Add passcode display length field to UDC messages (#40696)

* UDC

* Add passcode display length

* address comments

* encode/decode

* restyle

* restyle

* fix type conversion

* fix android build

* fix darwin build

* add passcode length to ID message

* restyle

* fix android build

* fix android build

* restyle

* restyle

* address comments

* address comments
diff --git a/examples/platform/linux/CommissioneeShellCommands.cpp b/examples/platform/linux/CommissioneeShellCommands.cpp
index 18f8355..f0ec1b9 100644
--- a/examples/platform/linux/CommissioneeShellCommands.cpp
+++ b/examples/platform/linux/CommissioneeShellCommands.cpp
@@ -66,8 +66,8 @@
                     "  udccommissionerpasscode <address> <port> [CommissionerPasscodeReady] [PairingHint] [PairingInst] Send UDC "
                     "commissioner passcode message to address. Usage: udccommissionerpasscode ::1 5543 t 5 HelloWorld\r\n");
     streamer_printf(sout,
-                    "  testudc <address> <port> [NoPasscode] [CdUponPasscodeDialog] [vid] [PairingHint] [PairingInst] Send UDC "
-                    "message to address. Usage: cast testudc ::1 5543 t t 5 HelloWorld\r\n");
+                    "  testudc <address> <port> [NoPasscode] [CdUponPasscodeDialog] [vid] [PairingHint] [PairingInst] [PinLength] "
+                    "Send UDC message to address. Usage: cast testudc ::1 5543 t t 5 HelloWorld 8\r\n");
 #endif // CHIP_DEVICE_CONFIG_ENABLE_COMMISSIONER_DISCOVERY_CLIENT
     // TODO: Figure out whether setdiscoverytimeout is a reasonable thing to do
     // at all, and if so what semantics it should have.  Presumably it should
@@ -157,7 +157,7 @@
         chip::Inet::IPAddress::FromString(argv[1], commissioner);
         uint16_t port = (uint16_t) strtol(argv[2], &eptr, 10);
 
-        // sendudc <address> <port> [NoPasscode] [CdUponPasscodeDialog] [vid] [PairingHint] [PairingInst]
+        // testudc <address> <port> [NoPasscode] [CdUponPasscodeDialog] [vid] [PairingHint] [PairingInst] [PinLength]
         // ex. sendudc <address> <port> t t 111 5 'hello world'
 
         Protocols::UserDirectedCommissioning::IdentificationDeclaration id;
@@ -185,6 +185,11 @@
         {
             id.SetPairingInst(argv[7]);
         }
+        if (argc > 8)
+        {
+            uint8_t pinLength = (uint8_t) strtol(argv[8], &eptr, 10);
+            id.SetPasscodeLength(pinLength);
+        }
         return Server::GetInstance().SendUserDirectedCommissioningRequest(chip::Transport::PeerAddress::UDP(commissioner, port),
                                                                           id);
     }
diff --git a/examples/tv-app/android/App/platform-app/src/main/java/com/matter/tv/server/MatterCommissioningPrompter.java b/examples/tv-app/android/App/platform-app/src/main/java/com/matter/tv/server/MatterCommissioningPrompter.java
index 743db56..8fe8409 100755
--- a/examples/tv-app/android/App/platform-app/src/main/java/com/matter/tv/server/MatterCommissioningPrompter.java
+++ b/examples/tv-app/android/App/platform-app/src/main/java/com/matter/tv/server/MatterCommissioningPrompter.java
@@ -71,13 +71,25 @@
   }
 
   @Override
-  public void promptForCommissionPinCode(int vendorId, int productId, String commissioneeName) {
+  public void promptForCommissionPinCode(
+      int vendorId,
+      int productId,
+      int passcodeLength,
+      String commissioneeName,
+      int pairingHint,
+      String pairingInstruction) {
     Log.d(
         TAG,
         "Received prompt for PIN code vendor id:"
             + vendorId
             + " productId:"
             + productId
+            + " passcode length:"
+            + passcodeLength
+            + " pairing hint:"
+            + pairingHint
+            + " pairing instruction:"
+            + pairingInstruction
             + ". Commissionee: "
             + commissioneeName);
     android.os.Message obtained = android.os.Message.obtain();
@@ -106,6 +118,7 @@
       int productId,
       String commissioneeName,
       long passcode,
+      int passcodeLength,
       int pairingHint,
       String pairingInstruction) {
     Log.d(
@@ -116,12 +129,24 @@
             + vendorId
             + " productId:"
             + productId
+            + " passcode length:"
+            + passcodeLength
+            + " pairing hint:"
+            + pairingHint
+            + " pairing instruction:"
+            + pairingInstruction
             + ". Commissionee: "
             + commissioneeName);
     Bundle bundle = new Bundle();
     PromptCommissionerPasscode promptCommissionerPasscode =
         new PromptCommissionerPasscode(
-            vendorId, productId, commissioneeName, passcode, pairingHint, pairingInstruction);
+            vendorId,
+            productId,
+            commissioneeName,
+            passcode,
+            passcodeLength,
+            pairingHint,
+            pairingInstruction);
     bundle.putParcelable(MsgHandler.KEY_PROMPT_COMMISSIONER_PASSCODE, promptCommissionerPasscode);
     android.os.Message obtained = android.os.Message.obtain();
     obtained.what = MsgHandler.MSG_CommissionerPasscode;
diff --git a/examples/tv-app/android/App/platform-app/src/main/java/com/matter/tv/server/model/PromptCommissionerPasscode.java b/examples/tv-app/android/App/platform-app/src/main/java/com/matter/tv/server/model/PromptCommissionerPasscode.java
index 1a5657f..824dc89 100644
--- a/examples/tv-app/android/App/platform-app/src/main/java/com/matter/tv/server/model/PromptCommissionerPasscode.java
+++ b/examples/tv-app/android/App/platform-app/src/main/java/com/matter/tv/server/model/PromptCommissionerPasscode.java
@@ -8,6 +8,7 @@
   private int productId;
   private String commissioneeName;
   private long passcode;
+  private int passcodeLength;
   private int pairingHint;
   private String pairingInstruction;
 
@@ -16,12 +17,14 @@
       int productId,
       String commissioneeName,
       long passcode,
+      int passcodeLength,
       int pairingHint,
       String pairingInstruction) {
     this.vendorId = vendorId;
     this.productId = productId;
     this.commissioneeName = commissioneeName;
     this.passcode = passcode;
+    this.passcodeLength = passcodeLength;
     this.pairingHint = pairingHint;
     this.pairingInstruction = pairingInstruction;
   }
@@ -31,6 +34,7 @@
     productId = in.readInt();
     commissioneeName = in.readString();
     passcode = in.readLong();
+    passcodeLength = in.readInt();
     pairingHint = in.readInt();
     pairingInstruction = in.readString();
   }
@@ -80,6 +84,14 @@
     this.passcode = passcode;
   }
 
+  public int getPasscodeLength() {
+    return passcodeLength;
+  }
+
+  public void setPasscodeLength(int passcodeLength) {
+    this.passcodeLength = passcodeLength;
+  }
+
   public int getPairingHint() {
     return pairingHint;
   }
@@ -108,6 +120,8 @@
         + '\''
         + ", passcode="
         + passcode
+        + ", passcodeLength="
+        + passcodeLength
         + ", pairingHint="
         + pairingHint
         + ", pairingInstruction='"
@@ -127,6 +141,7 @@
     parcel.writeInt(productId);
     parcel.writeString(commissioneeName);
     parcel.writeLong(passcode);
+    parcel.writeInt(passcodeLength);
     parcel.writeInt(pairingHint);
     parcel.writeString(pairingInstruction);
   }
diff --git a/examples/tv-app/android/java/MyUserPrompter-JNI.cpp b/examples/tv-app/android/java/MyUserPrompter-JNI.cpp
index 6d586d9..4cbadc9 100644
--- a/examples/tv-app/android/java/MyUserPrompter-JNI.cpp
+++ b/examples/tv-app/android/java/MyUserPrompter-JNI.cpp
@@ -44,7 +44,7 @@
     }
 
     mPromptForCommissionPincodeMethod =
-        env->GetMethodID(JNIMyUserPrompterClass, "promptForCommissionPinCode", "(IILjava/lang/String;)V");
+        env->GetMethodID(JNIMyUserPrompterClass, "promptForCommissionPinCode", "(IIILjava/lang/String;ILjava/lang/String;)V");
     if (mPromptForCommissionPincodeMethod == nullptr)
     {
         ChipLogError(Zcl, "Failed to access JNIMyUserPrompter 'promptForCommissionPinCode' method");
@@ -59,7 +59,7 @@
     }
 
     mPromptWithCommissionerPasscodeMethod =
-        env->GetMethodID(JNIMyUserPrompterClass, "promptWithCommissionerPasscode", "(IILjava/lang/String;JILjava/lang/String;)V");
+        env->GetMethodID(JNIMyUserPrompterClass, "promptWithCommissionerPasscode", "(IILjava/lang/String;JIILjava/lang/String;)V");
     if (mPromptWithCommissionerPasscodeMethod == nullptr)
     {
         ChipLogError(Zcl, "Failed to access JNIMyUserPrompter 'promptWithCommissionerPasscode' method");
@@ -142,7 +142,7 @@
  *
  */
 void JNIMyUserPrompter::PromptForCommissionPasscode(uint16_t vendorId, uint16_t productId, const char * commissioneeName,
-                                                    uint16_t pairingHint, const char * pairingInstruction)
+                                                    uint16_t pairingHint, const char * pairingInstruction, uint8_t passcodeLength)
 {
     DeviceLayer::StackUnlock unlock;
     CHIP_ERROR err = CHIP_NO_ERROR;
@@ -156,9 +156,12 @@
         jstring jcommissioneeName = env->NewStringUTF(commissioneeName);
         VerifyOrExit(jcommissioneeName != nullptr, ChipLogError(Zcl, "Could not create jstring"); err = CHIP_ERROR_INTERNAL);
 
+        ChipLogError(Zcl, "JNIMyUserPrompter::PromptForCommissionPasscode length=%d", static_cast<uint16_t>(passcodeLength));
+
         env->ExceptionClear();
         env->CallVoidMethod(mJNIMyUserPrompterObject.ObjectRef(), mPromptForCommissionPincodeMethod, static_cast<jint>(vendorId),
-                            static_cast<jint>(productId), jcommissioneeName);
+                            static_cast<jint>(productId), static_cast<jint>(passcodeLength), jcommissioneeName,
+                            static_cast<jint>(pairingHint), pairingInstruction);
         if (env->ExceptionCheck())
         {
             ChipLogError(Zcl, "Java exception in PromptForCommissionPincode");
@@ -234,7 +237,8 @@
  * For example "Casting Passcode: [passcode]. For more instructions, click here."
  */
 void JNIMyUserPrompter::PromptWithCommissionerPasscode(uint16_t vendorId, uint16_t productId, const char * commissioneeName,
-                                                       uint32_t passcode, uint16_t pairingHint, const char * pairingInstruction)
+                                                       uint32_t passcode, uint8_t passcodeLength, uint16_t pairingHint,
+                                                       const char * pairingInstruction)
 {
     ChipLogError(Zcl, "JNIMyUserPrompter::PromptWithCommissionerPasscode");
 
@@ -256,7 +260,8 @@
         env->ExceptionClear();
         env->CallVoidMethod(mJNIMyUserPrompterObject.ObjectRef(), mPromptWithCommissionerPasscodeMethod,
                             static_cast<jint>(vendorId), static_cast<jint>(productId), jcommissioneeName,
-                            static_cast<jlong>(passcode), static_cast<jint>(pairingHint), jpairingInstruction);
+                            static_cast<jlong>(passcode), static_cast<jint>(passcodeLength), static_cast<jint>(pairingHint),
+                            jpairingInstruction);
         if (env->ExceptionCheck())
         {
             ChipLogError(DeviceLayer, "Java exception in PromptWithCommissionerPasscode");
diff --git a/examples/tv-app/android/java/MyUserPrompter-JNI.h b/examples/tv-app/android/java/MyUserPrompter-JNI.h
index 4083463..8aa87da 100644
--- a/examples/tv-app/android/java/MyUserPrompter-JNI.h
+++ b/examples/tv-app/android/java/MyUserPrompter-JNI.h
@@ -28,11 +28,11 @@
     JNIMyUserPrompter(jobject prompter);
     void PromptForCommissionOKPermission(uint16_t vendorId, uint16_t productId, const char * commissioneeName) override;
     void PromptForCommissionPasscode(uint16_t vendorId, uint16_t productId, const char * commissioneeName, uint16_t pairingHint,
-                                     const char * pairingInstruction) override;
+                                     const char * pairingInstruction, uint8_t passcodeLength) override;
     void HidePromptsOnCancel(uint16_t vendorId, uint16_t productId, const char * commissioneeName) override;
     bool DisplaysPasscodeAndQRCode() override;
     void PromptWithCommissionerPasscode(uint16_t vendorId, uint16_t productId, const char * commissioneeName, uint32_t passcode,
-                                        uint16_t pairingHint, const char * pairingInstruction) override;
+                                        uint8_t passcodeLength, uint16_t pairingHint, const char * pairingInstruction) override;
     void PromptCommissioningStarted(uint16_t vendorId, uint16_t productId, const char * commissioneeName) override;
     void PromptCommissioningSucceeded(uint16_t vendorId, uint16_t productId, const char * commissioneeName) override;
     void PromptCommissioningFailed(const char * commissioneeName, CHIP_ERROR error) override;
diff --git a/examples/tv-app/android/java/TVApp-JNI.cpp b/examples/tv-app/android/java/TVApp-JNI.cpp
index 228cab6..dd99f26 100644
--- a/examples/tv-app/android/java/TVApp-JNI.cpp
+++ b/examples/tv-app/android/java/TVApp-JNI.cpp
@@ -228,10 +228,10 @@
         }
     }
 
-    uint32_t GetCommissionerPasscode(uint16_t vendorId, uint16_t productId, chip::CharSpan rotatingId) override
+    PasscodeInfo GetCommissionerPasscode(uint16_t vendorId, uint16_t productId, chip::CharSpan rotatingId) override
     {
         // TODO: randomly generate this value
-        return 12345678;
+        return { 12345678, 8 };
     }
 
     void FetchCommissionPasscodeFromContentApp(uint16_t vendorId, uint16_t productId, CharSpan rotatingId) override
diff --git a/examples/tv-app/android/java/src/com/matter/tv/server/tvapp/UserPrompter.java b/examples/tv-app/android/java/src/com/matter/tv/server/tvapp/UserPrompter.java
index dc6fb34..d3ba320 100644
--- a/examples/tv-app/android/java/src/com/matter/tv/server/tvapp/UserPrompter.java
+++ b/examples/tv-app/android/java/src/com/matter/tv/server/tvapp/UserPrompter.java
@@ -37,7 +37,13 @@
    * If user responds with Cancel then implementor calls UserPrompterResolver.OnPinCodeDeclined();
    *
    */
-  void promptForCommissionPinCode(int vendorId, int productId, String commissioneeName);
+  void promptForCommissionPinCode(
+      int vendorId,
+      int productId,
+      int passcodeLength,
+      String commissioneeName,
+      int pairingHint,
+      String pairingInstruction);
 
   /**
    * Called to when CancelCommissioning is received via UDC. Indicates that commissioner can stop
@@ -57,6 +63,7 @@
       int productId,
       String commissioneeName,
       long passcode,
+      int passcodeLength,
       int pairingHint,
       String pairingInstruction);
 
diff --git a/examples/tv-app/tv-common/src/AppTv.cpp b/examples/tv-app/tv-common/src/AppTv.cpp
index cdcd571..1553360 100644
--- a/examples/tv-app/tv-common/src/AppTv.cpp
+++ b/examples/tv-app/tv-common/src/AppTv.cpp
@@ -66,7 +66,7 @@
 
     // tv should override this with a dialog prompt
     inline void PromptForCommissionPasscode(uint16_t vendorId, uint16_t productId, const char * commissioneeName,
-                                            uint16_t pairingHint, const char * pairingInstruction) override
+                                            uint16_t pairingHint, const char * pairingInstruction, uint8_t passcodeLength) override
     {
         return;
     }
@@ -79,7 +79,8 @@
 
     // tv should override this with a dialog prompt
     inline void PromptWithCommissionerPasscode(uint16_t vendorId, uint16_t productId, const char * commissioneeName,
-                                               uint32_t passcode, uint16_t pairingHint, const char * pairingInstruction) override
+                                               uint32_t passcode, uint8_t passcodeLength, uint16_t pairingHint,
+                                               const char * pairingInstruction) override
     {
         return;
     }
@@ -131,10 +132,11 @@
         }
     }
 
-    uint32_t GetCommissionerPasscode(uint16_t vendorId, uint16_t productId, chip::CharSpan rotatingId) override
+    PasscodeInfo GetCommissionerPasscode(uint16_t vendorId, uint16_t productId, chip::CharSpan rotatingId) override
     {
         // TODO: randomly generate this value
-        return 12345678;
+        ChipLogDetail(AppServer, "GetCommissionerPasscode: returning a passcode");
+        return { 12345678, 8 };
     }
 
     void FetchCommissionPasscodeFromContentApp(uint16_t vendorId, uint16_t productId, CharSpan rotatingId) override
diff --git a/examples/tv-casting-app/android/App/app/src/main/java/com/matter/casting/ConnectionExampleFragment.java b/examples/tv-casting-app/android/App/app/src/main/java/com/matter/casting/ConnectionExampleFragment.java
index 3aab517..45ece3a 100644
--- a/examples/tv-casting-app/android/App/app/src/main/java/com/matter/casting/ConnectionExampleFragment.java
+++ b/examples/tv-casting-app/android/App/app/src/main/java/com/matter/casting/ConnectionExampleFragment.java
@@ -136,7 +136,8 @@
               if (useCommissionerGeneratedPasscode) {
                 // Set commissionerPasscode to true for CastingPlayer/Commissioner-Generated
                 // passcode commissioning.
-                idOptions = new IdentificationDeclarationOptions(false, false, true, false, false);
+                idOptions =
+                    new IdentificationDeclarationOptions(false, false, true, false, false, 0);
                 Log.d(
                     TAG,
                     "onViewCreated() calling CastingPlayer.verifyOrEstablishConnection() Target Content Application Vendor ID: "
@@ -144,7 +145,16 @@
                         + ", useCommissionerGeneratedPasscode: "
                         + useCommissionerGeneratedPasscode);
               } else {
-                idOptions = new IdentificationDeclarationOptions();
+                int passcodeLength =
+                    String.valueOf(
+                            Math.abs(
+                                InitializationExample.commissionableDataProvider
+                                    .get()
+                                    .getSetupPasscode()))
+                        .length();
+                idOptions =
+                    new IdentificationDeclarationOptions(
+                        false, false, false, false, false, passcodeLength);
                 Log.d(
                     TAG,
                     "onViewCreated() calling CastingPlayer.verifyOrEstablishConnection() Target Content Application Vendor ID: "
@@ -219,7 +229,9 @@
                                 displayPasscodeInputDialog(activity);
 
                                 connectionFragmentStatusTextView.setText(
-                                    "CommissionerDeclaration message received from Casting Player: A passcode is now displayed for the user by the Casting Player. \n\n");
+                                    "CommissionerDeclaration message received from Casting Player: A passcode (length "
+                                        + cd.getPasscodeLength()
+                                        + ") is now displayed for the user by the Casting Player. \n\n");
                               }
                               if (cd.getCancelPasscode()) {
                                 if (useCommissionerGeneratedPasscode) {
diff --git a/examples/tv-casting-app/android/App/app/src/main/jni/com/matter/casting/support/CommissionerDeclaration.java b/examples/tv-casting-app/android/App/app/src/main/jni/com/matter/casting/support/CommissionerDeclaration.java
index 401c948..9614df5 100644
--- a/examples/tv-casting-app/android/App/app/src/main/jni/com/matter/casting/support/CommissionerDeclaration.java
+++ b/examples/tv-casting-app/android/App/app/src/main/jni/com/matter/casting/support/CommissionerDeclaration.java
@@ -110,6 +110,9 @@
    */
   private boolean cancelPasscode = false;
 
+  /** Feature: Commissioner-Generated Passcode - Length of passcode */
+  private int passcodeLength = 0;
+
   public CommissionerDeclaration(
       int errorCode,
       boolean needsPasscode,
@@ -117,7 +120,8 @@
       boolean passcodeDialogDisplayed,
       boolean commissionerPasscode,
       boolean qRCodeDisplayed,
-      boolean cancelPasscode) {
+      boolean cancelPasscode,
+      int passcodeLength) {
     this.errorCode = CdError.values()[errorCode];
     this.needsPasscode = needsPasscode;
     this.noAppsFound = noAppsFound;
@@ -125,6 +129,7 @@
     this.commissionerPasscode = commissionerPasscode;
     this.qRCodeDisplayed = qRCodeDisplayed;
     this.cancelPasscode = cancelPasscode;
+    this.passcodeLength = passcodeLength;
   }
 
   public void setErrorCode(CdError errorCode) {
@@ -179,6 +184,10 @@
     return this.cancelPasscode;
   }
 
+  public int getPasscodeLength() {
+    return this.passcodeLength;
+  }
+
   @Override
   public String toString() {
     return "CommissionerDeclaration::errorCode:               "
@@ -196,6 +205,9 @@
         + "CommissionerDeclaration:commissionerPasscode:     "
         + commissionerPasscode
         + "\n"
+        + "CommissionerDeclaration:passcodeLength:     "
+        + passcodeLength
+        + "\n"
         + "CommissionerDeclaration::qRCodeDisplayed:         "
         + qRCodeDisplayed
         + "\n"
diff --git a/examples/tv-casting-app/android/App/app/src/main/jni/com/matter/casting/support/IdentificationDeclarationOptions.java b/examples/tv-casting-app/android/App/app/src/main/jni/com/matter/casting/support/IdentificationDeclarationOptions.java
index 48c7ace..0c38cdb 100644
--- a/examples/tv-casting-app/android/App/app/src/main/jni/com/matter/casting/support/IdentificationDeclarationOptions.java
+++ b/examples/tv-casting-app/android/App/app/src/main/jni/com/matter/casting/support/IdentificationDeclarationOptions.java
@@ -38,18 +38,21 @@
    * @param commissionerPasscode the commissioner passcode flag.
    * @param commissionerPasscodeReady the commissioner passcode ready flag.
    * @param cancelPasscode the cancel passcode flag.
+   * @param passcodeLength the passcode length.
    */
   public IdentificationDeclarationOptions(
       boolean noPasscode,
       boolean cdUponPasscodeDialog,
       boolean commissionerPasscode,
       boolean commissionerPasscodeReady,
-      boolean cancelPasscode) {
+      boolean cancelPasscode,
+      int passcodeLength) {
     this.noPasscode = noPasscode;
     this.cdUponPasscodeDialog = cdUponPasscodeDialog;
     this.commissionerPasscode = commissionerPasscode;
     this.commissionerPasscodeReady = commissionerPasscodeReady;
     this.cancelPasscode = cancelPasscode;
+    this.passcodeLength = passcodeLength;
   }
 
   /**
@@ -87,6 +90,8 @@
    * to exit the commissioning process.
    */
   private boolean cancelPasscode = false;
+  /** Feature: Coordinate Passcode Dialogs Flag - to indicate the passcode length. */
+  private int passcodeLength = 0;
   /**
    * Feature: Target Content Application - The set of content app Vendor IDs (and optionally,
    * Product IDs) that can be used for authentication. Also, if TargetAppInfo is passed in,
@@ -132,6 +137,10 @@
     return cancelPasscode;
   }
 
+  public int getPasscodeLength() {
+    return passcodeLength;
+  }
+
   public List<TargetAppInfo> getTargetAppInfoList() {
     return targetAppInfos;
   }
@@ -154,6 +163,9 @@
     sb.append("IdentificationDeclarationOptions::cancelPasscode:            ")
         .append(cancelPasscode)
         .append("\n");
+    sb.append("IdentificationDeclarationOptions::passcodeLength:            ")
+        .append(passcodeLength)
+        .append("\n");
     sb.append("IdentificationDeclarationOptions::targetAppInfos list: \n");
 
     for (TargetAppInfo targetAppInfo : targetAppInfos) {
diff --git a/examples/tv-casting-app/android/App/app/src/main/jni/cpp/support/Converters-JNI.cpp b/examples/tv-casting-app/android/App/app/src/main/jni/cpp/support/Converters-JNI.cpp
index 6ed8544..42f6a16 100644
--- a/examples/tv-casting-app/android/App/app/src/main/jni/cpp/support/Converters-JNI.cpp
+++ b/examples/tv-casting-app/android/App/app/src/main/jni/cpp/support/Converters-JNI.cpp
@@ -448,6 +448,7 @@
     jfieldID commissionerPasscodeReadyField = env->GetFieldID(idOptionsClass, "commissionerPasscodeReady", "Z");
     jfieldID cancelPasscodeField            = env->GetFieldID(idOptionsClass, "cancelPasscode", "Z");
     jfieldID targetAppInfosField            = env->GetFieldID(idOptionsClass, "targetAppInfos", "Ljava/util/List;");
+    jfieldID passcodeLengthField            = env->GetFieldID(idOptionsClass, "passcodeLength", "I");
     VerifyOrReturnValue(
         noPasscodeField != nullptr, nullptr,
         ChipLogError(AppServer, "convertIdentificationDeclarationOptionsFromJavaToCpp() noPasscodeField not found!"));
@@ -467,6 +468,9 @@
     VerifyOrReturnValue(
         targetAppInfosField != nullptr, nullptr,
         ChipLogError(AppServer, "convertIdentificationDeclarationOptionsFromJavaToCpp() targetAppInfosField not found!"));
+    VerifyOrReturnValue(
+        passcodeLengthField != nullptr, nullptr,
+        ChipLogError(AppServer, "convertIdentificationDeclarationOptionsFromJavaToCpp() passcodeLengthField not found!"));
 
     matter::casting::core::IdentificationDeclarationOptions * cppIdOptions =
         new matter::casting::core::IdentificationDeclarationOptions();
@@ -476,6 +480,7 @@
     cppIdOptions->mCommissionerPasscode      = env->GetBooleanField(jIdOptions, commissionerPasscodeField);
     cppIdOptions->mCommissionerPasscodeReady = env->GetBooleanField(jIdOptions, commissionerPasscodeReadyField);
     cppIdOptions->mCancelPasscode            = env->GetBooleanField(jIdOptions, cancelPasscodeField);
+    cppIdOptions->mPasscodeLength            = static_cast<uint8_t>(env->GetIntField(jIdOptions, passcodeLengthField));
 
     jobject targetAppInfosList = env->GetObjectField(jIdOptions, targetAppInfosField);
     VerifyOrReturnValue(
@@ -541,7 +546,7 @@
     return env->NewObject(jCommissionerDeclarationClass, jCommissionerDeclarationConstructor,
                           static_cast<jint>(cppCd.GetErrorCode()), cppCd.GetNeedsPasscode(), cppCd.GetNoAppsFound(),
                           cppCd.GetPasscodeDialogDisplayed(), cppCd.GetCommissionerPasscode(), cppCd.GetQRCodeDisplayed(),
-                          cppCd.GetCancelPasscode());
+                          cppCd.GetCancelPasscode(), cppCd.GetPasscodeLength());
 }
 
 }; // namespace support
diff --git a/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/MCCommissionerDeclaration.h b/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/MCCommissionerDeclaration.h
index f81d032..61bfe03 100644
--- a/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/MCCommissionerDeclaration.h
+++ b/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/MCCommissionerDeclaration.h
@@ -83,6 +83,8 @@
  * user has decided to exit the commissioning process.
  */
 @property (nonatomic, readonly) BOOL cancelPasscode;
+/** Feature: Commissioner-Generated Passcode - length of passcode displayed. */
+@property (nonatomic, readonly) NSInteger passcodeLength;
 
 - (instancetype)initWithOptions:(NSInteger)errorCode
                   needsPasscode:(BOOL)needsPasscode
@@ -90,7 +92,8 @@
         passcodeDialogDisplayed:(BOOL)passcodeDialogDisplayed
            commissionerPasscode:(BOOL)commissionerPasscode
                 qRCodeDisplayed:(BOOL)qRCodeDisplayed
-                 cancelPasscode:(BOOL)cancelPasscode;
+                 cancelPasscode:(BOOL)cancelPasscode
+                 passcodeLength:(NSInteger)passcodeLength;
 
 /**
  * Function to return the error code as a string.
diff --git a/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/MCCommissionerDeclaration.mm b/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/MCCommissionerDeclaration.mm
index c9388ac..874e2be 100644
--- a/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/MCCommissionerDeclaration.mm
+++ b/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/MCCommissionerDeclaration.mm
@@ -37,6 +37,7 @@
         _commissionerPasscode = cppCommissionerDeclaration->GetCommissionerPasscode();
         _qRCodeDisplayed = cppCommissionerDeclaration->GetQRCodeDisplayed();
         _cancelPasscode = cppCommissionerDeclaration->GetCancelPasscode();
+        _passcodeLength = cppCommissionerDeclaration->GetPasscodeLength();
     }
     return self;
 }
@@ -48,6 +49,7 @@
            commissionerPasscode:(BOOL)commissionerPasscode
                 qRCodeDisplayed:(BOOL)qRCodeDisplayed
                  cancelPasscode:(BOOL)cancelPasscode
+                 passcodeLength:(NSInteger)passcodeLength
 {
     self = [super init];
     if (self) {
@@ -58,20 +60,22 @@
         _commissionerPasscode = commissionerPasscode;
         _qRCodeDisplayed = qRCodeDisplayed;
         _cancelPasscode = cancelPasscode;
+        _passcodeLength = passcodeLength;
     }
     return self;
 }
 
 - (NSString *)description
 {
-    return [NSString stringWithFormat:@"MCCommissionerDeclaration::errorCode:               %@\nMCCommissionerDeclaration::needsPasscode:           %d\nMCCommissionerDeclaration::noAppsFound:             %d\nMCCommissionerDeclaration::passcodeDialogDisplayed: %d\nMCCommissionerDeclaration::commissionerPasscode:    %d\nMCCommissionerDeclaration::qRCodeDisplayed:         %d\nMCCommissionerDeclaration::cancelPasscode:          %d",
+    return [NSString stringWithFormat:@"MCCommissionerDeclaration::errorCode:               %@\nMCCommissionerDeclaration::needsPasscode:           %d\nMCCommissionerDeclaration::noAppsFound:             %d\nMCCommissionerDeclaration::passcodeDialogDisplayed: %d\nMCCommissionerDeclaration::commissionerPasscode:    %d\nMCCommissionerDeclaration::qRCodeDisplayed:         %d\nMCCommissionerDeclaration::cancelPasscode:          %d\nMCCommissionerDeclaration::passcodeLength: %d",
                      [self stringForErrorCode:self.errorCode],
                      self.needsPasscode,
                      self.noAppsFound,
                      self.passcodeDialogDisplayed,
                      self.commissionerPasscode,
                      self.qRCodeDisplayed,
-                     self.cancelPasscode];
+                     self.cancelPasscode,
+                     self.passcodeLength];
 }
 
 - (NSString *)getErrorCodeString
diff --git a/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/MCIdentificationDeclarationOptions.h b/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/MCIdentificationDeclarationOptions.h
index 9896957..20deccc 100644
--- a/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/MCIdentificationDeclarationOptions.h
+++ b/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/MCIdentificationDeclarationOptions.h
@@ -54,11 +54,15 @@
  * to exit the commissioning process.
  */
 @property (nonatomic, readonly) BOOL cancelPasscode;
+/** Feature: Commissioner-Generated Passcode - length of passcode displayed. */
+@property (nonatomic, readonly) NSInteger passcodeLength;
 
 - (instancetype)init;
 
 - (instancetype)initWithCommissionerPasscodeOnly:(BOOL)commissionerPasscode;
 
+- (instancetype)initWithPasscodeLengthOnly:(NSInteger)passcodeLength;
+
 /**
  * @brief Adds a TargetAppInfo to the IdentificationDeclarationOptions.java TargetAppInfos list,
  *     up to a maximum of CHIP_DEVICE_CONFIG_UDC_MAX_TARGET_APPS.
diff --git a/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/MCIdentificationDeclarationOptions.mm b/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/MCIdentificationDeclarationOptions.mm
index 9d7d1f5..86fb32e 100644
--- a/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/MCIdentificationDeclarationOptions.mm
+++ b/examples/tv-casting-app/darwin/MatterTvCastingBridge/MatterTvCastingBridge/MCIdentificationDeclarationOptions.mm
@@ -43,6 +43,7 @@
         _commissionerPasscodeReady = NO;
         _cancelPasscode = NO;
         _targetAppInfos = [[NSMutableArray alloc] init];
+        _passcodeLength = 0;
     }
     return self;
 }
@@ -57,6 +58,22 @@
         _commissionerPasscodeReady = NO;
         _cancelPasscode = NO;
         _targetAppInfos = [[NSMutableArray alloc] init];
+        _passcodeLength = 0;
+    }
+    return self;
+}
+
+- (instancetype)initWithPasscodeLengthOnly:(NSInteger)passcodeLength
+{
+    self = [super init];
+    if (self) {
+        _noPasscode = NO;
+        _cdUponPasscodeDialog = NO;
+        _commissionerPasscode = NO;
+        _commissionerPasscodeReady = NO;
+        _cancelPasscode = NO;
+        _targetAppInfos = [[NSMutableArray alloc] init];
+        _passcodeLength = passcodeLength;
     }
     return self;
 }
@@ -87,6 +104,11 @@
     return _cancelPasscode;
 }
 
+- (NSInteger)getPasscodeLength
+{
+    return _passcodeLength;
+}
+
 - (BOOL)addTargetAppInfo:(MCTargetAppInfo *)targetAppInfo
 {
     if (self.targetAppInfos.count >= CHIP_DEVICE_CONFIG_UDC_MAX_TARGET_APPS) {
@@ -109,6 +131,7 @@
     [sb appendFormat:@"MCIdentificationDeclarationOptions::commissionerPasscode:      %d\n", self.commissionerPasscode];
     [sb appendFormat:@"MCIdentificationDeclarationOptions::commissionerPasscodeReady: %d\n", self.commissionerPasscodeReady];
     [sb appendFormat:@"MCIdentificationDeclarationOptions::cancelPasscode:            %d\n", self.cancelPasscode];
+    [sb appendFormat:@"MCIdentificationDeclarationOptions::passcodeLength:            %d\n", self.passcodeLength];
     [sb appendString:@"MCIdentificationDeclarationOptions::targetAppInfos list:\n"];
 
     for (MCTargetAppInfo * targetAppInfo in self.targetAppInfos) {
@@ -126,6 +149,7 @@
     cppIdOptions.mCommissionerPasscode = [self getCommissionerPasscode];
     cppIdOptions.mCommissionerPasscodeReady = [self getCommissionerPasscodeReady];
     cppIdOptions.mCancelPasscode = [self getCancelPasscode];
+    cppIdOptions.mPasscodeLength = [self getPasscodeLength];
 
     NSArray<MCTargetAppInfo *> * targetAppInfos = [self getTargetAppInfoList];
     for (MCTargetAppInfo * appInfo in targetAppInfos) {
diff --git a/examples/tv-casting-app/linux/CastingShellCommands.cpp b/examples/tv-casting-app/linux/CastingShellCommands.cpp
index 1807834..3229c88 100644
--- a/examples/tv-casting-app/linux/CastingShellCommands.cpp
+++ b/examples/tv-casting-app/linux/CastingShellCommands.cpp
@@ -236,6 +236,7 @@
         DeviceLayer::SetCommissionableDataProvider(&gCommissionableDataProvider);
 
         CastingServer::GetInstance()->SetCommissionerPasscodeReady();
+        return CHIP_NO_ERROR;
     }
     if (strcmp(argv[0], "testudc") == 0)
     {
@@ -248,6 +249,7 @@
         // ex. sendudc <address> <port> t t 111 5 'hello world'
 
         Protocols::UserDirectedCommissioning::IdentificationDeclaration id;
+        id.SetPasscodeLength(8);
         if (argc > 3)
         {
             id.SetNoPasscode(strcmp(argv[3], "t") == 0);
diff --git a/examples/tv-casting-app/tv-casting-common/core/IdentificationDeclarationOptions.h b/examples/tv-casting-app/tv-casting-common/core/IdentificationDeclarationOptions.h
index 23d361e..ec53505 100644
--- a/examples/tv-casting-app/tv-casting-common/core/IdentificationDeclarationOptions.h
+++ b/examples/tv-casting-app/tv-casting-common/core/IdentificationDeclarationOptions.h
@@ -61,6 +61,11 @@
      * Flag to indicate when the Commissionee user has decided to exit the commissioning process.
      */
     bool mCancelPasscode = false;
+    /**
+     * Feature: Coordinate Passcode Dialogs
+     * Field to indicate the length of the passcode displayed by the client.
+     */
+    uint8_t mPasscodeLength = 0;
 
     /**
      * Commissionee's (random) DNS-SD instance name. This field is mandatory and will be auto generated if not provided by the
@@ -92,6 +97,7 @@
         mCommissionerPasscode      = false;
         mCommissionerPasscodeReady = false;
         mCancelPasscode            = false;
+        mPasscodeLength            = 0;
         mTargetAppInfos.clear();
     }
 
@@ -113,6 +119,11 @@
         id.SetCdUponPasscodeDialog(mCdUponPasscodeDialog);
         id.SetCancelPasscode(mCancelPasscode);
         id.SetCommissionerPasscode(mCommissionerPasscode);
+        if (!mCommissionerPasscode)
+        {
+            // only makes sense to have a length when the client is displaying the passcode
+            id.SetPasscodeLength(mPasscodeLength);
+        }
         if (mCommissionerPasscodeReady)
         {
             id.SetCommissionerPasscodeReady(true);
@@ -157,6 +168,8 @@
         ChipLogDetail(AppServer, "IdentificationDeclarationOptions::mCancelPasscode:            %s",
                       mCancelPasscode ? "true" : "false");
         ChipLogDetail(AppServer, "IdentificationDeclarationOptions::mCommissioneeInstanceName:  %s", mCommissioneeInstanceName);
+        ChipLogDetail(AppServer, "IdentificationDeclarationOptions::mPasscodeLength:            %d",
+                      static_cast<uint16_t>(mPasscodeLength));
 
         ChipLogDetail(AppServer, "IdentificationDeclarationOptions::TargetAppInfos list:");
         for (size_t i = 0; i < mTargetAppInfos.size(); i++)
diff --git a/src/controller/CommissionerDiscoveryController.cpp b/src/controller/CommissionerDiscoveryController.cpp
index 1e338b4..b622037 100644
--- a/src/controller/CommissionerDiscoveryController.cpp
+++ b/src/controller/CommissionerDiscoveryController.cpp
@@ -436,11 +436,13 @@
 
     if (mPasscodeService != nullptr)
     {
+        ChipLogDetail(AppServer, "UX Ok: have a passcodeService");
         // if CommissionerPasscode
         //    - if CommissionerPasscodeReady, then start commissioning
         //    - if CommissionerPasscode, then call new UX method to show passcode, send CDC
         if (passcode == 0 && client->GetCommissionerPasscode() && client->GetCdPort() != 0)
         {
+            ChipLogDetail(AppServer, "UX Ok: commissioner passcode and passcode from apps is 0");
             char rotatingIdBuffer[Dnssd::kMaxRotatingIdLen * 2];
             size_t rotatingIdLength = client->GetRotatingIdLength();
             CHIP_ERROR err = Encoding::BytesToUppercaseHexBuffer(client->GetRotatingId(), rotatingIdLength, rotatingIdBuffer,
@@ -455,7 +457,9 @@
             // first step of commissioner passcode
             ChipLogError(AppServer, "UX Ok: commissioner passcode, sending CDC");
             // generate a passcode
-            passcode = mPasscodeService->GetCommissionerPasscode(client->GetVendorId(), client->GetProductId(), rotatingIdSpan);
+            PasscodeInfo passcodeInfo =
+                mPasscodeService->GetCommissionerPasscode(client->GetVendorId(), client->GetProductId(), rotatingIdSpan);
+            passcode = passcodeInfo.passcode;
             if (passcode == 0)
             {
                 // passcode feature disabled
@@ -473,6 +477,7 @@
 
             CommissionerDeclaration cd;
             cd.SetCommissionerPasscode(true);
+            cd.SetPasscodeLength(passcodeInfo.displayLength);
             if (mUserPrompter->DisplaysPasscodeAndQRCode())
             {
                 cd.SetQRCodeDisplayed(true);
@@ -488,11 +493,13 @@
                           client->GetPairingInst(), ChipLogValueMEI(client->GetVendorId()), ChipLogValueMEI(client->GetProductId()),
                           client->GetInstanceName());
             mUserPrompter->PromptWithCommissionerPasscode(client->GetVendorId(), client->GetProductId(), client->GetDeviceName(),
-                                                          passcode, client->GetPairingHint(), client->GetPairingInst());
+                                                          passcode, client->GetPasscodeLength(), client->GetPairingHint(),
+                                                          client->GetPairingInst());
             return;
         }
         if (passcode != 0)
         {
+            ChipLogDetail(AppServer, "UX Ok: passcode is not 0, commissioning");
             CommissionWithPasscode(passcode);
             return;
         }
@@ -522,7 +529,7 @@
     if (mUserPrompter != nullptr)
     {
         mUserPrompter->PromptForCommissionPasscode(client->GetVendorId(), client->GetProductId(), client->GetDeviceName(),
-                                                   client->GetPairingHint(), client->GetPairingInst());
+                                                   client->GetPairingHint(), client->GetPairingInst(), client->GetPasscodeLength());
     }
     ChipLogDetail(Controller, "------Via Shell Enter: controller ux ok [passcode]");
 }
diff --git a/src/controller/CommissionerDiscoveryController.h b/src/controller/CommissionerDiscoveryController.h
index 94f172c..7598aaa 100644
--- a/src/controller/CommissionerDiscoveryController.h
+++ b/src/controller/CommissionerDiscoveryController.h
@@ -76,10 +76,11 @@
      *  @param[in]    commissioneeName   The commissioneeName in the DNS-SD advertisement of the requesting commissionee.
      *  @param[in]    pairingHint        The pairingHint in the DNS-SD advertisement of the requesting commissionee.
      *  @param[in]    pairingInstruction The pairingInstruction in the DNS-SD advertisement of the requesting commissionee.
+     *  @param[in]    passcodeLength     The length of the passcode as specificed by the commissionee. 0 means not specified.
      *
      */
     virtual void PromptForCommissionPasscode(uint16_t vendorId, uint16_t productId, const char * commissioneeName,
-                                             uint16_t pairingHint, const char * pairingInstruction) = 0;
+                                             uint16_t pairingHint, const char * pairingInstruction, uint8_t passcodeLength) = 0;
 
     /**
      * @brief
@@ -112,12 +113,14 @@
      *  @param[in]    productId          The productId in the DNS-SD advertisement of the requesting commissionee.
      *  @param[in]    commissioneeName   The commissioneeName in the DNS-SD advertisement of the requesting commissionee.
      *  @param[in]    passcode           The passcode to display.
+     *  @param[in]    passcodeLength     The length of the passcode as specificed by the commissionee. 0 means not specified.
      *  @param[in]    pairingHint        The pairingHint in the DNS-SD advertisement of the requesting commissionee.
      *  @param[in]    pairingInstruction The pairingInstruction in the DNS-SD advertisement of the requesting commissionee.
      *
      */
     virtual void PromptWithCommissionerPasscode(uint16_t vendorId, uint16_t productId, const char * commissioneeName,
-                                                uint32_t passcode, uint16_t pairingHint, const char * pairingInstruction) = 0;
+                                                uint32_t passcode, uint8_t passcodeLength, uint16_t pairingHint,
+                                                const char * pairingInstruction) = 0;
 
     /**
      * @brief
@@ -153,6 +156,12 @@
     virtual ~UserPrompter() = default;
 };
 
+struct PasscodeInfo
+{
+    uint32_t passcode;
+    uint8_t displayLength;
+};
+
 class DLL_EXPORT PasscodeService
 {
 public:
@@ -177,14 +186,14 @@
     /**
      * @brief
      *   Called to get the commissioner-generated setup passcode.
-     * Returns 0 if feature is disabled.
+     * Returns {0, 0} if feature is disabled.
      *
      *  @param[in]    vendorId           The vendorId in the DNS-SD advertisement of the requesting commissionee.
      *  @param[in]    productId          The productId in the DNS-SD advertisement of the requesting commissionee.
      *  @param[in]    rotatingId         The rotatingId in the DNS-SD advertisement of the requesting commissionee.
      *
      */
-    virtual uint32_t GetCommissionerPasscode(uint16_t vendorId, uint16_t productId, chip::CharSpan rotatingId) = 0;
+    virtual PasscodeInfo GetCommissionerPasscode(uint16_t vendorId, uint16_t productId, chip::CharSpan rotatingId) = 0;
 
     /**
      * @brief
diff --git a/src/protocols/user_directed_commissioning/UDCClientState.h b/src/protocols/user_directed_commissioning/UDCClientState.h
index 32b1cf0..c24af39 100644
--- a/src/protocols/user_directed_commissioning/UDCClientState.h
+++ b/src/protocols/user_directed_commissioning/UDCClientState.h
@@ -185,6 +185,9 @@
     void SetCancelPasscode(bool newValue) { mCancelPasscode = newValue; };
     bool GetCancelPasscode() const { return mCancelPasscode; };
 
+    void SetPasscodeLength(uint8_t newValue) { mPasscodeLength = newValue; };
+    uint8_t GetPasscodeLength() const { return mPasscodeLength; };
+
     void SetCachedCommissionerPasscode(uint32_t newValue) { mCachedCommissionerPasscode = newValue; };
     uint32_t GetCachedCommissionerPasscode() const { return mCachedCommissionerPasscode; };
 
@@ -211,6 +214,7 @@
         mUDCClientProcessingState   = UDCClientProcessingState::kNotInitialized;
         mCachedCommissionerPasscode = 0;
         mNumTargetAppInfos          = 0;
+        mPasscodeLength             = 0;
     }
 
 private:
@@ -235,6 +239,7 @@
     bool mCommissionerPasscode      = false;
     bool mCommissionerPasscodeReady = false;
     bool mCancelPasscode            = false;
+    uint8_t mPasscodeLength         = 0;
 
     UDCClientProcessingState mUDCClientProcessingState;
     System::Clock::Timestamp mExpirationTime = System::Clock::kZero;
diff --git a/src/protocols/user_directed_commissioning/UserDirectedCommissioning.h b/src/protocols/user_directed_commissioning/UserDirectedCommissioning.h
index f6a3f9a..762df86 100644
--- a/src/protocols/user_directed_commissioning/UserDirectedCommissioning.h
+++ b/src/protocols/user_directed_commissioning/UserDirectedCommissioning.h
@@ -147,6 +147,9 @@
     void SetCancelPasscode(bool newValue) { mCancelPasscode = newValue; };
     bool GetCancelPasscode() const { return mCancelPasscode; };
 
+    void SetPasscodeLength(uint8_t newValue) { mPasscodeLength = newValue; };
+    uint8_t GetPasscodeLength() const { return mPasscodeLength; };
+
     /**
      *  Writes the IdentificationDeclaration message to the given buffer.
      *
@@ -185,6 +188,7 @@
         client->SetCommissionerPasscode(GetCommissionerPasscode());
         client->SetCommissionerPasscodeReady(GetCommissionerPasscodeReady());
         client->SetCancelPasscode(GetCancelPasscode());
+        client->SetPasscodeLength(GetPasscodeLength());
     }
 
     void DebugLog()
@@ -247,6 +251,10 @@
         {
             ChipLogDetail(AppServer, "\tcancel passcode: true");
         }
+        if (mPasscodeLength != 0)
+        {
+            ChipLogDetail(AppServer, "\tpasscode length: %d", static_cast<uint16_t>(mPasscodeLength));
+        }
         ChipLogDetail(AppServer, "---- Identification Declaration End ----");
     }
 
@@ -271,6 +279,7 @@
         kCommissionerPasscodeTag,
         kCommissionerPasscodeReadyTag,
         kCancelPasscodeTag,
+        kPasscodeLengthTag,
 
         kMaxNum = UINT8_MAX
     };
@@ -296,6 +305,7 @@
     bool mCommissionerPasscode      = false;
     bool mCommissionerPasscodeReady = false;
     bool mCancelPasscode            = false;
+    uint8_t mPasscodeLength         = 0;
 };
 
 /**
@@ -351,6 +361,9 @@
     void SetCancelPasscode(bool newValue) { mCancelPasscode = newValue; };
     bool GetCancelPasscode() const { return mCancelPasscode; };
 
+    void SetPasscodeLength(uint8_t newValue) { mPasscodeLength = newValue; };
+    uint8_t GetPasscodeLength() const { return mPasscodeLength; };
+
     /**
      *  Writes the CommissionerDeclaration message to the given buffer.
      *
@@ -396,6 +409,10 @@
         {
             ChipLogDetail(AppServer, "\tPasscode cancelled: true");
         }
+        if (mPasscodeLength != 0)
+        {
+            ChipLogDetail(AppServer, "\tpasscode length: %d", static_cast<uint16_t>(mPasscodeLength));
+        }
         ChipLogDetail(AppServer, "---- Commissioner Declaration End ----");
     }
 
@@ -410,6 +427,7 @@
         kCommissionerPasscodeTag,
         kQRCodeDisplayedTag,
         kCancelPasscodeTag,
+        kPasscodeLengthTag,
 
         kMaxNum = UINT8_MAX
     };
@@ -421,6 +439,7 @@
     bool mCommissionerPasscode    = false;
     bool mQRCodeDisplayed         = false;
     bool mCancelPasscode          = false;
+    uint8_t mPasscodeLength       = 0;
 };
 
 class DLL_EXPORT InstanceNameResolver
diff --git a/src/protocols/user_directed_commissioning/UserDirectedCommissioningClient.cpp b/src/protocols/user_directed_commissioning/UserDirectedCommissioningClient.cpp
index f0ee3bb..fbaece7 100644
--- a/src/protocols/user_directed_commissioning/UserDirectedCommissioningClient.cpp
+++ b/src/protocols/user_directed_commissioning/UserDirectedCommissioningClient.cpp
@@ -169,6 +169,8 @@
                  LogErrorOnFailure(err));
     VerifyOrExit(CHIP_NO_ERROR == (err = writer.PutBoolean(chip::TLV::ContextTag(kCancelPasscodeTag), mCancelPasscode)),
                  LogErrorOnFailure(err));
+    VerifyOrExit(CHIP_NO_ERROR == (err = writer.Put(chip::TLV::ContextTag(kPasscodeLengthTag), GetPasscodeLength())),
+                 LogErrorOnFailure(err));
 
     VerifyOrExit(CHIP_NO_ERROR == (err = writer.EndContainer(outerContainerType)), LogErrorOnFailure(err));
     VerifyOrExit(CHIP_NO_ERROR == (err = writer.Finalize()), LogErrorOnFailure(err));
@@ -226,6 +228,9 @@
         case kCancelPasscodeTag:
             err = reader.Get(mCancelPasscode);
             break;
+        case kPasscodeLengthTag:
+            err = reader.Get(mPasscodeLength);
+            break;
         }
     }
 
diff --git a/src/protocols/user_directed_commissioning/UserDirectedCommissioningServer.cpp b/src/protocols/user_directed_commissioning/UserDirectedCommissioningServer.cpp
index 7e1f602..b2937a8 100644
--- a/src/protocols/user_directed_commissioning/UserDirectedCommissioningServer.cpp
+++ b/src/protocols/user_directed_commissioning/UserDirectedCommissioningServer.cpp
@@ -386,6 +386,9 @@
         case kCancelPasscodeTag:
             err = reader.Get(mCancelPasscode);
             break;
+        case kPasscodeLengthTag:
+            err = reader.Get(mPasscodeLength);
+            break;
         }
         if (err != CHIP_NO_ERROR)
         {
@@ -437,6 +440,8 @@
                  LogErrorOnFailure(err));
     VerifyOrExit(CHIP_NO_ERROR == (err = writer.PutBoolean(chip::TLV::ContextTag(kCancelPasscodeTag), mCancelPasscode)),
                  LogErrorOnFailure(err));
+    VerifyOrExit(CHIP_NO_ERROR == (err = writer.Put(chip::TLV::ContextTag(kPasscodeLengthTag), GetPasscodeLength())),
+                 LogErrorOnFailure(err));
 
     VerifyOrExit(CHIP_NO_ERROR == (err = writer.EndContainer(outerContainerType)), LogErrorOnFailure(err));
     VerifyOrExit(CHIP_NO_ERROR == (err = writer.Finalize()), LogErrorOnFailure(err));
diff --git a/src/protocols/user_directed_commissioning/tests/TestUdcMessages.cpp b/src/protocols/user_directed_commissioning/tests/TestUdcMessages.cpp
index d6115a7..49d9fde 100644
--- a/src/protocols/user_directed_commissioning/tests/TestUdcMessages.cpp
+++ b/src/protocols/user_directed_commissioning/tests/TestUdcMessages.cpp
@@ -390,6 +390,7 @@
     const char * deviceName   = "device1";
     uint16_t pairingHint      = 33;
     const char * pairingInst  = "Read 6 digit code from screen";
+    uint8_t passcodeLength    = 6;
 
     TargetAppInfo appInfo;
 
@@ -426,6 +427,7 @@
     id.SetCdUponPasscodeDialog(true);
     id.SetCommissionerPasscode(true);
     id.SetCommissionerPasscodeReady(true);
+    id.SetPasscodeLength(passcodeLength);
 
     EXPECT_TRUE(id.HasDiscoveryInfo());
     EXPECT_STREQ(id.GetInstanceName(), instanceName);
@@ -446,6 +448,7 @@
     EXPECT_EQ(id.GetCdUponPasscodeDialog(), true);
     EXPECT_EQ(id.GetCommissionerPasscode(), true);
     EXPECT_EQ(id.GetCommissionerPasscodeReady(), true);
+    EXPECT_EQ(id.GetPasscodeLength(), passcodeLength);
 
     // TODO: add an ip
 
@@ -475,6 +478,7 @@
     EXPECT_EQ(id.GetCdUponPasscodeDialog(), idOut.GetCdUponPasscodeDialog());
     EXPECT_EQ(id.GetCommissionerPasscode(), idOut.GetCommissionerPasscode());
     EXPECT_EQ(id.GetCommissionerPasscodeReady(), idOut.GetCommissionerPasscodeReady());
+    EXPECT_EQ(id.GetPasscodeLength(), idOut.GetPasscodeLength());
 
     // TODO: remove following "force-fail" debug line
     // NL_TEST_ASSERT(inSuite, rotatingIdLen != id.GetRotatingIdLength());
@@ -493,6 +497,7 @@
     id.SetPasscodeDialogDisplayed(true);
     id.SetCommissionerPasscode(true);
     id.SetQRCodeDisplayed(true);
+    id.SetPasscodeLength(8);
 
     EXPECT_EQ(errorCode, id.GetErrorCode());
     EXPECT_EQ(id.GetNeedsPasscode(), true);
@@ -500,6 +505,7 @@
     EXPECT_EQ(id.GetPasscodeDialogDisplayed(), true);
     EXPECT_EQ(id.GetCommissionerPasscode(), true);
     EXPECT_EQ(id.GetQRCodeDisplayed(), true);
+    EXPECT_EQ(id.GetPasscodeLength(), 8);
 
     uint8_t idBuffer[500];
     id.WritePayload(idBuffer, sizeof(idBuffer));
@@ -513,4 +519,5 @@
     EXPECT_EQ(id.GetPasscodeDialogDisplayed(), idOut.GetPasscodeDialogDisplayed());
     EXPECT_EQ(id.GetCommissionerPasscode(), idOut.GetCommissionerPasscode());
     EXPECT_EQ(id.GetQRCodeDisplayed(), idOut.GetQRCodeDisplayed());
+    EXPECT_EQ(id.GetPasscodeLength(), idOut.GetPasscodeLength());
 }