AlistGo/alist · error

step3 response layer2 aes ecb decrypt failed: %w

Error message

step3 response layer2 aes ecb decrypt failed: %w

What it means

The second decryption layer of the SSO response failed: the hex-decoded bytes could not be decrypted with AES-ECB using KEY_HEX_2. The ciphertext was not produced with the expected static key — a strong signal the server-side protocol/keys changed or the payload is an error blob.

Source

Thrown at drivers/139/util.go:1227

	}

	hexInner := jsoniter.Get(decryptedLayer1StrBytes, "data").ToString()
	if hexInner == "" {
		return "", errors.New("missing data field in first layer decryption result")
	}
	log.Debugf("DEBUG: 第一层解密提取到 hex_inner, length=%d", len(hexInner))

	key2, err := hex.DecodeString(KEY_HEX_2)
	if err != nil {
		return "", fmt.Errorf("failed to decode KEY_HEX_2: %w", err)
	}
	hexInnerBytes, err := hex.DecodeString(hexInner)
	if err != nil {
		return "", fmt.Errorf("failed to decode hex_inner: %w", err)
	}
	finalJsonStrBytes, err := aesEcbDecrypt(hexInnerBytes, key2)
	if err != nil {
		return "", fmt.Errorf("step3 response layer2 aes ecb decrypt failed: %w", err)
	}
	log.Debugf("DEBUG: third party login response decrypted.")

	authToken := jsoniter.Get(finalJsonStrBytes, "authToken").ToString()
	if authToken == "" {
		return "", errors.New("failed to extract authToken from final decryption result")
	}

	account := jsoniter.Get(finalJsonStrBytes, "account").ToString()
	userDomainId := jsoniter.Get(finalJsonStrBytes, "userDomainId").ToString()
	if account == "" || userDomainId == "" {
		return "", errors.New("failed to extract account or userDomainId from final decryption result")
	}

	d.Account = account
	d.UserDomainID = userDomainId
	return base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("pc:%s:%s", account, authToken))), nil
}

View on GitHub (pinned to 843d9dc814)

Solutions

  1. Update OpenList to the newest release — key constants (KEY_HEX_1/KEY_HEX_2) and headers are re-captured when the app protocol changes
  2. Re-capture the traffic from the current official android client and update KEY_HEX_2 in a local fork if you cannot wait upstream
  3. Verify the whole flow with fresh cookies to rule out error payloads masquerading as ciphertext
  4. Report upstream with the failing step and app version if persistent
Defensive patterns

Strategy: try-catch

Validate before calling

// Pre-check ciphertext block alignment before ECB decrypt
if len(hexInnerBytes) == 0 || len(hexInnerBytes)%aes.BlockSize != 0 {
	return fmt.Errorf("layer2 ciphertext misaligned (%d bytes): protocol drift", len(hexInnerBytes))
}

Type guard

func isLayer2DecryptErr(err error) bool {
	return err != nil && strings.Contains(err.Error(), "layer2 aes ecb decrypt failed")
}

Try / catch

// Never auto-retry; refresh keys or update the driver
if isLayer2DecryptErr(err) {
	return fmt.Errorf("139 SSO keys outdated — update OpenList or re-capture KEY_HEX_2: %w", err)
}

Prevention

When it happens

Trigger: aesEcbDecrypt rejects the data (bad block size or unpadding failure) after hexInner decoded fine; typically after China Mobile rotates keys or alters the layer-2 scheme in a newer app build.

Common situations: OpenList driver version lagging behind a 139 cloud app update (keys/headers captured from old app builds), or an upstream error body accidentally passing earlier checks.

Related errors


AI-assisted analysis of AlistGo/alist@843d9dc814 (2026-08-15). Data as JSON: /api/errors/c62f78f7b4116e29. Report an issue: GitHub.