93 lines
2.4 KiB
Go
93 lines
2.4 KiB
Go
|
// Copyright (C) 2019 Opsmate, Inc.
|
||
|
//
|
||
|
// This Source Code Form is subject to the terms of the Mozilla
|
||
|
// Public License, v. 2.0. If a copy of the MPL was not distributed
|
||
|
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||
|
//
|
||
|
// This software is distributed WITHOUT A WARRANTY OF ANY KIND.
|
||
|
// See the Mozilla Public License for details.
|
||
|
|
||
|
package certspotter
|
||
|
|
||
|
import (
|
||
|
"encoding/asn1"
|
||
|
)
|
||
|
|
||
|
func canonicalizeRDNString(fromStr string) string {
|
||
|
from := []byte(fromStr)
|
||
|
to := []byte{}
|
||
|
inWhitespace := true
|
||
|
for _, ch := range from {
|
||
|
if ch == ' ' || ch == '\f' || ch == '\n' || ch == '\r' || ch == '\t' || ch == '\v' {
|
||
|
if !inWhitespace {
|
||
|
to = append(to, ' ')
|
||
|
}
|
||
|
inWhitespace = true
|
||
|
} else {
|
||
|
if ch >= 'A' && ch <= 'Z' {
|
||
|
to = append(to, ch+32) // convert to lowercase
|
||
|
} else {
|
||
|
to = append(to, ch)
|
||
|
}
|
||
|
inWhitespace = false
|
||
|
}
|
||
|
}
|
||
|
if inWhitespace && len(to) > 0 {
|
||
|
// whack off the space character that we appended
|
||
|
to = to[:len(to)-1]
|
||
|
}
|
||
|
return string(to)
|
||
|
}
|
||
|
|
||
|
func shouldCanonicalizeASN1String(value *asn1.RawValue) bool {
|
||
|
if !value.IsCompound && value.Class == 0 {
|
||
|
return value.Tag == 12 || value.Tag == 19 || value.Tag == 22 || value.Tag == 20 || value.Tag == 26 || value.Tag == 30 || value.Tag == 28
|
||
|
}
|
||
|
return false
|
||
|
}
|
||
|
|
||
|
func canonicalizeATV(oldATV AttributeTypeAndValue) (AttributeTypeAndValue, error) {
|
||
|
if shouldCanonicalizeASN1String(&oldATV.Value) {
|
||
|
str, err := decodeASN1String(&oldATV.Value)
|
||
|
if err != nil {
|
||
|
return AttributeTypeAndValue{}, err
|
||
|
}
|
||
|
str = canonicalizeRDNString(str)
|
||
|
return AttributeTypeAndValue{
|
||
|
Type: oldATV.Type,
|
||
|
Value: asn1.RawValue{
|
||
|
Class: 0,
|
||
|
Tag: asn1.TagUTF8String,
|
||
|
IsCompound: false,
|
||
|
Bytes: []byte(str),
|
||
|
},
|
||
|
}, nil
|
||
|
} else {
|
||
|
return oldATV, nil
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func canonicalizeRDNSet(oldSet RelativeDistinguishedNameSET) (RelativeDistinguishedNameSET, error) {
|
||
|
newSet := make([]AttributeTypeAndValue, len(oldSet))
|
||
|
for i := range oldSet {
|
||
|
var err error
|
||
|
newSet[i], err = canonicalizeATV(oldSet[i])
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
}
|
||
|
return newSet, nil
|
||
|
}
|
||
|
|
||
|
func CanonicalizeRDNSequence(oldSequence RDNSequence) (RDNSequence, error) {
|
||
|
newSequence := make([]RelativeDistinguishedNameSET, len(oldSequence))
|
||
|
for i := range oldSequence {
|
||
|
var err error
|
||
|
newSequence[i], err = canonicalizeRDNSet(oldSequence[i])
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
}
|
||
|
return newSequence, nil
|
||
|
}
|