Skip to content

Commit

Permalink
Add basic extension support
Browse files Browse the repository at this point in the history
  • Loading branch information
hdecarne committed Jan 2, 2024
1 parent f07f3b5 commit 94e8052
Show file tree
Hide file tree
Showing 2 changed files with 214 additions and 0 deletions.
169 changes: 169 additions & 0 deletions certs/extensions.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
// Copyright (C) 2023-2024 Holger de Carne and contributors
//
// This software may be modified and distributed under the terms
// of the MIT license. See the LICENSE file for details.

package certs

import (
"crypto/x509"
"encoding/asn1"
"encoding/hex"
"fmt"
"slices"
"strconv"
"strings"
)

const KeyUsageExtensionName = "KeyUsage"
const KeyUsageExtensionOID = "2.5.29.15"

var keyUsages = []x509.KeyUsage{
x509.KeyUsageDigitalSignature,
x509.KeyUsageContentCommitment,
x509.KeyUsageKeyEncipherment,
x509.KeyUsageDataEncipherment,
x509.KeyUsageKeyAgreement,
x509.KeyUsageCertSign,
x509.KeyUsageCRLSign,
x509.KeyUsageEncipherOnly,
x509.KeyUsageDecipherOnly,
}

var keyUsageStrings = map[x509.KeyUsage]string{
x509.KeyUsageDigitalSignature: "digitalSignature",
x509.KeyUsageContentCommitment: "contentCommitment",
x509.KeyUsageKeyEncipherment: "keyEncipherment",
x509.KeyUsageDataEncipherment: "dataEncipherment",
x509.KeyUsageKeyAgreement: "keyAgreement",
x509.KeyUsageCertSign: "keyCertSign",
x509.KeyUsageCRLSign: "cRLSign",
x509.KeyUsageEncipherOnly: "encipherOnly",
x509.KeyUsageDecipherOnly: "decipherOnly",
}

func KeyUsageString(keyUsage x509.KeyUsage) string {
if keyUsage == 0 {
return "-"
}
var keyUsageFlags x509.KeyUsage
var builder strings.Builder
for _, keyUsageFlag := range keyUsages {
keyUsageFlags |= keyUsageFlag
if (keyUsage & keyUsageFlag) == keyUsageFlag {
if builder.Len() > 0 {
builder.WriteString(", ")
}
builder.WriteString(keyUsageStrings[keyUsageFlag])
}
}
unknownKeyUsage := keyUsage ^ keyUsageFlags
if unknownKeyUsage != 0 {
if builder.Len() > 0 {
builder.WriteString(", ")
}
builder.WriteString("0x" + strconv.FormatUint(uint64(unknownKeyUsage), 16))
}
return builder.String()
}

const ExtKeyUsageExtensionName = "ExtKeyUsage"
const ExtKeyUsageExtensionOID = "2.5.29.37"

var extKeyUsages = []x509.ExtKeyUsage{
x509.ExtKeyUsageAny,
x509.ExtKeyUsageServerAuth,
x509.ExtKeyUsageClientAuth,
x509.ExtKeyUsageCodeSigning,
x509.ExtKeyUsageEmailProtection,
x509.ExtKeyUsageIPSECEndSystem,
x509.ExtKeyUsageIPSECTunnel,
x509.ExtKeyUsageIPSECUser,
x509.ExtKeyUsageTimeStamping,
x509.ExtKeyUsageOCSPSigning,
x509.ExtKeyUsageMicrosoftServerGatedCrypto,
x509.ExtKeyUsageNetscapeServerGatedCrypto,
x509.ExtKeyUsageMicrosoftCommercialCodeSigning,
x509.ExtKeyUsageMicrosoftKernelCodeSigning,
}

var extKeyUsageStrings = map[x509.ExtKeyUsage]string{
x509.ExtKeyUsageAny: "any",
x509.ExtKeyUsageServerAuth: "serverAuth",
x509.ExtKeyUsageClientAuth: "clientAuth",
x509.ExtKeyUsageCodeSigning: "codeSigning",
x509.ExtKeyUsageEmailProtection: "emailProtection",
x509.ExtKeyUsageIPSECEndSystem: "ipsecEndSystem",
x509.ExtKeyUsageIPSECTunnel: "ipsecTunnel",
x509.ExtKeyUsageIPSECUser: "ipsecUser",
x509.ExtKeyUsageTimeStamping: "timeStamping",
x509.ExtKeyUsageOCSPSigning: "ocspSigning",
x509.ExtKeyUsageMicrosoftServerGatedCrypto: "microsoftServerGatedCrypto",
x509.ExtKeyUsageNetscapeServerGatedCrypto: "netscapeServerGatedCrypto",
x509.ExtKeyUsageMicrosoftCommercialCodeSigning: "microsoftCommercialCodeSigning",
x509.ExtKeyUsageMicrosoftKernelCodeSigning: "microsoftKernelCodeSigning",
}

func ExtKeyUsageString(extKeyUsage []x509.ExtKeyUsage, unknownExtKeyUsage []asn1.ObjectIdentifier) string {
if len(extKeyUsage) == 0 && len(unknownExtKeyUsage) == 0 {
return "-"
}
var builder strings.Builder
for _, extKeyUsageId := range extKeyUsages {
index := slices.Index(extKeyUsage, extKeyUsageId)
if index < 0 {
continue
}
if builder.Len() > 0 {
builder.WriteString(", ")
}
builder.WriteString(extKeyUsageStrings[extKeyUsageId])
}
for _, unknownExtKeyUsageId := range unknownExtKeyUsage {
if builder.Len() > 0 {
builder.WriteString(", ")
}
builder.WriteString(unknownExtKeyUsageId.String())
}
return builder.String()
}

