blob: 9ef77a1a5a0df78b62ca7813ee5b05b2b9f1d5b2 [file] [log] [blame]
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you 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 documents - data is signed and nested in "encrypted envelope"
*/
package documents
import (
"sync"
"github.com/gogo/protobuf/proto"
"github.com/pkg/errors"
)
//DocType - defines a document that is parseable
//It is necessary to build this list because there is no inheritance in
var (
//EnvelopeVersion the versioning of the entire Envelope, (not individual documents/contents)
EnvelopeVersion float32 = 1.0
mutex = &sync.Mutex{}
)
//IDDoc wrapper to encapsulate Header & IDDocument into one object
type IDDoc struct {
*Header
*IDDocument
}
//OrderDoc puts and order in encrypted wrapper
type OrderDoc struct {
*Header
*OrderDocument
}
//NewIDDoc generate a new empty IDDoc
func NewIDDoc() *IDDoc {
return &IDDoc{
&Header{},
&IDDocument{},
}
}
//NewOrderDoc generate a new order
func NewOrderDoc() OrderDoc {
ret := OrderDoc{}
ret.Header = &Header{}
ret.OrderDocument = &OrderDocument{}
return ret
}
//EncodeIDDocument encode an IDDoc into a raw bytes stream for the wire
func EncodeIDDocument(idDocument *IDDoc, blsSK []byte) ([]byte, error) {
rawDoc, err := Encode("", idDocument.IDDocument, nil, idDocument.Header, blsSK, nil)
return rawDoc, err
}
//EncodeOrderDocument encode an OrderDoc into a raw bytes stream for the wire
func EncodeOrderDocument(nodeID string, orderDoc OrderDoc, blsSK []byte, recipients map[string]*IDDoc) ([]byte, error) {
header := orderDoc.Header
// rawDoc, err := Encode(orderDoc.OrderDocument, nil, header, blsSK, nil)
rawDoc, err := Encode(nodeID, nil, orderDoc.OrderDocument, header, blsSK, recipients)
return rawDoc, err
}
//DecodeIDDocument - decode a raw byte stream into an IDDocument
func DecodeIDDocument(rawdoc []byte, tag string, idDocument *IDDoc) error {
plainText := IDDocument{}
header, err := Decode(rawdoc, tag, nil, "", &plainText, nil, nil)
if err != nil {
return errors.Wrap(err, "DecodeIDDocument Failed to Decode")
}
idDocument.Header = header
idDocument.IDDocument = &plainText
//validate the order document
err = idDocument.IDDocument.Validate()
if err != nil {
return err
}
return nil
}
//OrderDocumentSigner - look at the header inside an order document before decryption
func OrderDocumentSigner(rawDoc []byte) (string, error) {
signedEnvelope := SignedEnvelope{}
err := proto.Unmarshal(rawDoc, &signedEnvelope)
if err != nil {
return "", errors.New("Protobuf - Failed to unmarshal Signed Envelope")
}
return signedEnvelope.SignerCID, nil
}
//DecodeOrderDocument -
func DecodeOrderDocument(rawdoc []byte, tag string, orderdoc *OrderDoc, sikeSK []byte, recipientCID string, sendersBlsPK []byte) error {
cipherText := OrderDocument{}
header, err := Decode(rawdoc, tag, sikeSK, recipientCID, nil, &cipherText, sendersBlsPK)
if err != nil {
return errors.Wrap(err, "DecodeIDDocument Failed to Decode")
}
orderdoc.Header = header
orderdoc.OrderDocument = &cipherText
//validate the order document
//TODO: temp remove validation because we arent using IPFS any more
// err = orderdoc.OrderDocument.Validate()
// if err != nil {
// return err
// }
return nil
}
//Decode - Given a raw envelope, Sike Secret Key & ID - decode into plaintext, cipherText(decrypted) and header
func Decode(rawDoc []byte, tag string, sikeSK []byte, recipientID string, plainText proto.Message, encryptedText proto.Message, sendersBlsPK []byte) (header *Header, err error) {
signedEnvelope := SignedEnvelope{}
err = proto.Unmarshal(rawDoc, &signedEnvelope)
if err != nil {
return &Header{}, errors.New("Protobuf - Failed to unmarshal Signed Envelope")
}
//check the message verification if we have a key for it
if sendersBlsPK != nil {
//if the document is locally signed check with our signature
err = Verify(signedEnvelope, sendersBlsPK)
if err != nil {
return nil, err
}
}
//Decode the envelope
message := signedEnvelope.Message
envelope := Envelope{}
err = proto.Unmarshal(message, &envelope)
if err != nil {
return &Header{}, errors.New("Protobuf - Failed to unmarshal Envelope")
}
header = envelope.Header
header.IPFSID = tag
//Decode the plaintext
if plainText != nil {
err = proto.Unmarshal(envelope.Body, plainText)
if err != nil {
return &Header{}, errors.New("Protobuf - Failed to unmarshall plaintext")
}
}
//Decrypt the cipherText & decode into the correct object
if encryptedText != nil {
recipientList := header.Recipients
aesKey, err := decapsulate(recipientID, recipientList, sikeSK)
if err != nil {
return &Header{}, errors.Wrap(err, "Failed to Decapsulate Encrypted Text in Envelope Decode")
}
decryptedCipherText, err := aesDecrypt(envelope.EncryptedBody, header.EncryptedBodyIV, aesKey)
if err != nil {
return &Header{}, errors.Wrap(err, "Failed to AES Decrypt Envelope cipherText")
}
err = proto.Unmarshal(decryptedCipherText, encryptedText)
if err != nil {
return &Header{}, errors.New("Failed to unmarshall cipherText")
}
}
return header, nil
}
//Encode - convert the header, secret and plaintext into a message for the wire
//The Header can be pre-populated with any nece
func Encode(nodeID string, plainText proto.Message, secretText proto.Message, header *Header, blsSK []byte, recipients map[string]*IDDoc) (rawDoc []byte, err error) {
plainTextDocType, err := detectDocType(plainText)
if err != nil {
return nil, errors.New("Plaintext Document - Unknown Type")
}
encryptedTextDocType, err := detectDocType(secretText)
if err != nil {
return nil, errors.New("Encrypted Document - Unknown Type")
}
//build Header
header.Version = EnvelopeVersion
//TODO: change datetime to real value - its 0 to create fixed IPFS refs
header.DateTime = 0 //time.Now().Unix()
header.BodyTypeCode = plainTextDocType.TypeCode
header.BodyVersion = plainTextDocType.Version
header.EncryptedBodyTypeCode = encryptedTextDocType.TypeCode
header.EncryptedBodyVersion = encryptedTextDocType.Version
//Plaintext to bytes
var body []byte
if plainText != nil {
body, err = proto.Marshal(plainText)
if err != nil {
return nil, errors.Wrap(err, "Failed to marshall Plaintext to Protobuf")
}
}
//CipherText
var cipherText []byte
if secretText != nil {
secretBody, err := proto.Marshal(secretText)
if err != nil {
return nil, err
}
var aesKey, iv []byte
cipherText, aesKey, iv, err = aesEncrypt(secretBody)
if err != nil {
return nil, err
}
header.EncryptedBodyIV = iv
recipientList, err := encapsulateKeyForRecipient(recipients, aesKey)
if err != nil {
return nil, err
}
header.Recipients = recipientList
}
//assemble
envelope := Envelope{}
envelope.Header = header
envelope.Body = body
envelope.EncryptedBody = cipherText
//sign
signedEnvelope, err := sign(envelope, blsSK, nodeID)
if err != nil {
return nil, errors.Wrap(err, "Failed to Sign Envelope")
}
//SignedEnvelope to bytes
rawDoc, err = proto.Marshal(&signedEnvelope)
if err != nil {
return nil, errors.Wrap(err, "Failed to marshall Signed Envelope to Protobuf")
}
return rawDoc, nil
}