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
- Verify the provider public key (ProviderPublicKeyBase64) is the current key that actually wrapped the data private key.
- Confirm both sides use the same cipher scheme / ECC mode (curve vs gmsm).
- Check that agent and provider ztdolib versions agree on the key-wrapping patterns and PSK constants.
- Ask the provider to re-encrypt with its current key and re-publish the ztdo.
- 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
- Track provider key rotations and re-encrypt data on rotation.
- Distribute provider public keys via the authenticated peer table, not ad hoc.
- Keep ztdolib versions and PSK/wrapping-pattern constants aligned across producer and consumer.
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
- failed to unmarshal data private key wrapping
- access url is empty, please check with data provider
- failed to parse ztdo header
- ztdo id mismatch, please check with data provider
- Failed to decrypt ztdo file
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)