Skip to content
This repository has been archived by the owner on Jun 9, 2024. It is now read-only.

Commit

Permalink
Add XML CMS encoding/decoding
Browse files Browse the repository at this point in the history
  • Loading branch information
lspgn committed Jan 2, 2020
1 parent 2eb8cdf commit f978249
Show file tree
Hide file tree
Showing 6 changed files with 317 additions and 3 deletions.
133 changes: 133 additions & 0 deletions ca/xml.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
package ca

import (
"bytes"
"encoding/xml"
"io"
)

const (
XML_VERSION_RFC8181 = 4
XML_VERSION_RFC8183 = 1
)

type XMLMessage struct {
XMLName xml.Name `xml:"http://www.hactrn.net/uris/rpki/publication-spec/ msg"`
Version int `xml:"version,attr"`
Type string `xml:"type,attr"`
Inner string `xml:",innerxml"`
}

type XMLMessageChildRequest struct {
XMLName xml.Name `xml:"http://www.hactrn.net/uris/rpki/rpki-setup/ child_request"`
Version int `xml:"version,attr"`
ChildHandle string `xml:"child_handle,attr"`
Tag string `xml:"tag,attr"`
Inner string `xml:",innerxml"`
}

type XMLMessageParentResponse struct {
XMLName xml.Name `xml:"http://www.hactrn.net/uris/rpki/rpki-setup/ parent_response"`
Version int `xml:"version,attr"`
Tag string `xml:"tag,attr"`
ServiceURI string `xml:"service_uri,attr"`
ChildHandle string `xml:"child_handle,attr"`
ParentHandle string `xml:"parent_handle,attr"`
Inner string `xml:",innerxml"`
}

type XMLMessagePublisherRequest struct {
XMLName xml.Name `xml:"http://www.hactrn.net/uris/rpki/rpki-setup/ publisher_request"`
Version int `xml:"version,attr"`
Tag string `xml:"tag,attr"`
PublisherHandle string `xml:"publisher_handle,attr"`
Inner string `xml:",innerxml"`
}

type XMLMessageRepositoryResponse struct {
XMLName xml.Name `xml:"http://www.hactrn.net/uris/rpki/rpki-setup/ repository_response"`
Version int `xml:"version,attr"`
Tag string `xml:"tag,attr"`
ServiceURI string `xml:"service_uri,attr"`
SIABase string `xml:"sia_base,attr"`
RRDPNotificationURI string `xml:"rrdp_notification_uri,attr"`
PublisherHandle string `xml:"publisher_handle,attr"`
Inner string `xml:",innerxml"`
}

func NewXMLList() *XMLMessage {
return &XMLMessage{
Version: XML_VERSION_RFC8181,
Type: "query",
Inner: "<list/>",
}
}

type Content struct {
XMLName xml.Name
Hash string `xml:"hash,attr"`
ErrorCode string `xml:"error_code,attr"`
Tag string `xml:"tag,attr"`
URI string `xml:"uri,attr"`
Inner string `xml:",innerxml"`
}

func DecodeInner(inner []byte) ([]Content, error) {
var innerContent []Content
var err error
if len(inner) > 0 {
buf := bytes.NewBuffer(inner)
dec := xml.NewDecoder(buf)
for err == nil {
err = dec.Decode(&innerContent)
}
if err == io.EOF {
err = nil
}
}
return innerContent, err
}

func DecodeXML(message []byte) (*XMLMessage, error) {
var msg XMLMessage
buf := bytes.NewBuffer(message)
dec := xml.NewDecoder(buf)
err := dec.Decode(&msg)
return &msg, err
}

func DecodeXMLFull(message []byte) (*XMLMessage, []Content, error) {
var msg XMLMessage
buf := bytes.NewBuffer(message)
dec := xml.NewDecoder(buf)
err := dec.Decode(&msg)
if err != nil {
return nil, nil, err
}
innerContent, err := DecodeInner([]byte(msg.Inner))
return &msg, innerContent, err
}

