From e73bf333ef8920dbb52ad18d4bd38ad9d9bc76d7 Mon Sep 17 00:00:00 2001 From: Andrey Mirtchovski Date: Fri, 20 Apr 2018 16:33:09 +0000 Subject: [PATCH] x/crypto: cryptobyte: manage integers with implicit tags This change adds two functions to cryptobyte to encode and decode int64 with tags supplied by the user. This change also modifies a documentation comment which was outdated. Fixes golang/go#24973 Change-Id: I2e3ca475891ba62df902f33085719f94e87a27cc GitHub-Last-Rev: cd0300d9bfbd4e38f22503a046ea18f9ddd37676 GitHub-Pull-Request: golang/crypto#42 Reviewed-on: https://go-review.googlesource.com/108456 Reviewed-by: Adam Langley --- cryptobyte/asn1.go | 16 +++++++++++++++- cryptobyte/asn1_test.go | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 1 deletion(-) diff --git a/cryptobyte/asn1.go b/cryptobyte/asn1.go index bb094a5..528b9bf 100644 --- a/cryptobyte/asn1.go +++ b/cryptobyte/asn1.go @@ -23,6 +23,12 @@ func (b *Builder) AddASN1Int64(v int64) { b.addASN1Signed(asn1.INTEGER, v) } +// AddASN1Int64WithTag appends a DER-encoded ASN.1 INTEGER with the +// given tag. +func (b *Builder) AddASN1Int64WithTag(v int64, tag asn1.Tag) { + b.addASN1Signed(tag, v) +} + // AddASN1Enum appends a DER-encoded ASN.1 ENUMERATION. func (b *Builder) AddASN1Enum(v int64) { b.addASN1Signed(asn1.ENUM, v) @@ -362,6 +368,14 @@ func asn1Unsigned(out *uint64, n []byte) bool { return true } +// ReadASN1Int64WithTag decodes an ASN.1 INTEGER with the given tag into out +// and advances. It reports whether the read was successful and resulted in a +// value that can be represented in an int64. +func (s *String) ReadASN1Int64WithTag(out *int64, tag asn1.Tag) bool { + var bytes String + return s.ReadASN1(&bytes, tag) && checkASN1Integer(bytes) && asn1Signed(out, bytes) +} + // ReadASN1Enum decodes an ASN.1 ENUMERATION into out and advances. It reports // whether the read was successful. func (s *String) ReadASN1Enum(out *int) bool { @@ -623,7 +637,7 @@ func (s *String) ReadOptionalASN1Integer(out interface{}, tag asn1.Tag, defaultV // ReadOptionalASN1OctetString attempts to read an optional ASN.1 OCTET STRING // explicitly tagged with tag into out and advances. If no element with a -// matching tag is present, it writes defaultValue into out instead. It reports +// matching tag is present, it sets "out" to nil instead. It reports // whether the read was successful. func (s *String) ReadOptionalASN1OctetString(out *[]byte, outPresent *bool, tag asn1.Tag) bool { var present bool diff --git a/cryptobyte/asn1_test.go b/cryptobyte/asn1_test.go index ee6674a..9f6c952 100644 --- a/cryptobyte/asn1_test.go +++ b/cryptobyte/asn1_test.go @@ -149,6 +149,39 @@ func TestReadASN1IntegerSigned(t *testing.T) { } } }) + + // Repeat with the implicit-tagging functions + t.Run("WithTag", func(t *testing.T) { + for i, test := range testData64 { + tag := asn1.Tag((i * 3) % 32).ContextSpecific() + + testData := make([]byte, len(test.in)) + copy(testData, test.in) + + // Alter the tag of the test case. + testData[0] = uint8(tag) + + in := String(testData) + var out int64 + ok := in.ReadASN1Int64WithTag(&out, tag) + if !ok || out != test.out { + t.Errorf("#%d: in.ReadASN1Int64WithTag() = %v, want true; out = %d, want %d", i, ok, out, test.out) + } + + var b Builder + b.AddASN1Int64WithTag(test.out, tag) + result, err := b.Bytes() + + if err != nil { + t.Errorf("#%d: AddASN1Int64WithTag failed: %s", i, err) + continue + } + + if !bytes.Equal(result, testData) { + t.Errorf("#%d: AddASN1Int64WithTag: got %x, want %x", i, result, testData) + } + } + }) } func TestReadASN1IntegerUnsigned(t *testing.T) {