code: func GenerateJoinToken(rootCA *RootCA, fips bool) string {
  	var secretBytes [generatedSecretEntropyBytes]byte
  
  	if _, err := cryptorand.Read(secretBytes[:]); err != nil {
  		panic(fmt.Errorf("failed to read random bytes: %v", err))
  	}
  
  	var nn, digest big.Int
  	nn.SetBytes(secretBytes[:])
  	digest.SetString(rootCA.Digest.Hex(), 16)
  
  	fmtString := "SWMTKN-1-%0[1]*s-%0[3]*s"
  	if fips {
  		fmtString = "SWMTKN-2-1-%0[1]*s-%0[3]*s"
  	}
  	return fmt.Sprintf(fmtString, base36DigestLen,
  		digest.Text(joinTokenBase), maxGeneratedSecretLength, nn.Text(joinTokenBase))
  }
docs: // GenerateJoinToken creates a new join token. Current format is v2, but this is
  // currently used only if the cluster requires mandatory FIPS, in order to
  // facilitate mixed version clusters (the `fips` parameter is set to true).
  // Otherwise, v1 is used so as to maintain compatibility in mixed version
  // non-FIPS clusters.
  // v1: SWMTKN-1-<SHA256 digest of root CA cert in base 36, 0-left-padded to 50 chars>-<16-byte secret in base 36 0-left-padded to 25 chars>
  // v2: SWMTKN-2-<0/1 whether its FIPS or not>-<same rest of data as v1>