func DecodeXMLCRFull(message []byte) (*XMLMessageChildRequest, []Content, error) {
var msg XMLMessageChildRequest
buf := bytes.NewBuffer(message)
dec := xml.NewDecoder(buf)
err := dec.Decode(&msg)
if err != nil {
return nil, nil, err
}
innerContent, err := DecodeInner([]byte(msg.Inner))
return &msg, innerContent, err
}

func DecodeXMLPRFull(message []byte) (*XMLMessageParentResponse, []Content, error) {
var msg XMLMessageParentResponse
buf := bytes.NewBuffer(message)
dec := xml.NewDecoder(buf)
err := dec.Decode(&msg)
if err != nil {
return nil, nil, err
}
innerContent, err := DecodeInner([]byte(msg.Inner))
return &msg, innerContent, err
}
27 changes: 25 additions & 2 deletions validator/lib/cms.go
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,10 @@ func EncryptSignatureRSA(rand io.Reader, signature []byte, privKey *rsa.PrivateK
// Pass fullbytes of any EContent
// Do one for ROA and MFT
func EContentToEncap(econtent []byte) ([]byte, error) {
return EContentToEncapBF(econtent, false)
}

func EContentToEncapBF(econtent []byte, skipbf bool) ([]byte, error) {
var inner asn1.RawValue
_, err := asn1.Unmarshal(econtent, &inner)
if err != nil {
Expand All @@ -199,7 +203,10 @@ func EContentToEncap(econtent []byte) ([]byte, error) {
if err != nil {
return inner2.Bytes, err
}
fullbytes, _, err := BadFormatGroup(inner2.Bytes)
fullbytes := inner2.Bytes
if !skipbf {
fullbytes, _, err = BadFormatGroup(inner2.Bytes)
}
return fullbytes, err
}

Expand Down Expand Up @@ -261,6 +268,15 @@ func (cms *CMS) Sign(rand io.Reader, ski []byte, encap []byte, priv interface{},
return nil
}

func (cms *CMS) AddCRLs(crls []byte) error {
crlsM, err := asn1.MarshalWithParams([]asn1.RawValue{asn1.RawValue{FullBytes: crls}}, "tag:1,optional")
if err != nil {
return err
}
cms.SignedData.CRLs = asn1.RawValue{FullBytes: crlsM}
return nil
}

// Won't validate if signedattributes is empty
func (cms *CMS) Validate(encap []byte, cert *x509.Certificate) error {
signedAttributes := cms.SignedData.SignerInfos[0].SignedAttrs
Expand Down Expand Up @@ -378,8 +394,15 @@ func EncodeCMS(certificate []byte, encapContent interface{}, signingTime time.Ti
}
val.FullBytes = mftBytes
signOid = ManifestOID
case *XML:
xmlBytes, err := asn1.Marshal(*ec)
if err != nil {
return nil, err
}
val.FullBytes = xmlBytes
signOid = XMLOID
default:
return nil, errors.New("Unknown type of content (not ROA or Manifest)")
return nil, errors.New("Unknown type of content (not ROA, Manifest or XML)")
}

certificateBytes, err := asn1.MarshalWithParams(certificate, "tag:0,implicit")
Expand Down
1 change: 0 additions & 1 deletion validator/lib/tal.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,6 @@ func HashRSAPublicKey(key rsa.PublicKey) ([]byte, error) {
if err != nil {
return nil, err
}
//fmt.Printf("TESTA A %x\n", keyBytesHash)

hash := sha1.Sum(keyBytesHash)
return hash[:], nil
Expand Down
Binary file added validator/lib/xml.data
Binary file not shown.
88 changes: 88 additions & 0 deletions validator/lib/xml.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
package librpki

import (
"bytes"
"encoding/asn1"
"encoding/xml"
)

var (
XMLOID = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 16, 1, 28}
)

type XML struct {
OID asn1.ObjectIdentifier
EContent asn1.RawValue `asn1:"tag:0,explicit,optional"`
}

type XMLContent struct {
Message interface{}
}

type RPKI_XML struct {
Content []byte
Certificate *RPKI_Certificate

InnerValid bool
InnerValidityError error
}

func EncodeXMLContent(content interface{}) (*XML, error) {
buf := bytes.NewBuffer([]byte{})
enc := xml.NewEncoder(buf)
err := enc.Encode(content)
if err != nil {
return nil, err
}
return EncodeXMLData(buf.Bytes())
}

func EncodeXMLData(message []byte) (*XML, error) {
eContentEnc, err := asn1.MarshalWithParams(message, "tag:0,explicit")
if err != nil {
return nil, err
}

xmlContent := &XML{
OID: XMLOID,
EContent: asn1.RawValue{FullBytes: eContentEnc},
}
return xmlContent, nil
}

func DecodeXML(data []byte) (*RPKI_XML, error) {
c, err := DecodeCMS(data)
if err != nil {
return nil, err
}

var rawxml XML
_, err = asn1.Unmarshal(c.SignedData.EncapContentInfo.FullBytes, &rawxml)
if err != nil {
return nil, err
}

var inner asn1.RawValue
_, err = asn1.Unmarshal(rawxml.EContent.Bytes, &inner)
if err != nil {
return nil, err
}

var rpki_xml RPKI_XML
rpki_xml.Content = inner.Bytes

cert, err := c.GetRPKICertificate()
if err != nil {
return &rpki_xml, err
}
rpki_xml.Certificate = cert

err = c.Validate(inner.Bytes, cert.Certificate)
if err != nil {
rpki_xml.InnerValidityError = err
} else {
rpki_xml.InnerValid = true
}

return &rpki_xml, nil
}
71 changes: 71 additions & 0 deletions validator/lib/xml_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package librpki

import (
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"crypto/x509/pkix"
"encoding/asn1"
"encoding/hex"
"github.com/stretchr/testify/assert"
"math/big"
"strings"
"testing"
"time"
)

func TestEncodeXMLContent(t *testing.T) {
msg := []byte(`<msg xmlns="http://www.hactrn.net/uris/rpki/publication-spec/" version="4" type="query"><list /></msg>`)
contentEnc, err := EncodeXMLData(msg)
assert.Nil(t, err)

now := time.Now().UTC()
cms, err := EncodeCMS(nil, contentEnc, now)
assert.Nil(t, err)

privkeyParent, err := rsa.GenerateKey(rand.Reader, 2048)
skiParent, _ := HashRSAPublicKey(*privkeyParent.Public().(*rsa.PublicKey))

parentCert := &x509.Certificate{
Version: 1,
SerialNumber: big.NewInt(1),
Subject: pkix.Name{
CommonName: strings.ToUpper(hex.EncodeToString(skiParent)),
},
NotBefore: now.Add(-time.Minute * 5),
NotAfter: now.Add(time.Hour * 24 * (365*100 + 24)),
SubjectKeyId: skiParent,
}

privkey, err := rsa.GenerateKey(rand.Reader, 2048)
ski, _ := HashRSAPublicKey(*privkey.Public().(*rsa.PublicKey))

cert := &x509.Certificate{
Version: 1,
SerialNumber: big.NewInt(1),
Subject: pkix.Name{
CommonName: strings.ToUpper(hex.EncodeToString(ski)),
},
NotBefore: now.Add(-time.Minute * 5),
NotAfter: now.Add(time.Hour * 24 * (365*100 + 24)),
SubjectKeyId: ski,
}
pubkey := privkey.Public()
certBytes, err := x509.CreateCertificate(rand.Reader, cert, parentCert, pubkey, privkeyParent)

crls, err := parentCert.CreateCRL(rand.Reader, privkeyParent, []pkix.RevokedCertificate{}, now.Add(-time.Minute*5), now.Add(time.Minute*5))
assert.Nil(t, err)
cms.AddCRLs(crls)

encap, _ := EContentToEncapBF(contentEnc.EContent.FullBytes, true)
err = cms.Sign(rand.Reader, ski, encap, privkey, certBytes)
assert.Nil(t, err)

entriesBytes, err := asn1.Marshal(*cms)
assert.Nil(t, err)

data, err := DecodeXML(entriesBytes)
assert.Nil(t, err)
assert.Equal(t, data.Content, msg)
assert.Equal(t, data.InnerValid, true)
}

0 comments on commit f978249

Please sign in to comment.