cert-manager/pkg/util/pki/asn1_util_test.go
SpectralHiss d07dd3de5f Fix OtherName feature flag validation logic
* Improve test comments for UniversalValue

Signed-off-by: SpectralHiss <houssem.elfekih@jetstack.io>
2024-01-08 13:34:09 +00:00

269 lines
6.0 KiB
Go

/*
Copyright 2023 The cert-manager Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package pki
import (
"encoding/asn1"
"errors"
"reflect"
"testing"
)
func TestParseObjectIdentifier(t *testing.T) {
testCases := []struct {
oidString string
expectedOid asn1.ObjectIdentifier
expectedErr error
}{
{
oidString: "1.2.3.4.5",
expectedOid: asn1.ObjectIdentifier{1, 2, 3, 4, 5},
expectedErr: nil,
},
{
oidString: "1.2.840.113549.1.1.1",
expectedOid: asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 1},
expectedErr: nil,
},
{
oidString: "1.3.6.1.4.1.311.60.2.1.3",
expectedOid: asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 311, 60, 2, 1, 3},
expectedErr: nil,
},
{
oidString: ".",
expectedOid: nil,
expectedErr: errors.New("strconv.Atoi: parsing \"\": invalid syntax"),
},
{
oidString: ".555",
expectedOid: nil,
expectedErr: errors.New("strconv.Atoi: parsing \"\": invalid syntax"),
},
{
oidString: "555.",
expectedOid: nil,
expectedErr: errors.New("strconv.Atoi: parsing \"\": invalid syntax"),
},
{
oidString: "test.5",
expectedOid: nil,
expectedErr: errors.New("strconv.Atoi: parsing \"test\": invalid syntax"),
},
}
for _, tc := range testCases {
oid, err := ParseObjectIdentifier(tc.oidString)
if err != nil {
if tc.expectedErr == nil {
t.Errorf("Unexpected error: %v", err)
} else if err.Error() != tc.expectedErr.Error() {
t.Errorf("Expected error: %v, got: %v", tc.expectedErr, err)
}
} else if !oid.Equal(tc.expectedOid) {
t.Errorf("Expected OID: %v, got: %v", tc.expectedOid, oid)
}
}
}
func TestMarshalAndUnmarshalUniversalValue(t *testing.T) {
testCases := []struct {
name string
uv UniversalValue
raw asn1.RawValue
overrideRoundtripUv *UniversalValue
}{
{
name: "Test with IA5String",
uv: UniversalValue{
IA5String: "test",
},
raw: asn1.RawValue{
Bytes: []byte("test"),
Class: asn1.ClassUniversal,
Tag: asn1.TagIA5String,
},
},
{
name: "Test with Utf8String",
uv: UniversalValue{
UTF8String: "test",
},
raw: asn1.RawValue{
Bytes: []byte("test"),
Class: asn1.ClassUniversal,
Tag: asn1.TagUTF8String,
},
},
{
name: "Test with PrintableString",
uv: UniversalValue{
PrintableString: "test",
},
raw: asn1.RawValue{
Bytes: []byte("test"),
Class: asn1.ClassUniversal,
Tag: asn1.TagPrintableString,
},
},
{
name: "Test with Bytes",
uv: UniversalValue{
// Ia5String byte array with value "test"
// https://lapo.it/asn1js/#FgR0ZXN0
Bytes: []byte{0x16, 0x04, 0x74, 0x65, 0x73, 0x74},
},
overrideRoundtripUv: &UniversalValue{
IA5String: "test",
},
raw: asn1.RawValue{
Bytes: []byte("test"),
Class: asn1.ClassUniversal,
Tag: asn1.TagIA5String,
},
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
{
rawValue, err := MarshalUniversalValue(tc.uv)
if err != nil {
t.Errorf("Unexpected error: %v", err)
}
// Calculate fullBytes
fullBytes, err := asn1.Marshal(tc.raw)
if err != nil {
t.Errorf("Unexpected error: %v", err)
}
if !reflect.DeepEqual(rawValue, fullBytes) {
t.Errorf("Expected rawValue: %v, got: %v", fullBytes, rawValue)
}
}
{
uv, err := UnmarshalUniversalValue(tc.raw)
if err != nil {
t.Errorf("Unexpected error: %v", err)
}
targetUv := tc.uv
if tc.overrideRoundtripUv != nil {
targetUv = *tc.overrideRoundtripUv
}
if !reflect.DeepEqual(uv, targetUv) {
t.Errorf("Expected uv: %v, got: %v", targetUv, uv)
}
}
})
}
}
// Since we make use of the standard utf.ValidString
// we just do a sanity check to ensure it is used on Marshall/UnMarshal
func TestMarshalUTF8Validation(t *testing.T) {
uv := UniversalValue{
// Invalid utf8 byte sequence, string() just casts byte[] verbatim whereas "" causes compile error
UTF8String: string([]byte{0xc3, 0x28}),
}
_, err := MarshalUniversalValue(uv)
if err == nil {
t.Error("Expected invalid UTF8 string to raise error")
}
inValidASN1UTF8 := asn1.RawValue{
Tag: asn1.TagUTF8String,
Class: asn1.ClassUniversal,
Bytes: []byte{0xe2, 0x82, 0x28}, // Another out of range utf8 byte sequence
}
_, err = UnmarshalUniversalValue(inValidASN1UTF8)
if err == nil {
t.Error("Expected invalid UTF8 asn1 value to raise error")
}
}
func TestIsIA5String(t *testing.T) {
ia5Strings := []string{
"test",
"1234",
"!@#$",
" ",
"",
}
for _, ia5String := range ia5Strings {
err := isIA5String(ia5String)
if err != nil {
t.Errorf("Expected IA5 string %q, got: %s", ia5String, err.Error())
}
}
nonIA5Strings := []string{
"中文",
}
for _, nonIA5String := range nonIA5Strings {
err := isIA5String(nonIA5String)
if err == nil {
t.Errorf("Expected non-IA5 string error for %s, got: nil", nonIA5String)
}
}
}
func TestIsPrintable(t *testing.T) {
printableStrings := []string{
"test",
"1234",
"*AA:-)/?",
" ",
"",
"Test*",
"Test&",
}
for _, printableString := range printableStrings {
isPrintable := isPrintable(printableString)
if !isPrintable {
t.Errorf("Expected printable string %q, got: %v", printableString, isPrintable)
}
}
nonPrintableStrings := []string{
"中文",
"Test!",
"Test@",
"Test#",
"Test%",
}
for _, nonPrintableString := range nonPrintableStrings {
isPrintable := isPrintable(nonPrintableString)
if isPrintable {
t.Errorf("Expected non-printable string %q, got: %v", nonPrintableString, isPrintable)
}
}
}