Skip to content

Instantly share code, notes, and snippets.

@ronaldtse
Last active November 11, 2021 11:05
Show Gist options
  • Save ronaldtse/e7569de55a4ddb476d9881b7e5267e3c to your computer and use it in GitHub Desktop.
Save ronaldtse/e7569de55a4ddb476d9881b7e5267e3c to your computer and use it in GitHub Desktop.
{
"symmetric": [
"aes": {
"key-length": [
{
/* anything less will score "unacceptable" */
"predicate": "min",
"levels": [
{
"level": 2,
"value": 128
},
{
"level": 3,
"value": 192
}
]
}
]
},
/* disable SM4 */
"sm4": 0
],
"asymmetric": {
"rsa": {
/* different filtering parameters available to algorithms */
"keysize": [
{
/* anything less will score "unacceptable" */
"predicate": "min",
"levels": [
{
"value": 1024,
"level": 1
},
{
"value": 2048,
"level": 3
}
]
}
]
}
},
"hash": {
/* specifying a level as an attribute */
"md5": {
"level": 2,
}
/* specifying a default level */
"ripemd-160": 3,
"sha-1": {
"valid": [
{
/* sha-1 is considered level 2 until 20220101 */
"predicate": "until",
"levels": [
{
"level": 2,
"value": "20220101"
}
]
},
/* sha-1 is considered level 1 (e.g. insecure) since 20220101 */
{
"predicate": "from",
"levels": [
{
"level": 1,
"value": "20220101"
}
]
}
]
}
}
}
@ribose-jeffreylau
Copy link

I've modified it a bit so the options look a bit more uniform in terms of the object key names, hoping to ease parsing.

{
  "symmetric": {
    "aes": {
      "key-length": [
        {
          /* anything less will score "unacceptable" */
          "predicate": "min",
          "levels": [
            {
              "level": 2,
              "value": 128
            },
            {
              "level": 3,
              "value": 192
            }
          ]
        }
      ]
    }
  },

  "asymmetric": {
    "rsa": {
      /* different filtering parameters available to algorithms */
      "keysize": [
        {
          /* anything less will score "unacceptable" */
          "predicate": "min",
          "levels": [
            {
              "value": 1024,
              "level": 1
            },
            {
              "value": 2048,
              "level": 3
            }
          ]
        }
      ]
    }
  },

  "hash": {
    /* specifying a level as an attribute */
    "md5": {
      "level": 2,
    }

    /* specifying a default level */
    "ripemd-160": 3,

    "sha-1": {
      "valid": [
        {
        /* sha-1 is considered level 2 until 20220101 */
          "predicate": "until",
          "levels": [
            {
              "level": 2,
              "value": "20220101"
            }
          ]
        },
        /* sha-1 is considered level 1 (e.g. insecure) since 20220101 */
        {
          "predicate": "from",
          "levels": [
            {
              "level": 1,
              "value": "20220101"
            }
          ]
        }
      ]
    }
  }
}

@ronaldtse
Copy link
Author

Thanks @ribose-jeffreylau ! I've added in a line to disable an algorithm too, so this looks good to go.

@ribose-jeffreylau
Copy link

👍

@antonsviridenko
Copy link

    /* disable SM4 */
    "sm4": 0

Maybe we can use some more explicit syntax, like
"sm4": "disabled"
?

@ronaldtse
Copy link
Author

@antonsviridenko originally I was thinking that the 0 represents “security level 0” which would leave interpretation of what that means to the user.

But maybe there is a difference between level 0 and disabled? Eg will level 0 still perform validation? What do you think? @ni4 @dewyatt

@ni4
Copy link

ni4 commented Nov 3, 2021

