github.com/greenpau/go-identity@v1.1.6/public_key_test.go (about)

     1  // Copyright 2020 Paul Greenberg greenpau@outlook.com
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package identity
    16  
    17  import (
    18  	"bufio"
    19  	"bytes"
    20  	"crypto/rand"
    21  	"crypto/rsa"
    22  	"crypto/x509"
    23  	"encoding/pem"
    24  	"fmt"
    25  	"github.com/greenpau/go-identity/internal/tests"
    26  	"github.com/greenpau/go-identity/pkg/errors"
    27  	"github.com/greenpau/go-identity/pkg/requests"
    28  	"golang.org/x/crypto/ssh"
    29  	"os"
    30  	"strings"
    31  	"testing"
    32  )
    33  
    34  func readPEMFile(fp string) string {
    35  	var buffer bytes.Buffer
    36  	fileHandle, err := os.Open(fp)
    37  	if err != nil {
    38  		panic(err)
    39  	}
    40  	defer fileHandle.Close()
    41  	scanner := bufio.NewScanner(fileHandle)
    42  	for scanner.Scan() {
    43  		line := scanner.Text()
    44  		buffer.WriteString(strings.TrimSpace(line) + "\n")
    45  	}
    46  	if err := scanner.Err(); err != nil {
    47  		panic(err)
    48  	}
    49  	return buffer.String()
    50  }
    51  
    52  func getPublicKey(t *testing.T, pk *rsa.PrivateKey, keyType string) string {
    53  	// Derive Public Key
    54  	pubKeyBytes, err := x509.MarshalPKIXPublicKey(pk.Public())
    55  	if err != nil {
    56  		t.Fatalf("failed creating rsa public key: %v", err)
    57  	}
    58  
    59  	// Create PEM encoded string
    60  	pubKeyEncoded := pem.EncodeToMemory(
    61  		&pem.Block{
    62  			Type:  "RSA PUBLIC KEY",
    63  			Bytes: pubKeyBytes,
    64  		},
    65  	)
    66  
    67  	// Create OpenSSH formatted string
    68  	pubKeyOpenSSH, err := ssh.NewPublicKey(pk.Public())
    69  	if err != nil {
    70  		t.Fatalf("failed creating openssh key: %v", err)
    71  	}
    72  	authorizedKeyBytes := ssh.MarshalAuthorizedKey(pubKeyOpenSSH)
    73  	switch keyType {
    74  	case "openssh":
    75  		return string(authorizedKeyBytes)
    76  	}
    77  	return string(pubKeyEncoded)
    78  }
    79  
    80  func TestNewPublicKey(t *testing.T) {
    81  	pk, err := rsa.GenerateKey(rand.Reader, 2048)
    82  	if err != nil {
    83  		t.Fatalf("failed generating private key: %v", err)
    84  	}
    85  	if err := pk.Validate(); err != nil {
    86  		t.Fatalf("failed validating private key: %v", err)
    87  	}
    88  	pkb := x509.MarshalPKCS1PrivateKey(pk)
    89  	pkm := pem.EncodeToMemory(
    90  		&pem.Block{
    91  			Type:  "RSA PRIVATE KEY",
    92  			Bytes: pkb,
    93  		},
    94  	)
    95  	// t.Logf("private rsa key:\n%s", string(pkm))
    96  
    97  	testcases := []struct {
    98  		name      string
    99  		req       *requests.Request
   100  		want      map[string]interface{}
   101  		shouldErr bool
   102  		err       error
   103  	}{
   104  		{
   105  			name: "test ssh rsa key",
   106  			req: &requests.Request{
   107  				Key: requests.Key{
   108  					Usage:   "ssh",
   109  					Comment: "jsmith@outlook.com",
   110  					Payload: "rsa",
   111  				},
   112  			},
   113  			want: map[string]interface{}{
   114  				"usage":   "ssh",
   115  				"type":    "ssh-rsa",
   116  				"comment": "jsmith@outlook.com",
   117  			},
   118  		},
   119  		{
   120  			name: "test openssh key",
   121  			req: &requests.Request{
   122  				Key: requests.Key{
   123  					Usage:   "ssh",
   124  					Comment: "jsmith@outlook.com",
   125  					Payload: "openssh",
   126  				},
   127  			},
   128  			want: map[string]interface{}{
   129  				"usage":   "ssh",
   130  				"type":    "ssh-rsa",
   131  				"comment": "jsmith@outlook.com",
   132  			},
   133  		},
   134  		{
   135  			name: "test unsupported public key usage",
   136  			req: &requests.Request{
   137  				Key: requests.Key{
   138  					Usage:   "foobar",
   139  					Payload: "-----BEGIN RSA PUBLIC KEY-----",
   140  				},
   141  			},
   142  			shouldErr: true,
   143  			err:       errors.ErrPublicKeyInvalidUsage.WithArgs("foobar"),
   144  		},
   145  		{
   146  			name: "test empty public key payload",
   147  			req: &requests.Request{
   148  				Key: requests.Key{
   149  					Usage: "ssh",
   150  				},
   151  			},
   152  			shouldErr: true,
   153  			err:       errors.ErrPublicKeyEmptyPayload,
   154  		},
   155  		{
   156  			name: "test public key payload and usage mismatch",
   157  			req: &requests.Request{
   158  				Key: requests.Key{
   159  					Usage:   "gpg",
   160  					Payload: "-----BEGIN RSA PUBLIC KEY-----",
   161  				},
   162  			},
   163  			shouldErr: true,
   164  			err:       errors.ErrPublicKeyUsagePayloadMismatch.WithArgs("gpg"),
   165  		},
   166  		{
   167  			name: "test public key block type error",
   168  			req: &requests.Request{
   169  				Key: requests.Key{
   170  					Usage:   "ssh",
   171  					Payload: "-----BEGIN RSA PUBLIC KEY-----",
   172  				},
   173  			},
   174  			shouldErr: true,
   175  			err:       errors.ErrPublicKeyBlockType.WithArgs(""),
   176  		},
   177  		{
   178  			name: "test public key unexpected block type",
   179  			req: &requests.Request{
   180  				Key: requests.Key{
   181  					Usage:   "ssh",
   182  					Payload: strings.Replace(string(pkm), "PRIVATE", "PUBLIC", 1),
   183  				},
   184  			},
   185  			shouldErr: true,
   186  			err:       errors.ErrPublicKeyBlockType.WithArgs(""),
   187  		},
   188  		{
   189  			name: "test gpg public key without end block",
   190  			req: &requests.Request{
   191  				Key: requests.Key{
   192  					Usage:   "gpg",
   193  					Comment: "jsmith@outlook.com",
   194  					Payload: "-----BEGIN PGP PUBLIC KEY BLOCK-----",
   195  				},
   196  			},
   197  			shouldErr: true,
   198  			err:       errors.ErrPublicKeyParse.WithArgs("END PGP PUBLIC KEY BLOCK not found"),
   199  		},
   200  		{
   201  			name: "test gpg public key",
   202  			req: &requests.Request{
   203  				Key: requests.Key{
   204  					Usage:   "gpg",
   205  					Payload: readPEMFile("testdata/gpg/linux_gpg_pub.pem"),
   206  				},
   207  			},
   208  			want: map[string]interface{}{
   209  				"usage":   "gpg",
   210  				"type":    "dsa",
   211  				"comment": "Google, Inc. Linux Package Signing Key <linux-packages-keymaster@google.com>, algo DSA, created 2007-03-08 20:17:10 +0000 UTC",
   212  				"id":      "a040830f7fac5991",
   213  			},
   214  		},
   215  	}
   216  
   217  	for _, tc := range testcases {
   218  		t.Run(tc.name, func(t *testing.T) {
   219  			msgs := []string{fmt.Sprintf("test name: %s", tc.name)}
   220  			if tc.req.Key.Usage == "ssh" {
   221  				msgs = append(msgs, fmt.Sprintf("private rsa key:\n%s", string(pkm)))
   222  			} else {
   223  				msgs = append(msgs, fmt.Sprintf("payload:\n%s", string(tc.req.Key.Payload)))
   224  			}
   225  			// t.Logf("public key:\n%s", tc.req.Key.Payload)
   226  
   227  			if tc.req.Key.Payload == "rsa" || tc.req.Key.Payload == "openssh" {
   228  				tc.req.Key.Payload = getPublicKey(t, pk, tc.req.Key.Payload)
   229  			}
   230  
   231  			key, err := NewPublicKey(tc.req)
   232  			if tests.EvalErrWithLog(t, err, "new public key", tc.shouldErr, tc.err, msgs) {
   233  				return
   234  			}
   235  			// t.Logf("%v", key)
   236  
   237  			got := make(map[string]interface{})
   238  			got["type"] = key.Type
   239  			got["usage"] = key.Usage
   240  			got["comment"] = key.Comment
   241  			if key.Usage == "gpg" {
   242  				got["id"] = key.ID
   243  			}
   244  			tests.EvalObjectsWithLog(t, "eval", tc.want, got, msgs)
   245  
   246  			bundle := NewPublicKeyBundle()
   247  			bundle.Add(key)
   248  			bundle.Get()
   249  			key.Disable()
   250  		})
   251  	}
   252  }