const BasicConstraintsExtensionName = "BasicConstraints"
const BasicConstraintsExtensionOID = "2.5.29.19"

func BasicConstraintsString(isCA bool, maxPathLen int, maxPathLenZero bool) string {
if !isCA {
return "CA = false"
}
if maxPathLen < 0 || (maxPathLen == 0 && !maxPathLenZero) {
return "CA = true"
}
return fmt.Sprintf("CA = true, pathLenConstraint = %d", maxPathLen)
}

const SubjectKeyIdentifierExtensionName = "SubjectKeyIdentifier"
const SubjectKeyIdentifierExtensionOID = "2.5.29.14"

const AuthorityKeyIdentifierExtensionName = "AuthorityKeyIdentifier"
const AuthorityKeyIdentifierExtensionOID = "2.5.29.35"

const stringLimit = 32

func KeyIdentifierString(keyId []byte) string {
if len(keyId) == 0 {
return ""
}
var builder strings.Builder
encoder := hex.NewEncoder(&builder)
for i := range keyId {
if i >= stringLimit {
builder.WriteString(":...")
break
}
if builder.Len() > 0 {
builder.WriteString(":")
}
encoder.Write(keyId[i : i+1])
}
return builder.String()
}
45 changes: 45 additions & 0 deletions certs/extensions_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// Copyright (C) 2023-2024 Holger de Carne and contributors
//
// This software may be modified and distributed under the terms
// of the MIT license. See the LICENSE file for details.

package certs_test

import (
"crypto/x509"
"encoding/asn1"
"testing"

"github.com/hdecarne-github/go-certstore/certs"
"github.com/stretchr/testify/require"
)

func TestKeyUsageString(t *testing.T) {
require.Equal(t, "-", certs.KeyUsageString(0))
require.Equal(t, "digitalSignature, contentCommitment, keyEncipherment, dataEncipherment, keyAgreement, keyCertSign, cRLSign, encipherOnly, decipherOnly, 0xfe00", certs.KeyUsageString(0xffff))
}

func TestExtKeyUsageString(t *testing.T) {
require.Equal(t, "-", certs.ExtKeyUsageString([]x509.ExtKeyUsage{}, []asn1.ObjectIdentifier{}))
require.Equal(t, "any, 1.2.3.4", certs.ExtKeyUsageString([]x509.ExtKeyUsage{x509.ExtKeyUsageAny}, []asn1.ObjectIdentifier{asn1.ObjectIdentifier([]int{1, 2, 3, 4})}))
}

const basicConstraintsNoCA = "CA = false"
const basicConstratinsCAWithoutPathLenConstraint = "CA = true"
const basicConstratinsCAWithPathLenConstraint = "CA = true, pathLenConstraint = 2"

func TestBasicConstraintsString(t *testing.T) {
require.Equal(t, basicConstraintsNoCA, certs.BasicConstraintsString(false, 0, false))
require.Equal(t, basicConstratinsCAWithoutPathLenConstraint, certs.BasicConstraintsString(true, -1, true))
require.Equal(t, basicConstratinsCAWithoutPathLenConstraint, certs.BasicConstraintsString(true, -1, false))
require.Equal(t, basicConstratinsCAWithoutPathLenConstraint, certs.BasicConstraintsString(true, 0, false))
require.Equal(t, basicConstratinsCAWithPathLenConstraint, certs.BasicConstraintsString(true, 2, false))
require.Equal(t, basicConstratinsCAWithPathLenConstraint, certs.BasicConstraintsString(true, 2, true))
}

func TestKeyIdentiferString(t *testing.T) {
keyId1 := []byte{0x88, 0x1b, 0xd6, 0x08, 0x08, 0xe2, 0xef, 0x84, 0x74, 0xc7, 0x1c, 0x2c, 0x87, 0xd1, 0xd6, 0x87, 0x6b, 0x7b, 0x94, 0x59}
require.Equal(t, "88:1b:d6:08:08:e2:ef:84:74:c7:1c:2c:87:d1:d6:87:6b:7b:94:59", certs.KeyIdentifierString(keyId1))
keyId2 := []byte{0x88, 0x1b, 0xd6, 0x08, 0x08, 0xe2, 0xef, 0x84, 0x74, 0xc7, 0x1c, 0x2c, 0x87, 0xd1, 0xd6, 0x87, 0x6b, 0x7b, 0x94, 0x59, 0x88, 0x1b, 0xd6, 0x08, 0x08, 0xe2, 0xef, 0x84, 0x74, 0xc7, 0x1c, 0x2c, 0x87, 0xd1, 0xd6, 0x87, 0x6b, 0x7b, 0x94, 0x59}
require.Equal(t, "88:1b:d6:08:08:e2:ef:84:74:c7:1c:2c:87:d1:d6:87:6b:7b:94:59:88:1b:d6:08:08:e2:ef:84:74:c7:1c:2c:...", certs.KeyIdentifierString(keyId2))
}

0 comments on commit 94e8052

Please sign in to comment.