func appendHMAC(rTagPlaintext string, roleEntry *awsRoleEntry) (string, error) {
	if rTagPlaintext == "" {
		return "", fmt.Errorf("empty role tag plaintext string")
	}
	if roleEntry == nil {
		return "", fmt.Errorf("nil role entry")
	}
	hmacB64, err := createRoleTagHMACBase64(roleEntry.HMACKey, rTagPlaintext)
	if err != nil {
		return "", err
	}
	rTagValue := fmt.Sprintf("%s:%s", rTagPlaintext, hmacB64)
	if len(rTagValue) > 255 {
		return "", fmt.Errorf("role tag 'value' exceeding the limit of 255 characters")
	}
	return rTagValue, nil
}
Takes in the plaintext part of the role tag, creates a HMAC of it and returns
a role tag value containing both the plaintext part and the HMAC part.
package awsec2
import (
	"crypto/hmac"
	"crypto/sha256"
	"crypto/subtle"
	"encoding/base64"
	"fmt"
	"strconv"
	"strings"
	"time"
	"github.com/hashicorp/go-uuid"
	"github.com/hashicorp/vault/helper/policyutil"
	"github.com/hashicorp/vault/helper/strutil"
	"github.com/hashicorp/vault/logical"
	"github.com/hashicorp/vault/logical/framework"
)
const roleTagVersion = "v1"
func pathRoleTag(b *backend) *framework.Path {
	return &framework.Path{
		Pattern: "role/" + framework.GenericNameRegex("role") + "/tag$",
		Fields: map[string]*framework.FieldSchema{
			"role": &framework.FieldSchema{
				Type:        framework.TypeString,
				Description: "Name of the role.",
			},
			"instance_id": &framework.FieldSchema{
				Type: framework.TypeString,
				Description: `Instance ID for which this tag is intended for.
If set, the created tag can only be used by the instance with the given ID.`,
			},
			"policies": &framework.FieldSchema{
				Type:        framework.TypeString,
				Description: "Policies to be associated with the tag. If set, must be a subset of the role's policies. If set, but set to an empty value, only the 'default' policy will be given to issued tokens.",
			},
			"max_ttl": &framework.FieldSchema{
				Type:        framework.TypeDurationSecond,
				Default:     0,
				Description: "If set, specifies the maximum allowed token lifetime.",
			},
			"allow_instance_migration": &framework.FieldSchema{
				Type:        framework.TypeBool,
				Default:     false,
				Description: "If set, allows migration of the underlying instance where the client resides. This keys off of pendingTime in the metadata document, so essentially, this disables the client nonce check whenever the instance is migrated to a new host and pendingTime is newer than the previously-remembered time. Use with caution.",
			},
			"disallow_reauthentication": &framework.FieldSchema{
				Type:        framework.TypeBool,
				Default:     false,
				Description: "If set, only allows a single token to be granted per instance ID. In order to perform a fresh login, the entry in whitelist for the instance ID needs to be cleared using the 'auth/aws-ec2/identity-whitelist/<instance_id>' endpoint.",
			},
		},
		Callbacks: map[logical.Operation]framework.OperationFunc{
			logical.UpdateOperation: b.pathRoleTagUpdate,
		},
		HelpSynopsis:    pathRoleTagSyn,
		HelpDescription: pathRoleTagDesc,
	}
}
func (b *backend) pathRoleTagUpdate(
	req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
	roleName := strings.ToLower(data.Get("role").(string))
	if roleName == "" {
		return logical.ErrorResponse("missing role"), nil
	}
	roleEntry, err := b.lockedAWSRole(req.Storage, roleName)
	if err != nil {
		return nil, err
	}
	if roleEntry == nil {
		return logical.ErrorResponse(fmt.Sprintf("entry not found for role %s", roleName)), nil
	}
	if roleEntry.RoleTag == "" {
		return logical.ErrorResponse("tag creation is not enabled for this role"), nil
	}
	if roleEntry.HMACKey == "" {
		return nil, fmt.Errorf("failed to find the HMAC key")
	}
	resp := &logical.Response{}
	instanceID := strings.ToLower(data.Get("instance_id").(string))
	var policies []string
	policiesStr, ok := data.GetOk("policies")
	if ok {
		policies = policyutil.ParsePolicies(policiesStr.(string))
	}
	if !strutil.StrListSubset(roleEntry.Policies, policies) {
		resp.AddWarning("Policies on the tag are not a subset of the policies set on the role. Login will not be allowed with this tag unless the role policies are updated.")
	}
	disallowReauthentication := data.Get("disallow_reauthentication").(bool)
	allowInstanceMigration := data.Get("allow_instance_migration").(bool)
	if allowInstanceMigration && !roleEntry.AllowInstanceMigration {
		resp.AddWarning("Role does not allow instance migration. Login will not be allowed with this tag unless the role value is updated.")
	}
	maxTTL := time.Duration(data.Get("max_ttl").(int)) * time.Second
	if maxTTL > b.System().MaxLeaseTTL() {
		resp.AddWarning(fmt.Sprintf("Given max TTL of %d is greater than the mount maximum of %d seconds, and will be capped at login time.", maxTTL/time.Second, b.System().MaxLeaseTTL()/time.Second))
	}
	if roleEntry.MaxTTL != time.Duration(0) && maxTTL > roleEntry.MaxTTL {
		resp.AddWarning(fmt.Sprintf("Given max TTL of %d is greater than the role maximum of %d seconds, and will be capped at login time.", maxTTL/time.Second, roleEntry.MaxTTL/time.Second))
	}
	if maxTTL < time.Duration(0) {
		return logical.ErrorResponse("max_ttl cannot be negative"), nil
	}
	nonce, err := createRoleTagNonce()
	if err != nil {
		return nil, err
	}
	rTagValue, err := createRoleTagValue(&roleTag{
		Version:                  roleTagVersion,
		Role:                     roleName,
		Nonce:                    nonce,
		Policies:                 policies,
		MaxTTL:                   maxTTL,
		InstanceID:               instanceID,
		DisallowReauthentication: disallowReauthentication,
		AllowInstanceMigration:   allowInstanceMigration,
	}, roleEntry)
	if err != nil {
		return nil, err
	}
	resp.Data = map[string]interface{}{
		"tag_key":   roleEntry.RoleTag,
		"tag_value": rTagValue,
	}
	return resp, nil
}
func createRoleTagValue(rTag *roleTag, roleEntry *awsRoleEntry) (string, error) {
	if rTag == nil {
		return "", fmt.Errorf("nil role tag")
	}
	if roleEntry == nil {
		return "", fmt.Errorf("nil role entry")
	}
	rTagPlaintext, err := prepareRoleTagPlaintextValue(rTag)
	if err != nil {
		return "", err
	}
	return appendHMAC(rTagPlaintext, roleEntry)
}