OpenNHP/opennhp · error

failed to unwrap data private key

Error message

failed to unwrap data private key: %s

What it means

The agent could not unwrap the ztdo data private key. It derives a symmetric key via a Noise-style agreement with the provider's public key (saDataPrk.AgreeSymmetricKey) and calls dataPrkWrapping.Unwrap; any failure (wrong key material, tampered wrapping blob, wrong cipher scheme/ECC mode) is wrapped here.

Solutions

  1. Verify the provider public key (ProviderPublicKeyBase64) is the current key that actually wrapped the data private key.
  2. Confirm both sides use the same cipher scheme / ECC mode (curve vs gmsm).
  3. Check that agent and provider ztdolib versions agree on the key-wrapping patterns and PSK constants.
  4. Ask the provider to re-encrypt with its current key and re-publish the ztdo.
  5. Confirm the ztdo id mismatch check passed — a wrong record will also fail unwrapping.

Example fix

// before: provider key from message trusted blindly
providerPbk, _ := base64.StdEncoding.DecodeString(dataPrkWrapping.ProviderPublicKeyBase64)

// after: decode with error check
providerPbk, err := base64.StdEncoding.DecodeString(dataPrkWrapping.ProviderPublicKeyBase64)
if err != nil || len(providerPbk) != expectedKeyLen {
    return "", fmt.Errorf("invalid provider public key: %w", err)
}
Defensive patterns

Strategy: validation

Validate before calling

pbk, err := base64.StdEncoding.DecodeString(dataPrkWrapping.ProviderPublicKeyBase64)
if err != nil {
    return fmt.Errorf("provider public key is not valid base64")
}
if len(pbk) != expectedPublicKeyLen(eccMode) {
    return fmt.Errorf("provider public key length mismatch for ecc mode")
}

Type guard

func validProviderKey(b64 string, mode ztdolib.ECCMode) bool {
    k, err := base64.StdEncoding.DecodeString(b64)
    return err == nil && len(k) == expectedPublicKeyLen(mode)
}

Try / catch

prk, err := dataPrkWrapping.Unwrap(gcmKey[:], ad)
if err != nil {
    // likely key rotation; refetch provider pubkey and retry once
    return fmt.Errorf("data private key unwrap failed (check provider key rotation): %w", err)
}

Prevention

When it happens

Trigger: dataPrkWrapping.Unwrap(gcmKey[:], ad) errors: the ProviderPublicKeyBase64 doesn't match the key that wrapped the data private key, the wrapped key blob was altered, or the DHP key-wrapping pattern/PSK constants differ between producer and consumer versions.

Common situations: Provider rotated its key pair after encrypting; agent configured with an outdated provider public key; mixed SM2 vs Curve25519 (ECC mode) implementations; ztdo produced by an older library version using different InitialDHPKeyWrappingString.

Related errors


AI-assisted analysis of OpenNHP/opennhp@6e04ca5ff0 (2026-09-07). Data as JSON: /api/errors/50e2dcb876de56f5. Report an issue: GitHub.

Appendix: source

Thrown at endpoints/agent/udpagent.go:1399

			if ztdoId != ztdo.GetObjectID() {
				fmt.Printf("Error: ztdo id mismatch, please check with data provider\n")
				return "", fmt.Errorf("ztdo id mismatch, please check with data provider")
			}

			// decrypt data private key
			saDataPrk := ztdolib.NewSymmetricAgreement(ztdo.GetECCMode(), false)
			saDataPrk.SetMessagePatterns(ztdolib.DataPrivateKeyWrappingPatterns)
			saDataPrk.SetPsk([]byte(ztdolib.InitialDHPKeyWrappingString))
			saDataPrk.SetStaticKeyPair(teeEcdh)
			saDataPrk.SetEphemeralKeyPair(consumerEphemeralEcdh)
			saDataPrk.SetRemoteStaticPublicKey(providerPbk)

			gcmKey, ad := saDataPrk.AgreeSymmetricKey()

			dataPrkBase64, err := dataPrkWrapping.Unwrap(gcmKey[:], ad)
			if err != nil {
				return "", fmt.Errorf("failed to unwrap data private key: %s", err)
			}

			if ztdoPath == "" || output == "" {
				return "", fmt.Errorf("ztdo path or output is empty")
			}

			// decrypt data
			dataKeyPairEccMode := ztdo.GetECCMode()

			dataMsgPattern := [][]ztdolib.MessagePattern{
				{ztdolib.MessagePatternS, ztdolib.MessagePatternDHSS},
				{ztdolib.MessagePatternRS, ztdolib.MessagePatternDHSS},
			}

			dataPrk, _ := base64.StdEncoding.DecodeString(dataPrkBase64)
			saData := ztdolib.NewSymmetricAgreement(dataKeyPairEccMode, false)
			saData.SetMessagePatterns(dataMsgPattern)
			saData.SetStaticKeyPair(core.ECDHFromKey(dataKeyPairEccMode.ToEccType(), dataPrk))

View on GitHub (pinned to 6e04ca5ff0)