github.com/zmap/zcrypto@v0.0.0-20240512203510-0fef58d9a9db/x509/qc_statements.go (about)

     1  package x509
     2  
     3  import (
     4  	"encoding/json"
     5  	"errors"
     6  
     7  	"github.com/zmap/zcrypto/encoding/asn1"
     8  )
     9  
    10  type QCStatementASN struct {
    11  	StatementID   asn1.ObjectIdentifier
    12  	StatementInfo asn1.RawValue `asn1:"optional"`
    13  }
    14  
    15  func (s *QCStatementASN) MarshalJSON() ([]byte, error) {
    16  	aux := struct {
    17  		ID    string `json:"id,omitempty"`
    18  		Value []byte `json:"value,omitempty"`
    19  	}{
    20  		ID:    s.StatementID.String(),
    21  		Value: s.StatementInfo.Bytes,
    22  	}
    23  	return json.Marshal(&aux)
    24  }
    25  
    26  type QCStatementsASN struct {
    27  	QCStatements []QCStatementASN
    28  }
    29  
    30  // ETSI OIDS from https://www.etsi.org/deliver/etsi_en/319400_319499/31941205/02.02.03_20/en_31941205v020203a.pdf
    31  var (
    32  	oidEtsiQcsQcCompliance      = asn1.ObjectIdentifier{0, 4, 0, 1862, 1, 1}
    33  	oidEtsiQcsQcLimitValue      = asn1.ObjectIdentifier{0, 4, 0, 1862, 1, 2}
    34  	oidEtsiQcsQcRetentionPeriod = asn1.ObjectIdentifier{0, 4, 0, 1862, 1, 3}
    35  	oidEtsiQcsQcSSCD            = asn1.ObjectIdentifier{0, 4, 0, 1862, 1, 4}
    36  	oidEtsiQcsQcEuPDS           = asn1.ObjectIdentifier{0, 4, 0, 1862, 1, 5}
    37  	oidEtsiQcsQcType            = asn1.ObjectIdentifier{0, 4, 0, 1862, 1, 6}
    38  	oidEtsiQcsQcCCLegislation   = asn1.ObjectIdentifier{0, 4, 0, 1862, 1, 7}
    39  	oidEtsiQcsQctEsign          = asn1.ObjectIdentifier{0, 4, 0, 1862, 1, 6, 1}
    40  	oidEtsiQcsQctEseal          = asn1.ObjectIdentifier{0, 4, 0, 1862, 1, 6, 2}
    41  	oidEtsiQcsQctWeb            = asn1.ObjectIdentifier{0, 4, 0, 1862, 1, 6, 3}
    42  )
    43  
    44  type QCStatements struct {
    45  	StatementIDs     []string            `json:"ids,omitempty"`
    46  	ParsedStatements *ParsedQCStatements `json:"parsed,omitempty"`
    47  }
    48  
    49  type ParsedQCStatements struct {
    50  	ETSICompliance  []bool          `json:"etsi_compliance,omitempty"`
    51  	SSCD            []bool          `json:"sscd,omitempty"`
    52  	Types           []QCType        `json:"types,omitempty"`
    53  	Limit           []MonetaryValue `json:"limit,omitempty"`
    54  	PDSLocations    []PDSLocations  `json:"pds_locations,omitempty"`
    55  	RetentionPeriod []int           `json:"retention_period,omitempty"`
    56  	Legislation     []QCLegistation `json:"legislation,omitempty"`
    57  }
    58  
    59  type MonetaryValue struct {
    60  	Currency       string `json:"currency,omitempty"`
    61  	CurrencyNumber int    `json:"currency_number,omitempty"`
    62  	Amount         int    `json:"amount,omitempty"`
    63  	Exponent       int    `json:"exponent,omitempty"`
    64  }
    65  
    66  type monetaryValueASNString struct {
    67  	Currency string `asn1:"printable"`
    68  	Amount   int
    69  	Exponent int
    70  }
    71  
    72  type monetaryValueASNNumber struct {
    73  	Currency int
    74  	Amount   int
    75  	Exponent int
    76  }
    77  
    78  type PDSLocations struct {
    79  	Locations []PDSLocation `json:"locations,omitempty"`
    80  }
    81  
    82  type PDSLocation struct {
    83  	URL      string `json:"url,omitempty" asn1:"ia5"`
    84  	Language string `json:"language,omitempty" asn1:"printable"`
    85  }
    86  
    87  type QCType struct {
    88  	TypeIdentifiers []asn1.ObjectIdentifier
    89  }
    90  
    91  type QCLegistation struct {
    92  	CountryCodes []string `json:"country_codes,omitempty"`
    93  }
    94  
    95  func (qt *QCType) MarshalJSON() ([]byte, error) {
    96  	aux := struct {
    97  		Types []string `json:"ids,omitempty"`
    98  	}{
    99  		Types: make([]string, len(qt.TypeIdentifiers)),
   100  	}
   101  	for idx := range qt.TypeIdentifiers {
   102  		aux.Types[idx] = qt.TypeIdentifiers[idx].String()
   103  	}
   104  	return json.Marshal(&aux)
   105  }
   106  
   107  func (q *QCStatements) Parse(in *QCStatementsASN) error {
   108  	q.StatementIDs = make([]string, len(in.QCStatements))
   109  	known := ParsedQCStatements{}
   110  	for i, s := range in.QCStatements {
   111  		val := in.QCStatements[i].StatementInfo.FullBytes
   112  		q.StatementIDs[i] = s.StatementID.String()
   113  		if s.StatementID.Equal(oidEtsiQcsQcCompliance) {
   114  			known.ETSICompliance = append(known.ETSICompliance, true)
   115  			if val != nil {
   116  				return errors.New("EtsiQcsQcCompliance QCStatement must not contain a statementInfo")
   117  			}
   118  		} else if s.StatementID.Equal(oidEtsiQcsQcLimitValue) {
   119  			// TODO
   120  			mvs := monetaryValueASNString{}
   121  			mvn := monetaryValueASNNumber{}
   122  			out := MonetaryValue{}
   123  			if _, err := asn1.Unmarshal(val, &mvs); err == nil {
   124  				out.Currency = mvs.Currency
   125  				out.Amount = mvs.Amount
   126  				out.Exponent = mvs.Exponent
   127  			} else if _, err := asn1.Unmarshal(val, &mvn); err == nil {
   128  				out.CurrencyNumber = mvn.Currency
   129  				out.Amount = mvn.Amount
   130  				out.Exponent = mvn.Exponent
   131  			} else {
   132  				return err
   133  			}
   134  			known.Limit = append(known.Limit, out)
   135  		} else if s.StatementID.Equal(oidEtsiQcsQcRetentionPeriod) {
   136  			var retentionPeriod int
   137  			if _, err := asn1.Unmarshal(val, &retentionPeriod); err != nil {
   138  				return err
   139  			}
   140  			known.RetentionPeriod = append(known.RetentionPeriod, retentionPeriod)
   141  		} else if s.StatementID.Equal(oidEtsiQcsQcSSCD) {
   142  			known.SSCD = append(known.SSCD, true)
   143  			if val != nil {
   144  				return errors.New("EtsiQcsQcSSCD QCStatement must not contain a statementInfo")
   145  			}
   146  		} else if s.StatementID.Equal(oidEtsiQcsQcEuPDS) {
   147  			locations := make([]PDSLocation, 0)
   148  			if _, err := asn1.Unmarshal(val, &locations); err != nil {
   149  				return err
   150  			}
   151  			known.PDSLocations = append(known.PDSLocations, PDSLocations{
   152  				Locations: locations,
   153  			})
   154  		} else if s.StatementID.Equal(oidEtsiQcsQcType) {
   155  			typeIds := make([]asn1.ObjectIdentifier, 0)
   156  			if _, err := asn1.Unmarshal(val, &typeIds); err != nil {
   157  				return err
   158  			}
   159  			known.Types = append(known.Types, QCType{
   160  				TypeIdentifiers: typeIds,
   161  			})
   162  		} else if s.StatementID.Equal(oidEtsiQcsQcCCLegislation) {
   163  			countryCodes := make([]string, 0)
   164  			if _, err := asn1.Unmarshal(val, &countryCodes); err != nil {
   165  				return err
   166  			}
   167  			known.Legislation = append(known.Legislation, QCLegistation{
   168  				CountryCodes: countryCodes,
   169  			})
   170  		}
   171  	}
   172  	q.ParsedStatements = &known
   173  	return nil
   174  }