package tpm2 import ( "fmt" ) type errorDesc struct { name string description string } var fmt0Descs = map[TPMRC]errorDesc{ TPMRCInitialize: { name: "TPM_RC_INITIALIZE", description: "TPM not initialized by TPM2_Startup or already initialized", }, TPMRCFailure: { name: "TPM_RC_FAILURE", description: "commands not being accepted because of a TPM failure", }, TPMRCSequence: { name: "TPM_RC_SEQUENCE", description: "improper use of a sequence handle", }, TPMRCPrivate: { name: "TPM_RC_PRIVATE", description: "not currently used", }, TPMRCHMAC: { name: "TPM_RC_HMAC", description: "not currently used", }, TPMRCDisabled: { name: "TPM_RC_DISABLED", description: "the command is disabled", }, TPMRCExclusive: { name: "TPM_RC_EXCLUSIVE", description: "command failed because audit sequence required exclusivity", }, TPMRCAuthType: { name: "TPM_RC_AUTH_TYPE", description: "authorization handle is not correct for command", }, TPMRCAuthMissing: { name: "TPM_RC_AUTH_MISSING", description: "command requires an authorization session for handle and it is not present.", }, TPMRCPolicy: { name: "TPM_RC_POLICY", description: "policy failure in math operation or an invalid authPolicy value", }, TPMRCPCR: { name: "TPM_RC_PCR", description: "PCR check fail", }, TPMRCPCRChanged: { name: "TPM_RC_PCR_CHANGED", description: "PCR have changed since checked.", }, TPMRCUpgrade: { name: "TPM_RC_UPGRADE", description: "for all commands other than TPM2_FieldUpgradeData(), this code indicates that the TPM is in field upgrade mode; for TPM2_FieldUpgradeData(), this code indicates that the TPM is not in field upgrade mode", }, TPMRCTooManyContexts: { name: "TPM_RC_TOO_MANY_CONTEXTS", description: "context ID counter is at maximum.", }, TPMRCAuthUnavailable: { name: "TPM_RC_AUTH_UNAVAILABLE", description: "authValue or authPolicy is not available for selected entity.", }, TPMRCReboot: { name: "TPM_RC_REBOOT", description: "a _TPM_Init and Startup(CLEAR) is required before the TPM can resume operation.", }, TPMRCUnbalanced: { name: "TPM_RC_UNBALANCED", description: "the protection algorithms (hash and symmetric) are not reasonably balanced. The digest size of the hash must be larger than the key size of the symmetric algorithm.", }, TPMRCCommandSize: { name: "TPM_RC_COMMAND_SIZE", description: "command commandSize value is inconsistent with contents of the command buffer; either the size is not the same as the octets loaded by the hardware interface layer or the value is not large enough to hold a command header", }, TPMRCCommandCode: { name: "TPM_RC_COMMAND_CODE", description: "command code not supported", }, TPMRCAuthSize: { name: "TPM_RC_AUTHSIZE", description: "the value of authorizationSize is out of range or the number of octets in the Authorization Area is greater than required", }, TPMRCAuthContext: { name: "TPM_RC_AUTH_CONTEXT", description: "use of an authorization session with a context command or another command that cannot have an authorization session.", }, TPMRCNVRange: { name: "TPM_RC_NV_RANGE", description: "NV offset+size is out of range.", }, TPMRCNVSize: { name: "TPM_RC_NV_SIZE", description: "Requested allocation size is larger than allowed.", }, TPMRCNVLocked: { name: "TPM_RC_NV_LOCKED", description: "NV access locked.", }, TPMRCNVAuthorization: { name: "TPM_RC_NV_AUTHORIZATION", description: "NV access authorization fails in command actions (this failure does not affect lockout.action)", }, TPMRCNVUninitialized: { name: "TPM_RC_NV_UNINITIALIZED", description: "an NV Index is used before being initialized or the state saved by TPM2_Shutdown(STATE) could not be restored", }, TPMRCNVSpace: { name: "TPM_RC_NV_SPACE", description: "insufficient space for NV allocation", }, TPMRCNVDefined: { name: "TPM_RC_NV_DEFINED", description: "NV Index or persistent object already defined", }, TPMRCBadContext: { name: "TPM_RC_BAD_CONTEXT", description: "context in TPM2_ContextLoad() is not valid", }, TPMRCCPHash: { name: "TPM_RC_CPHASH", description: "cpHash value already set or not correct for use", }, TPMRCParent: { name: "TPM_RC_PARENT", description: "handle for parent is not a valid parent", }, TPMRCNeedsTest: { name: "TPM_RC_NEEDS_TEST", description: "some function needs testing.", }, TPMRCNoResult: { name: "TPM_RC_NO_RESULT", description: "an internal function cannot process a request due to an unspecified problem. This code is usually related to invalid parameters that are not properly filtered by the input unmarshaling code.", }, TPMRCSensitive: { name: "TPM_RC_SENSITIVE", description: "the sensitive area did not unmarshal correctly after decryption – this code is used in lieu of the other unmarshaling errors so that an attacker cannot determine where the unmarshaling error occurred", }, } var fmt1Descs = map[TPMRC]errorDesc{ TPMRCAsymmetric: { name: "TPM_RC_ASYMMETRIC RC_FMT1", description: "asymmetric algorithm not supported or not correct", }, TPMRCAttributes: { name: "TPM_RC_ATTRIBUTES", description: "inconsistent attributes", }, TPMRCHash: { name: "TPM_RC_HASH", description: "hash algorithm not supported or not appropriate", }, TPMRCValue: { name: "TPM_RC_VALUE", description: "value is out of range or is not correct for the context", }, TPMRCHierarchy: { name: "TPM_RC_HIERARCHY", description: "hierarchy is not enabled or is not correct for the use", }, TPMRCKeySize: { name: "TPM_RC_KEY_SIZE", description: "key size is not supported", }, TPMRCMGF: { name: "TPM_RC_MGF", description: "mask generation function not supported", }, TPMRCMode: { name: "TPM_RC_MODE", description: "mode of operation not supported", }, TPMRCType: { name: "TPM_RC_TYPE", description: "the type of the value is not appropriate for the use", }, TPMRCHandle: { name: "TPM_RC_HANDLE", description: "the handle is not correct for the use", }, TPMRCKDF: { name: "TPM_RC_KDF", description: "unsupported key derivation function or function not appropriate for use", }, TPMRCRange: { name: "TPM_RC_RANGE", description: "value was out of allowed range.", }, TPMRCAuthFail: { name: "TPM_RC_AUTH_FAIL", description: "the authorization HMAC check failed and DA counter incremented", }, TPMRCNonce: { name: "TPM_RC_NONCE", description: "invalid nonce size or nonce value mismatch", }, TPMRCPP: { name: "TPM_RC_PP", description: "authorization requires assertion of PP", }, TPMRCScheme: { name: "TPM_RC_SCHEME", description: "unsupported or incompatible scheme", }, TPMRCSize: { name: "TPM_RC_SIZE", description: "structure is the wrong size", }, TPMRCSymmetric: { name: "TPM_RC_SYMMETRIC", description: "unsupported symmetric algorithm or key size, or not appropriate for instance", }, TPMRCTag: { name: "TPM_RC_TAG", description: "incorrect structure tag", }, TPMRCSelector: { name: "TPM_RC_SELECTOR", description: "union selector is incorrect", }, TPMRCInsufficient: { name: "TPM_RC_INSUFFICIENT", description: "the TPM was unable to unmarshal a value because there were not enough octets in the input buffer", }, TPMRCSignature: { name: "TPM_RC_SIGNATURE", description: "the signature is not valid", }, TPMRCKey: { name: "TPM_RC_KEY", description: "key fields are not compatible with the selected use", }, TPMRCPolicyFail: { name: "TPM_RC_POLICY_FAIL", description: "a policy check failed", }, TPMRCIntegrity: { name: "TPM_RC_INTEGRITY", description: "integrity check failed", }, TPMRCTicket: { name: "TPM_RC_TICKET", description: "invalid ticket", }, TPMRCReservedBits: { name: "TPM_RC_RESERVED_BITS", description: "reserved bits not set to zero as required", }, TPMRCBadAuth: { name: "TPM_RC_BAD_AUTH", description: "authorization failure without DA implications", }, TPMRCExpired: { name: "TPM_RC_EXPIRED", description: "the policy has expired", }, TPMRCPolicyCC: { name: "TPM_RC_POLICY_CC", description: "the commandCode in the policy is not the commandCode of the command or the command code in a policy command references a command that is not implemented", }, TPMRCBinding: { name: "TPM_RC_BINDING", description: "public and sensitive portions of an object are not cryptographically bound", }, TPMRCCurve: { name: "TPM_RC_CURVE", description: "curve not supported", }, TPMRCECCPoint: { name: "TPM_RC_ECC_POINT", description: "point is not on the required curve.", }, } var warnDescs = map[TPMRC]errorDesc{ TPMRCContextGap: { name: "TPM_RC_CONTEXT_GAP", description: "gap for context ID is too large", }, TPMRCObjectMemory: { name: "TPM_RC_OBJECT_MEMORY", description: "out of memory for object contexts", }, TPMRCSessionMemory: { name: "TPM_RC_SESSION_MEMORY", description: "out of memory for session contexts", }, TPMRCMemory: { name: "TPM_RC_MEMORY", description: "out of shared object/session memory or need space for internal operations", }, TPMRCSessionHandles: { name: "TPM_RC_SESSION_HANDLES", description: "out of session handles – a session must be flushed before a new session may be created", }, TPMRCObjectHandles: { name: "TPM_RC_OBJECT_HANDLES", description: "out of object handles – the handle space for objects is depleted and a reboot is required", }, TPMRCLocality: { name: "TPM_RC_LOCALITY", description: "bad locality", }, TPMRCYielded: { name: "TPM_RC_YIELDED", description: "the TPM has suspended operation on the command; forward progress was made and the command may be retried", }, TPMRCCanceled: { name: "TPM_RC_CANCELED", description: "the command was canceled", }, TPMRCTesting: { name: "TPM_RC_TESTING", description: "TPM is performing self-tests", }, TPMRCReferenceH0: { name: "TPM_RC_REFERENCE_H0", description: "the 1st handle in the handle area references a transient object or session that is not loaded", }, TPMRCReferenceH1: { name: "TPM_RC_REFERENCE_H1", description: "the 2nd handle in the handle area references a transient object or session that is not loaded", }, TPMRCReferenceH2: { name: "TPM_RC_REFERENCE_H2", description: "the 3rd handle in the handle area references a transient object or session that is not loaded", }, TPMRCReferenceH3: { name: "TPM_RC_REFERENCE_H3", description: "the 4th handle in the handle area references a transient object or session that is not loaded", }, TPMRCReferenceH4: { name: "TPM_RC_REFERENCE_H4", description: "the 5th handle in the handle area references a transient object or session that is not loaded", }, TPMRCReferenceH5: { name: "TPM_RC_REFERENCE_H5", description: "the 6th handle in the handle area references a transient object or session that is not loaded", }, TPMRCReferenceH6: { name: "TPM_RC_REFERENCE_H6", description: "the 7th handle in the handle area references a transient object or session that is not loaded", }, TPMRCReferenceS0: { name: "TPM_RC_REFERENCE_S0", description: "the 1st authorization session handle references a session that is not loaded", }, TPMRCReferenceS1: { name: "TPM_RC_REFERENCE_S1", description: "the 2nd authorization session handle references a session that is not loaded", }, TPMRCReferenceS2: { name: "TPM_RC_REFERENCE_S2", description: "the 3rd authorization session handle references a session that is not loaded", }, TPMRCReferenceS3: { name: "TPM_RC_REFERENCE_S3", description: "the 4th authorization session handle references a session that is not loaded", }, TPMRCReferenceS4: { name: "TPM_RC_REFERENCE_S4", description: "the 5th session handle references a session that is not loaded", }, TPMRCReferenceS5: { name: "TPM_RC_REFERENCE_S5", description: "the 6th session handle references a session that is not loaded", }, TPMRCReferenceS6: { name: "TPM_RC_REFERENCE_S6", description: "the 7th authorization session handle references a session that is not loaded", }, TPMRCNVRate: { name: "TPM_RC_NV_RATE", description: "the TPM is rate-limiting accesses to prevent wearout of NV", }, TPMRCLockout: { name: "TPM_RC_LOCKOUT", description: "authorizations for objects subject to DA protection are not allowed at this time because the TPM is in DA lockout mode", }, TPMRCRetry: { name: "TPM_RC_RETRY", description: "the TPM was not able to start the command", }, TPMRCNVUnavailable: { name: "TPM_RC_NV_UNAVAILABLE", description: "the command may require writing of NV and NV is not current accessible", }, } // subject represents a subject of a TPM error code with additional details // (i.e., FMT1 codes) type subject int const ( handleRelated subject = iota + 1 parameterRelated sessionRelated ) // String returns the string representation of the ErrorSubject. func (s subject) String() string { switch s { case handleRelated: return "handle" case parameterRelated: return "parameter" case sessionRelated: return "session" default: return "unknown subject" } } // TPMFmt1Error represents a TPM 2.0 format-1 error, with additional information. type TPMFmt1Error struct { // The canonical TPM error code, with handle/parameter/session info // stripped out. canonical TPMRC // Whether this was a handle, parameter, or session error. subject subject // Which handle, parameter, or session was in error index int } // Error returns the string representation of the error. func (e TPMFmt1Error) Error() string { desc, ok := fmt1Descs[e.canonical] if !ok { return fmt.Sprintf("unknown format-1 error: %s %d (%x)", e.subject, e.index, uint32(e.canonical)) } return fmt.Sprintf("%s (%v %d): %s", desc.name, e.subject, e.index, desc.description) } // Handle returns whether the error is handle-related and if so, which handle is // in error. func (e TPMFmt1Error) Handle() (bool, int) { if e.subject != handleRelated { return false, 0 } return true, e.index } // Parameter returns whether the error is handle-related and if so, which handle // is in error. func (e TPMFmt1Error) Parameter() (bool, int) { if e.subject != parameterRelated { return false, 0 } return true, e.index } // Session returns whether the error is handle-related and if so, which handle // is in error. func (e TPMFmt1Error) Session() (bool, int) { if e.subject != sessionRelated { return false, 0 } return true, e.index } // isFmt0Error returns true if the result is a format-0 error. func (r TPMRC) isFmt0Error() bool { return (r&rcVer1) == rcVer1 && (r&rcWarn) != rcWarn } // isFmt1Error returns true and a format-1 error structure if the error is a // format-1 error. func (r TPMRC) isFmt1Error() (bool, TPMFmt1Error) { if (r & rcFmt1) != rcFmt1 { return false, TPMFmt1Error{} } subj := handleRelated if (r & rcP) == rcP { subj = parameterRelated r ^= rcP } else if (r & rcS) == rcS { subj = sessionRelated r ^= rcS } idx := int((r & 0xF00) >> 8) r &= 0xFFFFF0FF return true, TPMFmt1Error{ canonical: r, subject: subj, index: idx, } } // IsWarning returns true if the error is a warning code. // This usually indicates a problem with the TPM state, and not the command. // Retrying the command later may succeed. func (r TPMRC) IsWarning() bool { if isFmt1, _ := r.isFmt1Error(); isFmt1 { // There aren't any format-1 warnings. return false } return (r&rcVer1) == rcVer1 && (r&rcWarn) == rcWarn } // Error produces a nice human-readable representation of the error, parsing TPM // FMT1 errors as needed. func (r TPMRC) Error() string { if isFmt1, fmt1 := r.isFmt1Error(); isFmt1 { return fmt1.Error() } if r.isFmt0Error() { desc, ok := fmt0Descs[r] if !ok { return fmt.Sprintf("unknown format-0 error code (0x%x)", uint32(r)) } return fmt.Sprintf("%s: %s", desc.name, desc.description) } if r.IsWarning() { desc, ok := warnDescs[r] if !ok { return fmt.Sprintf("unknown warning (0x%x)", uint32(r)) } return fmt.Sprintf("%s: %s", desc.name, desc.description) } return fmt.Sprintf("unrecognized error code (0x%x)", uint32(r)) } // Is returns whether the TPMRC (which may be a FMT1 error) is equal to the // given canonical error. func (r TPMRC) Is(target error) bool { targetTPMRC, ok := target.(TPMRC) if !ok { return false } if isFmt1, fmt1 := r.isFmt1Error(); isFmt1 { return fmt1.canonical == targetTPMRC } return r == targetTPMRC } // As returns whether the error can be assigned to the given interface type. // If supported, it updates the value pointed at by target. // Supports the Fmt1Error type. func (r TPMRC) As(target interface{}) bool { pFmt1, ok := target.(*TPMFmt1Error) if !ok { return false } isFmt1, fmt1 := r.isFmt1Error() if !isFmt1 { return false } *pFmt1 = fmt1 return true }