Actually we should have some way to disable algorithm(s), and not only during compilation. Using 0 for this purpose (or -1 if we'd like to have signed integer?) sounds logical.

@ronaldtse
Copy link
Author

So maybe we have:

  • 0 for disabled
  • 1..n for a user defined security level. The meaning of 1 to n is user specific, the numbers don’t have to be continuous.

This allows a user to express “reject algorithm when less than 2”. Is this sufficient?

@ni4
Copy link

ni4 commented Nov 8, 2021

@ronaldtse We should have some mapping between security level and actions, which should be taken internally. I.e., for instance, if signature has level 2 hash, level 3 public key, should we mark it as valid, as weak, or invalid?

@ronaldtse
Copy link
Author

@ni4 Good call. I am inclined to make the mechanism simple -- we take the lowest security level to determine action. So in this example we provide "2" as the return value but could also provide the specifics (e.g. hash 2 and type, public key 3 and type).

It is possible to allow further fine tuning for combinations, e.g. hash 2 public key 3 => 3, hash 2 public key 2 => 1, but it may be an overkill for 0.16.

@dkg
Copy link

dkg commented Nov 10, 2021

I'm not sure i understand why the user is defining different levels? Will the level also be passed into the library when it is used, e.g. by fiddling with the rnp_ffi_t object?

If so, why not just specify a single policy on the rnp_ffi_t object, then if the user wants different levels, they can just instantiate multiple rnp_ffi_t objects, each with different policies.

Or are you imagining that some operations might return some complex semantics like "this would have worked if you were in level 3, but since you're in level 4 it fails"?

It seems simpler and cleaner to to define a single policy than multiple ones, and avoiding complex return semantics also seems like a usability win.

@dkg
Copy link

dkg commented Nov 10, 2021

I also like having the date cutoffs, but the way these are specified seems like they could be potentially ambiguous/overlapping. It's also not entirely clear to me what they mean. For example, when you say (for example) a given hash is valid "from" a certain time, does that mean that when verifying a signature, the date in the signature is checked? or the current time is checked? does it mean "stop making signatures with this hash at this date? What does it mean for a date limit to be placed on an encryption algorithm?

By comparison, the sop draft specifies that it's looking at signature dates, not the current time (but that of course isn't bound to a specific algorithm, and it's only relevant for signature verification). What's proposed here is much more complex, and therefore needs clearer specification, i think.

@ronaldtse
Copy link
Author

I'm not sure i understand why the user is defining different levels?

The concept of "levels" allows the user application to define a mapping between conditions to some simple integer output. For instance, in one application 0 could mean "disabled", 1 could mean "accepted". In another application the scale could be 0 to 3, where 1 is "risky", 2 is "safe". It provides a flexible way for the application to determine the outcome based on the combination of algorithms.

If so, why not just specify a single policy on the rnp_ffi_t object, then if the user wants different levels, they can just instantiate multiple rnp_ffi_t objects, each with different policies.

This depends on what constitutes a "policy" or "profile". Or if one rnp_ffi_t can link multiple policies. The reason we went with a single policy with more complex provisions rather than multiple policies with simple provisions is that the mapping within a single policy is more likely to be consistent. On the other hand, a set of simple policies linked into one "complex policy" also works.

In any case, the construct of a profile/policy goes:

  • filter => level
  • a filter can specify acceptance or rejection
  • the level is application-specific and has no inherent meaning in RNP

the sop draft specifies that it's looking at signature dates, not the current time

Good point, there is a distinction between filtering on signature date and current time. We need to make that distinction.

does it mean "stop making signatures with this hash at this date?

So far we have only specified filtering criteria for content validation (received content), instead of content generation. Would you find it useful to specify behavior such as "stop making signatures with this hash at this date"?

@ribose-jeffreylau
Copy link

The following shows usage for both production and consumption of algorithms, with examples of how a client can interpret the values.

{
  "production": {
    "symmetric": {
      "aes": {
        "key-length": [
          {
            /* client can interpret as: We show a green tick if user chooses "AES 192", yellow question mark if "AES 128", red cross otherwise */
            /* anything less will score "unacceptable" */
            "predicate": "min",
            "levels": [
              {
                "level": 2,
                "value": 128
              },
              {
                "level": 3,
                "value": 192
              }
            ]
          }
        ]
      },

      /* client can interpret as: We don't generate anything with SM4 */
      "sm4": 0
    },

    "asymmetric": {
      "rsa": {
        /* different filtering parameters available to algorithms */
        "keysize": [
          {
            /* client can interpret as: We don't provide options to generate RSA keys with < 1024 bits, and we show a green tick if >= 2048 bits */
            "predicate": "min",
            "levels": [
              {
                "value": 1024,
                "level": 1
              },
              {
                "value": 2048,
                "level": 3
              }
            ]
          }
        ]
      }
    },

    "hash": {
      /* specifying a level as an attribute */
      /* client can interpret as: Anything with md5 is a yellow question mark */
      "md5": {
        "level": 2,
      }

      /* client can interpret as: Anything with ripemd-160 is a green tick */
      "ripemd-160": 3,

      "sha-1": {
        "valid": [
          {
          /* sha-1 is considered level 2 until 20220101 */
          /* TODO: expand on the date time value --- take into account timezones? local time? */
            "predicate": "until",
            "levels": [
              {
                "level": 2,
                "value": "20220101"
              }
            ]
          },
          /* sha-1 is considered level 1 (e.g. insecure) since 20220101 */
          {
            "predicate": "from",
            "levels": [
              {
                "level": 1,
                "value": "20220101"
              }
            ]
          }
        ]
      }
    }
  },
  "consumption": {
    "symmetric": {
      "aes": {
        "key-length": [
          {
            /* anything less will score "unacceptable" */
            "predicate": "min",
            "levels": [
              {
                "level": 2,
                "value": 128
              },
              {
                "level": 3,
                "value": 192
              }
            ]
          }
        ]
      },

      "sm4": 0
    },

    "asymmetric": {
      "rsa": {
        "keysize": [
          {
            "predicate": "min",
            "levels": [
              {
                "value": 1024,
                "level": 1
              },
              {
                "value": 2048,
                "level": 3
              }
            ]
          }
        ]
      }
    },

    "hash": {
      "md5": {
        "level": 2,
      }

      "ripemd-160": 3,

      "sha-1": {
        "valid": [
          {
          /* client can interpret as: The signature, if made with sha-1 on or before (or just before? TODO) 20220101, is considered level 2 */
            "predicate": "until",
            "levels": [
              {
                "level": 2,
                "value": "20220101"
              }
            ]
          },
          /* client can interpret as: The signature, if made with sha-1 on or after (or just after? TODO) 20220101, is considered level 1, or "insecure", or "red cross", etc. */
          {
            "predicate": "from",
            "levels": [
              {
                "level": 1,
                "value": "20220101"
              }
            ]
          }
        ]
      }
    }
  }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment