github.com/hyperledger-gerrit-archive/fabric-ca@v2.0.0-alpha.0.20190916143245-4cd4192f0366+incompatible/lib/attrmgr/attrmgr.go (about)

     1  /*
     2  Copyright IBM Corp. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  /*
     8   * The attrmgr package contains utilities for managing attributes.
     9   * Attributes are added to an X509 certificate as an extension.
    10   */
    11  
    12  package attrmgr
    13  
    14  import (
    15  	"crypto/x509"
    16  	"crypto/x509/pkix"
    17  	"encoding/asn1"
    18  	"encoding/json"
    19  	"fmt"
    20  
    21  	"github.com/pkg/errors"
    22  )
    23  
    24  var (
    25  	// AttrOID is the ASN.1 object identifier for an attribute extension in an
    26  	// X509 certificate
    27  	AttrOID = asn1.ObjectIdentifier{1, 2, 3, 4, 5, 6, 7, 8, 1}
    28  	// AttrOIDString is the string version of AttrOID
    29  	AttrOIDString = "1.2.3.4.5.6.7.8.1"
    30  )
    31  
    32  // Attribute is a name/value pair
    33  type Attribute interface {
    34  	// GetName returns the name of the attribute
    35  	GetName() string
    36  	// GetValue returns the value of the attribute
    37  	GetValue() string
    38  }
    39  
    40  // AttributeRequest is a request for an attribute
    41  type AttributeRequest interface {
    42  	// GetName returns the name of an attribute
    43  	GetName() string
    44  	// IsRequired returns true if the attribute is required
    45  	IsRequired() bool
    46  }
    47  
    48  // New constructs an attribute manager
    49  func New() *Mgr { return &Mgr{} }
    50  
    51  // Mgr is the attribute manager and is the main object for this package
    52  type Mgr struct{}
    53  
    54  // ProcessAttributeRequestsForCert add attributes to an X509 certificate, given
    55  // attribute requests and attributes.
    56  func (mgr *Mgr) ProcessAttributeRequestsForCert(requests []AttributeRequest, attributes []Attribute, cert *x509.Certificate) error {
    57  	attrs, err := mgr.ProcessAttributeRequests(requests, attributes)
    58  	if err != nil {
    59  		return err
    60  	}
    61  	return mgr.AddAttributesToCert(attrs, cert)
    62  }
    63  
    64  // ProcessAttributeRequests takes an array of attribute requests and an identity's attributes
    65  // and returns an Attributes object containing the requested attributes.
    66  func (mgr *Mgr) ProcessAttributeRequests(requests []AttributeRequest, attributes []Attribute) (*Attributes, error) {
    67  	attrsMap := map[string]string{}
    68  	attrs := &Attributes{Attrs: attrsMap}
    69  	missingRequiredAttrs := []string{}
    70  	// For each of the attribute requests
    71  	for _, req := range requests {
    72  		// Get the attribute
    73  		name := req.GetName()
    74  		attr := getAttrByName(name, attributes)
    75  		if attr == nil {
    76  			if req.IsRequired() {
    77  				// Didn't find attribute and it was required; return error below
    78  				missingRequiredAttrs = append(missingRequiredAttrs, name)
    79  			}
    80  			// Skip attribute requests which aren't required
    81  			continue
    82  		}
    83  		attrsMap[name] = attr.GetValue()
    84  	}
    85  	if len(missingRequiredAttrs) > 0 {
    86  		return nil, errors.Errorf("The following required attributes are missing: %+v",
    87  			missingRequiredAttrs)
    88  	}
    89  	return attrs, nil
    90  }
    91  
    92  // AddAttributesToCert adds public attribute info to an X509 certificate.
    93  func (mgr *Mgr) AddAttributesToCert(attrs *Attributes, cert *x509.Certificate) error {
    94  	buf, err := json.Marshal(attrs)
    95  	if err != nil {
    96  		return errors.Wrap(err, "Failed to marshal attributes")
    97  	}
    98  	ext := pkix.Extension{
    99  		Id:       AttrOID,
   100  		Critical: false,
   101  		Value:    buf,
   102  	}
   103  	cert.Extensions = append(cert.Extensions, ext)
   104  	return nil
   105  }
   106  
   107  // GetAttributesFromCert gets the attributes from a certificate.
   108  func (mgr *Mgr) GetAttributesFromCert(cert *x509.Certificate) (*Attributes, error) {
   109  	// Get certificate attributes from the certificate if it exists
   110  	buf, err := getAttributesFromCert(cert)
   111  	if err != nil {
   112  		return nil, err
   113  	}
   114  	// Unmarshal into attributes object
   115  	attrs := &Attributes{}
   116  	if buf != nil {
   117  		err := json.Unmarshal(buf, attrs)
   118  		if err != nil {
   119  			return nil, errors.Wrap(err, "Failed to unmarshal attributes from certificate")
   120  		}
   121  	}
   122  	return attrs, nil
   123  }
   124  
   125  // Attributes contains attribute names and values
   126  type Attributes struct {
   127  	Attrs map[string]string `json:"attrs"`
   128  }
   129  
   130  // Names returns the names of the attributes
   131  func (a *Attributes) Names() []string {
   132  	i := 0
   133  	names := make([]string, len(a.Attrs))
   134  	for name := range a.Attrs {
   135  		names[i] = name
   136  		i++
   137  	}
   138  	return names
   139  }
   140  
   141  // Contains returns true if the named attribute is found
   142  func (a *Attributes) Contains(name string) bool {
   143  	_, ok := a.Attrs[name]
   144  	return ok
   145  }
   146  
   147  // Value returns an attribute's value
   148  func (a *Attributes) Value(name string) (string, bool, error) {
   149  	attr, ok := a.Attrs[name]
   150  	return attr, ok, nil
   151  }
   152  
   153  // True returns nil if the value of attribute 'name' is true;
   154  // otherwise, an appropriate error is returned.
   155  func (a *Attributes) True(name string) error {
   156  	val, ok, err := a.Value(name)
   157  	if err != nil {
   158  		return err
   159  	}
   160  	if !ok {
   161  		return fmt.Errorf("Attribute '%s' was not found", name)
   162  	}
   163  	if val != "true" {
   164  		return fmt.Errorf("Attribute '%s' is not true", name)
   165  	}
   166  	return nil
   167  }
   168  
   169  // Get the attribute info from a certificate extension, or return nil if not found
   170  func getAttributesFromCert(cert *x509.Certificate) ([]byte, error) {
   171  	for _, ext := range cert.Extensions {
   172  		if isAttrOID(ext.Id) {
   173  			return ext.Value, nil
   174  		}
   175  	}
   176  	return nil, nil
   177  }
   178  
   179  // Is the object ID equal to the attribute info object ID?
   180  func isAttrOID(oid asn1.ObjectIdentifier) bool {
   181  	if len(oid) != len(AttrOID) {
   182  		return false
   183  	}
   184  	for idx, val := range oid {
   185  		if val != AttrOID[idx] {
   186  			return false
   187  		}
   188  	}
   189  	return true
   190  }
   191  
   192  // Get an attribute from 'attrs' by its name, or nil if not found
   193  func getAttrByName(name string, attrs []Attribute) Attribute {
   194  	for _, attr := range attrs {
   195  		if attr.GetName() == name {
   196  			return attr
   197  		}
   198  	}
   199  	return nil
   200  }