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

     1  package x509
     2  
     3  import (
     4  	"github.com/zmap/zcrypto/encoding/asn1"
     5  	"github.com/zmap/zcrypto/x509/pkix"
     6  )
     7  
     8  var (
     9  	// oidBRTorServiceDescriptor is the assigned OID for the CAB Forum Tor Service
    10  	// Descriptor Hash extension (see EV Guidelines Appendix F)
    11  	oidBRTorServiceDescriptor = asn1.ObjectIdentifier{2, 23, 140, 1, 31}
    12  )
    13  
    14  // TorServiceDescriptorHash is a structure corrsponding to the
    15  // TorServiceDescriptorHash SEQUENCE described in Appendix F ("Issuance of
    16  // Certificates for .onion Domain Names").
    17  //
    18  // Each TorServiceDescriptorHash holds an onion URI (a utf8 string with the
    19  // .onion address that was validated), a hash algorithm name (computed based on
    20  // the pkix.AlgorithmIdentifier in the TorServiceDescriptorHash), the hash bytes
    21  // (computed over the DER encoding of the ASN.1 SubjectPublicKey of the .onion
    22  // service), and the number of bits in the hash bytes.
    23  type TorServiceDescriptorHash struct {
    24  	Onion         string                   `json:"onion"`
    25  	Algorithm     pkix.AlgorithmIdentifier `json:"-"`
    26  	AlgorithmName string                   `json:"algorithm_name"`
    27  	Hash          CertificateFingerprint   `json:"hash"`
    28  	HashBits      int                      `json:"hash_bits"`
    29  }
    30  
    31  // parseTorServiceDescriptorSyntax parses the given pkix.Extension (assumed to
    32  // have OID == oidBRTorServiceDescriptor) and returns a slice of parsed
    33  // TorServiceDescriptorHash objects, or an error. An error will be returned if
    34  // there are any structural errors related to the ASN.1 content (wrong tags,
    35  // trailing data, missing fields, etc).
    36  func parseTorServiceDescriptorSyntax(ext pkix.Extension) ([]*TorServiceDescriptorHash, error) {
    37  	// TorServiceDescriptorSyntax ::=
    38  	//    SEQUENCE ( 1..MAX ) of TorServiceDescriptorHash
    39  	var seq asn1.RawValue
    40  	rest, err := asn1.Unmarshal(ext.Value, &seq)
    41  	if err != nil {
    42  		return nil, asn1.SyntaxError{
    43  			Msg: "unable to unmarshal outer TorServiceDescriptor SEQUENCE",
    44  		}
    45  	}
    46  	if len(rest) != 0 {
    47  		return nil, asn1.SyntaxError{
    48  			Msg: "trailing data after outer TorServiceDescriptor SEQUENCE",
    49  		}
    50  	}
    51  	if seq.Tag != asn1.TagSequence || seq.Class != asn1.ClassUniversal || !seq.IsCompound {
    52  		return nil, asn1.SyntaxError{
    53  			Msg: "invalid outer TorServiceDescriptor SEQUENCE",
    54  		}
    55  	}
    56  
    57  	var descriptors []*TorServiceDescriptorHash
    58  	rest = seq.Bytes
    59  	for len(rest) > 0 {
    60  		var descriptor *TorServiceDescriptorHash
    61  		descriptor, rest, err = parseTorServiceDescriptorHash(rest)
    62  		if err != nil {
    63  			return nil, err
    64  		}
    65  		descriptors = append(descriptors, descriptor)
    66  	}
    67  	return descriptors, nil
    68  }
    69  
    70  // parseTorServiceDescriptorHash unmarshals a SEQUENCE from the provided data
    71  // and parses a TorServiceDescriptorHash using the data contained in the
    72  // sequence. The TorServiceDescriptorHash object and the remaining data are
    73  // returned if no error occurs.
    74  func parseTorServiceDescriptorHash(data []byte) (*TorServiceDescriptorHash, []byte, error) {
    75  	// TorServiceDescriptorHash:: = SEQUENCE {
    76  	//   onionURI UTF8String
    77  	//   algorithm AlgorithmIdentifier
    78  	//   subjectPublicKeyHash BIT STRING
    79  	// }
    80  	var outerSeq asn1.RawValue
    81  	var err error
    82  	data, err = asn1.Unmarshal(data, &outerSeq)
    83  	if err != nil {
    84  		return nil, data, asn1.SyntaxError{
    85  			Msg: "error unmarshaling TorServiceDescriptorHash SEQUENCE",
    86  		}
    87  	}
    88  	if outerSeq.Tag != asn1.TagSequence ||
    89  		outerSeq.Class != asn1.ClassUniversal ||
    90  		!outerSeq.IsCompound {
    91  		return nil, data, asn1.SyntaxError{
    92  			Msg: "TorServiceDescriptorHash missing compound SEQUENCE tag",
    93  		}
    94  	}
    95  	fieldData := outerSeq.Bytes
    96  
    97  	// Unmarshal and verify the structure of the onionURI UTF8String field.
    98  	var rawOnionURI asn1.RawValue
    99  	fieldData, err = asn1.Unmarshal(fieldData, &rawOnionURI)
   100  	if err != nil {
   101  		return nil, data, asn1.SyntaxError{
   102  			Msg: "error unmarshaling TorServiceDescriptorHash onionURI",
   103  		}
   104  	}
   105  	if rawOnionURI.Tag != asn1.TagUTF8String ||
   106  		rawOnionURI.Class != asn1.ClassUniversal ||
   107  		rawOnionURI.IsCompound {
   108  		return nil, data, asn1.SyntaxError{
   109  			Msg: "TorServiceDescriptorHash missing non-compound UTF8String tag",
   110  		}
   111  	}
   112  
   113  	// Unmarshal and verify the structure of the algorithm UTF8String field.
   114  	var algorithm pkix.AlgorithmIdentifier
   115  	fieldData, err = asn1.Unmarshal(fieldData, &algorithm)
   116  	if err != nil {
   117  		return nil, nil, asn1.SyntaxError{
   118  			Msg: "error unmarshaling TorServiceDescriptorHash algorithm",
   119  		}
   120  	}
   121  
   122  	var algorithmName string
   123  	if algorithm.Algorithm.Equal(oidSHA256) {
   124  		algorithmName = "SHA256"
   125  	} else if algorithm.Algorithm.Equal(oidSHA384) {
   126  		algorithmName = "SHA384"
   127  	} else if algorithm.Algorithm.Equal(oidSHA512) {
   128  		algorithmName = "SHA512"
   129  	} else {
   130  		algorithmName = "Unknown"
   131  	}
   132  
   133  	// Unmarshal and verify the structure of the Subject Public Key Hash BitString
   134  	// field.
   135  	var spkh asn1.BitString
   136  	fieldData, err = asn1.Unmarshal(fieldData, &spkh)
   137  	if err != nil {
   138  		return nil, data, asn1.SyntaxError{
   139  			Msg: "error unmarshaling TorServiceDescriptorHash Hash",
   140  		}
   141  	}
   142  
   143  	// There should be no trailing data after the TorServiceDescriptorHash
   144  	// SEQUENCE.
   145  	if len(fieldData) > 0 {
   146  		return nil, data, asn1.SyntaxError{
   147  			Msg: "trailing data after TorServiceDescriptorHash",
   148  		}
   149  	}
   150  
   151  	return &TorServiceDescriptorHash{
   152  		Onion:         string(rawOnionURI.Bytes),
   153  		Algorithm:     algorithm,
   154  		AlgorithmName: algorithmName,
   155  		HashBits:      spkh.BitLength,
   156  		Hash:          CertificateFingerprint(spkh.Bytes),
   157  	}, data, nil
   158  }