Example #1
0
// Signs a block. The signing process takes the value of a block and signs
// it using JWS. The signature generated is included in the
// `signatures` block. If a block is empty or unknown, an error is returned.
func (self *Stone) Sign(blockName string, privateKey string) (string, error) {

	var block map[string]interface{}

	signer, err := crypto.ParsePrivateKey([]byte(privateKey))
	if err != nil {
		return "", errors.New("Private Key Error: " + err.Error())
	}

	// block name must be known
	if !util.InStringSlice(KnownBlockNames, blockName) {
		return "", errors.New("block unknown")
	}

	block = self.getBlock(blockName)
	if util.IsMapEmpty(block) {
		return "", errors.New("failed to sign empty block")
	}

	// sign block
	payload, _ := util.MapToJSON(block)
	signature, err := signer.JWS_RSA_Sign(payload)
	if err != nil {
		return "", errors.New("failed to sign block")
	}

	self.Signatures[blockName] = signature
	return signature, nil
}
Example #2
0
// Verify a block's JWS signature. It expects the public key
// part of the keypair used to sign the block.
func (self *Stone) Verify(blockName, signerPublicKey string) error {

	signer, err := crypto.ParsePublicKey([]byte(signerPublicKey))
	if err != nil {
		return errors.New(fmt.Sprintf("Public Key Error: %v", err))
	}

	// block name must be known
	if !util.InStringSlice(KnownBlockNames, blockName) {
		return errors.New("block unknown")
	}

	// ensure block has signature
	if !self.HasSignature(blockName) {
		return errors.New("`" + blockName + "` block has no signature")
	}

	// verify
	_, err = signer.JWS_RSA_Verify(self.Signatures[blockName].(string))
	if err != nil {
		return errors.New(fmt.Sprintf("`%s` block signature could not be verified", blockName))
	}

	return nil
}
Example #3
0
// Validate attributes block.
//
//  Rules:
//
//  - It must accept only `ref_id` and `data` properties.
//  - `ref_id` property must be provided.
//  - `ref_id` property must be a string.
//  - `ref_id` property must equal meta id (meta.id property).
//  - `data` property must be set.
func ValidateAttributesBlock(attributes map[string]interface{}, metaID string) error {

	// must reject unexpected properties
	for prop, _ := range attributes {
		if !util.InStringSlice([]string{"ref_id", "data"}, prop) {
			return errors.New(fmt.Sprintf("`%s` property is unexpected in `attributes` block", prop))
		}
	}

	// `ref_id` property must be set
	if attributes["ref_id"] == nil {
		return errors.New("`attributes` block is missing `ref_id` property")
	}

	// `ref_id` property must be a string value
	if !util.IsStringValue(attributes["ref_id"]) {
		return errors.New("`attributes.ref_id` value type is invalid. Expects string value")
	}

	// `ref_id` property must be equal to meta id
	if attributes["ref_id"].(string) != metaID {
		return errors.New("`attributes.ref_id` not equal to `meta.id`")
	}

	// `data` property must be provided
	if attributes["data"] == nil {
		return errors.New("`attributes` block is missing `data` property")
	}

	return nil
}
Example #4
0
// Validate `signature` block.
//
//  Rules:
//
//  - It must contain only acceptable properties (meta, ownership, embeds).
//  - `meta` signature must be present and must be a string type.
//  - `attributes` property must be string type if set.
//  - `ownership` property must be string type if set.
//  - `embeds` property must be string type if set.
func ValidateSignaturesBlock(signatures map[string]interface{}) error {

	// must reject unexpected properties
	accetableProps := []string{"meta", "ownership", "attributes", "embeds"}
	for prop, _ := range signatures {
		if !util.InStringSlice(accetableProps, prop) {
			return errors.New(fmt.Sprintf("`%s` property is unexpected in `signatures` block", prop))
		}
	}

	// must have `meta` property
	if signatures["meta"] == nil {
		return errors.New("missing `signatures.meta` property")
	} else {
		// meta value type must be string
		if !util.IsStringValue(signatures["meta"]) {
			return errors.New("`signatures.meta` value type is invalid. Expects a string")
		}
	}

	// if signature has `ownership` property, it's value type must be string
	if signatures["ownership"] != nil {
		if !util.IsStringValue(signatures["ownership"]) {
			return errors.New("`signatures.ownership` value type is invalid. Expects a string")
		}
	}

	// if signature has `attributes` property, it's value type must be string
	if signatures["attributes"] != nil {
		if !util.IsStringValue(signatures["attributes"]) {
			return errors.New("`signatures.attributes` value type is invalid. Expects a string")
		}
	}

	// if signature has `embeds` property, it's value type must be string
	if signatures["embeds"] != nil {
		if !util.IsStringValue(signatures["embeds"]) {
			return errors.New("`signatures.embeds` value type is invalid. Expects a string")
		}
	}

	return nil
}
Example #5
0
// Validate `meta` block.
//
//  Rules:
//
//  - It must not contain unknown properties.
//  - It must contain the following properties: `id`, `type` and `created_at`.
//  - `id` property value type must be a string and 40 characters in length.
//  - `type` property value type must be string.
//  - `created_at` must be an interger and a valid unix date in the past but not beyond a start/launch time.
func ValidateMetaBlock(meta map[string]interface{}) error {

	var createdAt int64
	var err error

	// must reject unexpected properties
	accetableProps := []string{"id", "type", "created_at"}
	for prop, _ := range meta {
		if !util.InStringSlice(accetableProps, prop) {
			return errors.New(fmt.Sprintf("`%s` property is unexpected in `meta` block", prop))
		}
	}

	// must have expected properties
	props := []string{"id", "type", "created_at"}
	for _, prop := range props {
		if !util.HasKey(meta, prop) {
			return errors.New(fmt.Sprintf("`meta` block is missing `%s` property", prop))
		}
	}

	// stone id must be a string
	if !util.IsStringValue(meta["id"]) {
		return errors.New("`meta.id` value type is invalid. Expects a string")
	}

	// stone id must be 40 characters in length
	if len(meta["id"].(string)) != 40 {
		return errors.New("`meta.id` must have 40 characters. Preferrable a UUIDv4 SHA1 hashed string")
	}

	// type must be string
	if !util.IsStringValue(meta["type"]) {
		return errors.New("`meta.type` value type is invalid. Expects a string")
	}

	// created_at must be a json number or a float or integer
	if !util.IsJSONNumber(meta["created_at"]) && !util.IsNumberValue(meta["created_at"]) {
		return errors.New("`meta.created_at` value type is invalid. Expects a number")
	}

	// created_at is json.Number, convert to int64
	if util.IsJSONNumber(meta["created_at"]) {
		createdAt, err = meta["created_at"].(json.Number).Int64()
		if err != nil {
			return errors.New("`meta.created_at` value type is invalid. Expects a number")
		}
	}

	// created_at is an integer
	if util.IsInt(meta["created_at"]) {
		createdAt = util.ToInt64(meta["created_at"])
	}

	// make time objects
	createdAtTime := util.UnixToTime(createdAt)
	startTime := util.UnixToTime(START_TIME)

	// date of creation cannot be before the start time
	if createdAtTime.Before(startTime) {
		return errors.New("`meta.created_at` value is too far in the past. Expects unix time on or after " + startTime.Format(time.RFC3339))
	}

	// date of creation cannot be a time in the future
	if createdAtTime.After(time.Now().UTC()) {
		return errors.New("`meta.created_at` value cannot be a unix time in the future")
	}

	return nil
}
Example #6
0
// Validate embeds block.
//
//  Rules:
//
//  - It must not contain only `ref_id` and `data` properties.
//  - `ref_id` property must be set and should have a string value.
//  - `ref_id` property must be equal to meta id.
//  - `data` property must be set and value type must be an array of json objects.
func ValidateEmbedsBlock(embeds map[string]interface{}, metaID string) error {

	// must reject unexpected properties
	for prop, _ := range embeds {
		if !util.InStringSlice([]string{"ref_id", "data"}, prop) {
			return errors.New(fmt.Sprintf("`%s` property is unexpected in `embeds` block", prop))
		}
	}

	// `ref_id` property must be set
	if embeds["ref_id"] == nil {
		return errors.New("`embeds` block is missing `ref_id` property")
	}

	// `ref_id` property must be a string value
	if !util.IsStringValue(embeds["ref_id"]) {
		return errors.New("`embeds.ref_id` value type is invalid. Expects string value")
	}

	// `ref_id` property must be equal to meta id
	if embeds["ref_id"].(string) != metaID {
		return errors.New("`embeds.ref_id` not equal to `meta.id`")
	}

	// `data` property must be provided
	if embeds["data"] == nil {
		return errors.New("`embeds` block is missing `data` property")
	}

	// `data` property must be a map
	if !util.IsSlice(embeds["data"]) || !util.ContainsOnlyMapType(embeds["data"].([]interface{})) {
		return errors.New("`embeds.data` value type is invalid. Expects a slice of JSON objects")
	}

	allEmbeds := embeds["data"].([]interface{})

	// validate each embeds. To prevent continues validaton of child embeds,
	// we will remove the `embeds` block before calling Validate() for every objects,
	// Reassigning the values of the `embeds` block after validation.
	for i, embed := range allEmbeds {

		var embedsClone map[string]interface{}
		item := embed.(map[string]interface{})

		// Ensure the item has a embeds block set.
		// If so, temporary remove embeds property of the object
		if item["embeds"] != nil {
			embedsClone = item["embeds"].(map[string]interface{})
			item["embeds"] = map[string]interface{}{}
		}

		if err := Validate(item); err != nil {
			return errors.New(fmt.Sprintf("unable to validate embed at index %d. Reason: %s", i, err.Error()))
		}

		// reassign stone's embeds
		item["embeds"] = embedsClone
	}

	return nil
}
Example #7
0
// Validate ownership block.
//
//  Rules:
//
//  - It must not contain unknown properties.
//  - A valid ownership block can only contain ref_id, type, sole and status properties.
//  - `ownership.ref_id` property must be set and value type must be string.
//  - `ref_id` property must be equal to the meta id.
//  - A valid ownership block can only contain type, sole and status properties.
//  - `ownership.type` property must be set, value type must be a string and value must be known.
//
//  If ownership.type is 'sole':
//  - `ownership.sole` must be set to an object.
//  - `ownership.sole.address_id` must be set and it must be a string.
//  - `ownership.status` is optional, but if set.
//  - `ownership.status` must be a string value. The value must also be known.
func ValidateOwnershipBlock(ownership map[string]interface{}, metaID string) error {

	// must reject unexpected properties
	accetableProps := []string{"ref_id", "type", "sole", "status"}
	for prop, _ := range ownership {
		if !util.InStringSlice(accetableProps, prop) {
			return errors.New(fmt.Sprintf("`%s` property is unexpected in `ownership` block", prop))
		}
	}

	// `ref_id` property must be set
	if ownership["ref_id"] == nil {
		return errors.New("`ownership` block is missing `ref_id` property")
	}

	// `ref_id` property must be a string value
	if !util.IsStringValue(ownership["ref_id"]) {
		return errors.New("`ownership.ref_id` value type is invalid. Expects string value")
	}

	// `ref_id` property must be equal to meta id
	if ownership["ref_id"].(string) != metaID {
		return errors.New("`ownership.ref_id` not equal to `meta.id`")
	}

	// `type` property must be set
	if ownership["type"] == nil {
		return errors.New("`ownership` block is missing `type` property")
	}

	// type property must have string value
	if !util.IsStringValue(ownership["type"]) {
		return errors.New("`ownership.type` value type is invalid. Expects a string")
	}

	// type property value must be known
	acceptableValues := []string{"sole"}
	if !util.InStringSlice(acceptableValues, ownership["type"].(string)) {
		return errors.New("`ownership.type` property has unexpected value")
	}

	// if ownership.type is `sole`, `sole` property is required
	if ownership["type"].(string) == "sole" && ownership["sole"] == nil {
		return errors.New("`ownership` block is missing `sole` property")
	} else {

		// `sole` property must be a map
		if !util.IsMapOfAny(ownership["sole"]) {
			return errors.New("`ownership.sole` value type is invalid. Expects a JSON object")
		}

		// `sole` property must have `address_id` property
		soleProperty := ownership["sole"].(map[string]interface{})
		if soleProperty["address_id"] == nil {
			return errors.New("`ownership.sole` property is missing `address_id` property")
		}

		// `sole.address_id` value type must be string
		if !util.IsStringValue(soleProperty["address_id"]) {
			return errors.New("`ownership.sole.address_id` value type is invalid. Expects a string")
		}
	}

	// `status` property is optional, but if set, it's type must be string
	// and must have acceptable values
	if ownership["status"] != nil {
		if !util.IsStringValue(ownership["status"]) {
			return errors.New("`ownership.status` value type is invalid. Expects a string")
		}
		if !util.InStringSlice([]string{"transferred"}, ownership["status"].(string)) {
			return errors.New("`ownership.status` property has unexpected value")
		}
	}

	return nil
}