go.mondoo.com/cnquery@v0.0.0-20231005093811-59568235f6ea/providers/network/resources/dnsshake/dkim_test.go (about)

     1  // Copyright (c) Mondoo, Inc.
     2  // SPDX-License-Identifier: BUSL-1.1
     3  
     4  package dnsshake
     5  
     6  import (
     7  	"errors"
     8  	"testing"
     9  
    10  	"github.com/stretchr/testify/assert"
    11  )
    12  
    13  const (
    14  	pubKey = `MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAx3E9IavfvGHiENM/bFBTJfRLBUE1PV9f2q2mbYOHu2d1zZ3VB22sXnpGN6TV1m8Tq8zUWlXPgkApOaSF/+zRqBuyF6ci1rmcfvFCAHdERXy37bFgi0/EkoslaqEZel4eddqqWt93KuwydPL2jEhd01M+PGbfFfCu65iZFW107u0PhlXWZG0iJbFsBNdp4mKXI4CxWNlVb0xPr0kcYaE0eAi+EcnG5QHONv5cQrQJ6ncUNehV0caUKWibIKTKPmwttPTyTYbF6sWY7olT9FAgbGz5flHHqBVWPXsf5Jivv5HbsJLTdejAvQwm7e+w0S//OFafffZUXgF/yNB4HczZiQIDAQAB`
    15  )
    16  
    17  func TestDkimPublicKeyRepresentation(t *testing.T) {
    18  	type test struct {
    19  		Title        string
    20  		DnsTxtRecord string
    21  		Expected     *DkimPublicKeyRepresentation
    22  		ParseErr     error
    23  		IsValid      bool
    24  	}
    25  
    26  	// test cases for https://datatracker.ietf.org/doc/html/rfc6376#section-3.6.1
    27  	testCases := []test{
    28  		{
    29  			Title:        "minimal valid dkim",
    30  			DnsTxtRecord: "p=" + pubKey,
    31  			Expected: &DkimPublicKeyRepresentation{
    32  				PublicKeyData: pubKey,
    33  			},
    34  			IsValid: true,
    35  		},
    36  		{
    37  			Title:        "empty dkim record",
    38  			DnsTxtRecord: "",
    39  			Expected:     &DkimPublicKeyRepresentation{},
    40  			IsValid:      false,
    41  		},
    42  		//  v= Version of the DKIM key record (plain-text; RECOMMENDED, default is "DKIM1")
    43  		{
    44  			Title:        "v tag MUST be the first tag",
    45  			DnsTxtRecord: "p=" + pubKey + "; v=DKIM1",
    46  			Expected:     nil,
    47  			ParseErr:     errors.New("invalid DKIM record"),
    48  			IsValid:      false,
    49  		},
    50  		{
    51  			Title:        "invalid DKIM version",
    52  			DnsTxtRecord: "v=DKIM1.0; p=" + pubKey,
    53  			Expected: &DkimPublicKeyRepresentation{
    54  				Version:       "DKIM1.0",
    55  				PublicKeyData: pubKey,
    56  			},
    57  			IsValid: false,
    58  		},
    59  		// h= Acceptable hash algorithms (plain-text; OPTIONAL, defaults to allowing all algorithms)
    60  		{
    61  			Title:        "valid hash algorithms",
    62  			DnsTxtRecord: "v=DKIM1; h=sha1:sha256; p=" + pubKey,
    63  			Expected: &DkimPublicKeyRepresentation{
    64  				Version:        "DKIM1",
    65  				HashAlgorithms: []string{"sha1", "sha256"},
    66  				PublicKeyData:  pubKey,
    67  			},
    68  			IsValid: true,
    69  		},
    70  		{
    71  			Title:        "single hash algorithm",
    72  			DnsTxtRecord: "v=DKIM1; h=sha256; p=" + pubKey,
    73  			Expected: &DkimPublicKeyRepresentation{
    74  				Version:        "DKIM1",
    75  				HashAlgorithms: []string{"sha256"},
    76  				PublicKeyData:  pubKey,
    77  			},
    78  			IsValid: true,
    79  		},
    80  		{
    81  			Title:        "unsupported hash algorithm",
    82  			DnsTxtRecord: "v=DKIM1; h=sha512; p=" + pubKey,
    83  			Expected: &DkimPublicKeyRepresentation{
    84  				Version:        "DKIM1",
    85  				HashAlgorithms: []string{"sha512"},
    86  				PublicKeyData:  pubKey,
    87  			},
    88  			IsValid: true, // still valid according to RFC: Unrecognized algorithms MUST be ignored
    89  		},
    90  		{
    91  			Title:        "empty hash list",
    92  			DnsTxtRecord: "v=DKIM1; h=; p=" + pubKey,
    93  			Expected: &DkimPublicKeyRepresentation{
    94  				Version:        "DKIM1",
    95  				HashAlgorithms: []string{""},
    96  				PublicKeyData:  pubKey,
    97  			},
    98  			IsValid: true, // still valid according to RFC: defaults to allowing all algorithms
    99  		},
   100  		// k= Key type (plain-text; OPTIONAL, default is "rsa")
   101  		{
   102  			Title:        "valid DKIM with rsa key type and public key",
   103  			DnsTxtRecord: "v=DKIM1; k=rsa; p=" + pubKey,
   104  			Expected: &DkimPublicKeyRepresentation{
   105  				Version:       "DKIM1",
   106  				KeyType:       "rsa",
   107  				PublicKeyData: pubKey,
   108  			},
   109  			IsValid: true,
   110  		},
   111  		{
   112  			Title:        "unsupported public key type",
   113  			DnsTxtRecord: "v=DKIM1; k=dsa; p=" + pubKey,
   114  			Expected: &DkimPublicKeyRepresentation{
   115  				Version:       "DKIM1",
   116  				KeyType:       "dsa",
   117  				PublicKeyData: pubKey,
   118  			},
   119  			IsValid: false,
   120  		},
   121  		{
   122  			Title:        "empty_key_type",
   123  			DnsTxtRecord: "v=DKIM1; k=; p=" + pubKey,
   124  			Expected: &DkimPublicKeyRepresentation{
   125  				Version:       "DKIM1",
   126  				PublicKeyData: pubKey,
   127  			},
   128  			IsValid: true, // valid since it defaults to rsa
   129  		},
   130  		// n= Notes that might be of interest to a human
   131  		{
   132  			Title:        "empty note",
   133  			DnsTxtRecord: "v=DKIM1; n=; p=" + pubKey,
   134  			Expected: &DkimPublicKeyRepresentation{
   135  				Version:       "DKIM1",
   136  				PublicKeyData: pubKey,
   137  			},
   138  			IsValid: true,
   139  		},
   140  		{
   141  			Title:        "with simple note",
   142  			DnsTxtRecord: "v=DKIM1; n=a note; p=" + pubKey,
   143  			Expected: &DkimPublicKeyRepresentation{
   144  				Version:       "DKIM1",
   145  				Notes:         "a note",
   146  				PublicKeyData: pubKey,
   147  			},
   148  			IsValid: true,
   149  		},
   150  		{
   151  			// see https://en.wikipedia.org/wiki/Quoted-printable
   152  			Title:        "quoted printable note",
   153  			DnsTxtRecord: "v=DKIM1; n=H=E4tten H=FCte ein =DF im Namen, w=E4ren sie m=F6glicherweise keine H=FCte= mehr,sondern H=FC=DFe.; p=" + pubKey,
   154  			Expected: &DkimPublicKeyRepresentation{
   155  				Version:       "DKIM1",
   156  				Notes:         "H\xe4tten H\xfcte ein \xdf im Namen, w\xe4ren sie m\xf6glicherweise keine H\xfcte= mehr,sondern H\xfc\xdfe.",
   157  				PublicKeyData: pubKey,
   158  			},
   159  			IsValid: true,
   160  		},
   161  		{
   162  			Title:        "uninterpreted note",
   163  			DnsTxtRecord: "v=DKIM1; n=Hätten; p=" + pubKey,
   164  			Expected: &DkimPublicKeyRepresentation{
   165  				Version:       "DKIM1",
   166  				Notes:         "Hätten",
   167  				PublicKeyData: pubKey,
   168  			},
   169  			IsValid: true,
   170  		},
   171  		// p= Public-key data (base64; REQUIRED)
   172  		{
   173  			Title:        "missing public key",
   174  			DnsTxtRecord: "v=DKIM1",
   175  			Expected: &DkimPublicKeyRepresentation{
   176  				Version: "DKIM1",
   177  			},
   178  			IsValid: false,
   179  		},
   180  		{
   181  			Title:        "revoked public key",
   182  			DnsTxtRecord: "v=DKIM1; p=",
   183  			Expected: &DkimPublicKeyRepresentation{
   184  				Version: "DKIM1",
   185  			},
   186  			IsValid: false,
   187  		},
   188  		{
   189  			Title:        "invalid base64 public key",
   190  			DnsTxtRecord: "v=DKIM1; p=invalidBase64key",
   191  			Expected: &DkimPublicKeyRepresentation{
   192  				Version:       "DKIM1",
   193  				PublicKeyData: "invalidBase64key",
   194  			},
   195  			IsValid: false,
   196  		},
   197  		// s= Service Type (plain-text; OPTIONAL; default is "*")
   198  		{
   199  			Title:        "matches all service types",
   200  			DnsTxtRecord: "v=DKIM1; s=*; p=" + pubKey,
   201  			Expected: &DkimPublicKeyRepresentation{
   202  				Version:       "DKIM1",
   203  				ServiceType:   []string{"*"},
   204  				PublicKeyData: pubKey,
   205  			},
   206  			IsValid: true,
   207  		},
   208  		{
   209  			Title:        "email service type",
   210  			DnsTxtRecord: "v=DKIM1; s=email; p=" + pubKey,
   211  			Expected: &DkimPublicKeyRepresentation{
   212  				Version:       "DKIM1",
   213  				ServiceType:   []string{"email"},
   214  				PublicKeyData: pubKey,
   215  			},
   216  			IsValid: true,
   217  		},
   218  		{
   219  			Title:        "unsupported service type",
   220  			DnsTxtRecord: "v=DKIM1; s=unsupported; p=" + pubKey,
   221  			Expected: &DkimPublicKeyRepresentation{
   222  				Version:       "DKIM1",
   223  				ServiceType:   []string{"unsupported"},
   224  				PublicKeyData: pubKey,
   225  			},
   226  			IsValid: true,
   227  		},
   228  		{
   229  			Title:        "colon separated service type list with supported and unsupported entries",
   230  			DnsTxtRecord: "v=DKIM1; s=email:unsupported; p=" + pubKey,
   231  			Expected: &DkimPublicKeyRepresentation{
   232  				Version:       "DKIM1",
   233  				ServiceType:   []string{"email", "unsupported"},
   234  				PublicKeyData: pubKey,
   235  			},
   236  			IsValid: true,
   237  		},
   238  		{
   239  			Title:        "empty services types",
   240  			DnsTxtRecord: "v=DKIM1; s=; p=" + pubKey,
   241  			Expected: &DkimPublicKeyRepresentation{
   242  				Version:       "DKIM1",
   243  				ServiceType:   []string{""},
   244  				PublicKeyData: pubKey,
   245  			},
   246  			IsValid: true,
   247  		},
   248  
   249  		// t= Flags, represented as a colon-separated list of name
   250  		{
   251  			Title:        "testing mode flag",
   252  			DnsTxtRecord: "v=DKIM1; t=y; p=" + pubKey,
   253  			Expected: &DkimPublicKeyRepresentation{
   254  				Version:       "DKIM1",
   255  				PublicKeyData: pubKey,
   256  				Flags:         []string{"y"},
   257  			},
   258  			IsValid: true,
   259  		},
   260  		{
   261  			Title:        "include invalid test flag",
   262  			DnsTxtRecord: "v=DKIM1; t=y:s:?; p=" + pubKey,
   263  			Expected: &DkimPublicKeyRepresentation{
   264  				Version:        "DKIM1",
   265  				HashAlgorithms: nil,
   266  				KeyType:        "",
   267  				ServiceType:    nil,
   268  				PublicKeyData:  pubKey,
   269  				Flags:          []string{"y", "s", "?"},
   270  			},
   271  			IsValid: true,
   272  		},
   273  		{
   274  			Title:        "no flags",
   275  			DnsTxtRecord: "v=DKIM1; t=; p=" + pubKey,
   276  			Expected: &DkimPublicKeyRepresentation{
   277  				Version:       "DKIM1",
   278  				PublicKeyData: pubKey,
   279  				Flags:         []string{""},
   280  			},
   281  			IsValid: true,
   282  		},
   283  	}
   284  
   285  	for i := range testCases {
   286  		tc := testCases[i]
   287  		t.Run(tc.Title, func(t *testing.T) {
   288  			// parse dns entry
   289  			pubKeyRep, err := NewDkimPublicKeyRepresentation(tc.DnsTxtRecord)
   290  			if tc.ParseErr != nil {
   291  				assert.EqualError(t, err, tc.ParseErr.Error())
   292  			} else {
   293  				assert.NoError(t, err)
   294  			}
   295  
   296  			// check that the data was parsed as expected
   297  			assert.EqualValues(t, tc.Expected, pubKeyRep)
   298  
   299  			// check if validation is successful
   300  			if pubKeyRep != nil {
   301  				valid, _, _ := pubKeyRep.Valid()
   302  				assert.Equal(t, tc.IsValid, valid)
   303  			}
   304  		})
   305  	}
   306  }