You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
284 lines
8.1 KiB
284 lines
8.1 KiB
// Package der checks that bytes are one element in the Distinguished Encoding
|
|
// Rules of X.690, as spec v0.11 §29.10 asks of a CMS signature before it
|
|
// looks inside it: definite and minimal lengths, no high tag numbers, no
|
|
// constructed form of a type that DER only has primitive, canonical BOOLEAN,
|
|
// INTEGER, NULL, OBJECT IDENTIFIER, BIT STRING, UTCTime and GeneralizedTime,
|
|
// and no bytes after the element. Go's encoding/asn1 accepts some of what DER
|
|
// forbids, and a signature that two implementations read the same must not
|
|
// depend on it.
|
|
//
|
|
// The order of the elements of a SET OF cannot be checked without a schema:
|
|
// SetOfSorted does it for the callers that know theirs.
|
|
package der
|
|
|
|
import (
|
|
"bytes"
|
|
"errors"
|
|
"fmt"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
const maxDepth = 32
|
|
|
|
// Check returns nil when b is exactly one DER element.
|
|
func Check(b []byte) error {
|
|
n, err := check(b, 0)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if n != len(b) {
|
|
return fmt.Errorf("der: %d bytes after the element", len(b)-n)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Split returns the content of the DER element b, which must be constructed,
|
|
// as the encodings of its children, and the identifier octet of b. It
|
|
// assumes Check passed.
|
|
func Split(b []byte) (id byte, children [][]byte, err error) {
|
|
if len(b) == 0 || b[0]&0x20 == 0 {
|
|
return 0, nil, errors.New("der: not a constructed element")
|
|
}
|
|
hl, cl, err := header(b)
|
|
if err != nil {
|
|
return 0, nil, err
|
|
}
|
|
rest := b[hl : hl+cl]
|
|
for len(rest) > 0 {
|
|
h, c, err := header(rest)
|
|
if err != nil {
|
|
return 0, nil, err
|
|
}
|
|
children = append(children, rest[:h+c])
|
|
rest = rest[h+c:]
|
|
}
|
|
return b[0], children, nil
|
|
}
|
|
|
|
// Content returns the content octets of the DER element b.
|
|
func Content(b []byte) ([]byte, error) {
|
|
hl, cl, err := header(b)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return b[hl : hl+cl], nil
|
|
}
|
|
|
|
// SetOfSorted reports whether the encodings are in ascending order of their
|
|
// bytes, as DER requires of the elements of a SET OF (X.690 11.6). Equal
|
|
// elements may repeat, side by side: X.690 does not forbid it, and a time-
|
|
// stamping authority may send its certificate twice. Whoever counts the
|
|
// elements of a SET OF decides what a repetition means.
|
|
func SetOfSorted(elems [][]byte) bool {
|
|
for i := 1; i < len(elems); i++ {
|
|
if bytes.Compare(elems[i-1], elems[i]) > 0 {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
// ParseTime reads a UTCTime or a GeneralizedTime element as DER writes them
|
|
// (X.690 11.7 and 11.8): YYMMDDHHMMSSZ, with the years 50 to 99 in the 20th
|
|
// century (RFC 5280 4.1.2.5.1), or YYYYMMDDHHMMSS, an optional fraction of
|
|
// seconds without a trailing zero, and Z. A date or a time that does not
|
|
// exist, a second 60 included, is refused. fraction reports whether a
|
|
// GeneralizedTime has one.
|
|
func ParseTime(b []byte) (t time.Time, fraction bool, err error) {
|
|
if len(b) < 2 || (b[0] != 0x17 && b[0] != 0x18) {
|
|
return time.Time{}, false, errors.New("der: not a UTCTime or a GeneralizedTime")
|
|
}
|
|
c, err := Content(b)
|
|
if err != nil {
|
|
return time.Time{}, false, err
|
|
}
|
|
return parseTime(b[0], string(c))
|
|
}
|
|
|
|
func parseTime(tag byte, s string) (time.Time, bool, error) {
|
|
bad := errors.New("der: a time that is not in the form of DER")
|
|
if len(s) < 13 || s[len(s)-1] != 'Z' {
|
|
return time.Time{}, false, bad
|
|
}
|
|
body, frac := s[:len(s)-1], ""
|
|
var year int
|
|
switch tag {
|
|
case 0x17:
|
|
if len(body) != 12 || !digits(body) {
|
|
return time.Time{}, false, bad
|
|
}
|
|
year = atoi(body[:2]) + 1900
|
|
if year < 1950 {
|
|
year += 100
|
|
}
|
|
body = body[2:]
|
|
default:
|
|
if dot := strings.IndexByte(body, '.'); dot >= 0 {
|
|
body, frac = body[:dot], body[dot+1:]
|
|
if frac == "" || frac[len(frac)-1] == '0' || !digits(frac) {
|
|
return time.Time{}, false, bad
|
|
}
|
|
}
|
|
if len(body) != 14 || !digits(body) {
|
|
return time.Time{}, false, bad
|
|
}
|
|
year = atoi(body[:4])
|
|
body = body[4:]
|
|
}
|
|
month, day, hour, minute, second := atoi(body[0:2]), atoi(body[2:4]), atoi(body[4:6]), atoi(body[6:8]), atoi(body[8:10])
|
|
if month < 1 || month > 12 || day < 1 || hour > 23 || minute > 59 || second > 59 ||
|
|
day > time.Date(year, time.Month(month)+1, 0, 0, 0, 0, 0, time.UTC).Day() {
|
|
return time.Time{}, false, errors.New("der: a date or a time that does not exist")
|
|
}
|
|
nanos := 0
|
|
for i := 0; i < 9; i++ {
|
|
nanos *= 10
|
|
if i < len(frac) {
|
|
nanos += int(frac[i] - '0')
|
|
}
|
|
}
|
|
return time.Date(year, time.Month(month), day, hour, minute, second, nanos, time.UTC), frac != "", nil
|
|
}
|
|
|
|
func digits(s string) bool {
|
|
for i := 0; i < len(s); i++ {
|
|
if s[i] < '0' || s[i] > '9' {
|
|
return false
|
|
}
|
|
}
|
|
return s != ""
|
|
}
|
|
|
|
func atoi(s string) int {
|
|
n := 0
|
|
for i := 0; i < len(s); i++ {
|
|
n = n*10 + int(s[i]-'0')
|
|
}
|
|
return n
|
|
}
|
|
|
|
// header returns the length of the identifier and length octets of the
|
|
// element at the start of b, and the length of its content, which must fit
|
|
// in b.
|
|
func header(b []byte) (headerLen, contentLen int, err error) {
|
|
if len(b) < 2 {
|
|
return 0, 0, errors.New("der: truncated element")
|
|
}
|
|
if b[0]&0x1f == 0x1f {
|
|
return 0, 0, errors.New("der: a tag number of 31 or more")
|
|
}
|
|
l := b[1]
|
|
switch {
|
|
case l < 0x80:
|
|
headerLen, contentLen = 2, int(l)
|
|
case l == 0x80:
|
|
return 0, 0, errors.New("der: an indefinite length")
|
|
case l == 0xff:
|
|
return 0, 0, errors.New("der: a length of 0xff")
|
|
default:
|
|
n := int(l & 0x7f)
|
|
if n > 4 || len(b) < 2+n {
|
|
return 0, 0, errors.New("der: a length that does not fit")
|
|
}
|
|
if b[2] == 0 {
|
|
return 0, 0, errors.New("der: a length with a leading zero")
|
|
}
|
|
v := 0
|
|
for _, c := range b[2 : 2+n] {
|
|
v = v<<8 | int(c)
|
|
}
|
|
if v < 0x80 {
|
|
return 0, 0, errors.New("der: a long form for a length under 128")
|
|
}
|
|
headerLen, contentLen = 2+n, v
|
|
}
|
|
if contentLen < 0 || contentLen > len(b)-headerLen {
|
|
return 0, 0, errors.New("der: an element longer than its container")
|
|
}
|
|
return headerLen, contentLen, nil
|
|
}
|
|
|
|
// check validates the element at the start of b and returns its length.
|
|
func check(b []byte, depth int) (int, error) {
|
|
if depth > maxDepth {
|
|
return 0, errors.New("der: nested too deep")
|
|
}
|
|
hl, cl, err := header(b)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
content := b[hl : hl+cl]
|
|
id := b[0]
|
|
class, constructed, tag := id>>6, id&0x20 != 0, id&0x1f
|
|
if constructed {
|
|
if class == 0 && tag != 16 && tag != 17 {
|
|
return 0, fmt.Errorf("der: constructed form of the universal type %d", tag)
|
|
}
|
|
for len(content) > 0 {
|
|
n, err := check(content, depth+1)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
content = content[n:]
|
|
}
|
|
return hl + cl, nil
|
|
}
|
|
if class == 0 {
|
|
if err := checkPrimitive(tag, content); err != nil {
|
|
return 0, err
|
|
}
|
|
}
|
|
return hl + cl, nil
|
|
}
|
|
|
|
func checkPrimitive(tag byte, c []byte) error {
|
|
switch tag {
|
|
case 1: // BOOLEAN
|
|
if len(c) != 1 || (c[0] != 0 && c[0] != 0xff) {
|
|
return errors.New("der: a BOOLEAN that is not 00 or FF")
|
|
}
|
|
case 2, 10: // INTEGER, ENUMERATED
|
|
if len(c) == 0 {
|
|
return errors.New("der: an empty INTEGER")
|
|
}
|
|
if len(c) > 1 && (c[0] == 0 && c[1]&0x80 == 0 || c[0] == 0xff && c[1]&0x80 != 0) {
|
|
return errors.New("der: an INTEGER that is not minimal")
|
|
}
|
|
case 3: // BIT STRING
|
|
if len(c) == 0 || c[0] > 7 || len(c) == 1 && c[0] != 0 {
|
|
return errors.New("der: a malformed BIT STRING")
|
|
}
|
|
if len(c) > 1 && c[0] != 0 && c[len(c)-1]&(1<<c[0]-1) != 0 {
|
|
return errors.New("der: a BIT STRING with unused bits that are not zero")
|
|
}
|
|
case 5: // NULL
|
|
if len(c) != 0 {
|
|
return errors.New("der: a NULL with content")
|
|
}
|
|
case 6: // OBJECT IDENTIFIER
|
|
if len(c) == 0 || c[len(c)-1]&0x80 != 0 {
|
|
return errors.New("der: a malformed OBJECT IDENTIFIER")
|
|
}
|
|
start := true
|
|
for _, x := range c {
|
|
if start && x == 0x80 {
|
|
return errors.New("der: an OBJECT IDENTIFIER with a leading 0x80 in a subidentifier")
|
|
}
|
|
start = x&0x80 == 0
|
|
}
|
|
case 23, 24: // UTCTime, GeneralizedTime
|
|
if _, _, err := parseTime(tag, string(c)); err != nil {
|
|
return err
|
|
}
|
|
case 4, 12, 19, 20, 22, 26, 28, 30: // OCTET STRING and the string types of X.509
|
|
case 16, 17:
|
|
return fmt.Errorf("der: the universal type %d in primitive form", tag)
|
|
default:
|
|
// 0 is the end of contents of BER, and the rest are types that no
|
|
// certificate, signature or token of the profile has.
|
|
return fmt.Errorf("der: the universal type %d, which the profile does not use", tag)
|
|
}
|
|
return nil
|
|
}
|