Skip to content

Commit

Permalink
AVM: FIDO2 OpCodes (algorand#3514)
Browse files Browse the repository at this point in the history
Three opcodes that all pertain to FIDO2 authorization.  They are slated for Teal7 here, but they can be delayed by changing fidoVersion.

Co-authored-by: Igor Durovic <[email protected]>
Co-authored-by: algoidurovic <[email protected]>
Co-authored-by: [email protected] <[email protected]>
  • Loading branch information
4 people authored Mar 10, 2022
1 parent 41ac5a7 commit d946d2f
Show file tree
Hide file tree
Showing 16 changed files with 1,485 additions and 56 deletions.
2 changes: 2 additions & 0 deletions config/consensus.go
Original file line number Diff line number Diff line change
Expand Up @@ -1124,6 +1124,8 @@ func initConsensusProtocols() {
vFuture.CompactCertWeightThreshold = (1 << 32) * 30 / 100
vFuture.CompactCertSecKQ = 128

vFuture.LogicSigVersion = 7

Consensus[protocol.ConsensusFuture] = vFuture
}

Expand Down
2 changes: 2 additions & 0 deletions data/transactions/logic/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,8 @@ return stack matches the name of the input value.
| `extract_uint16` | A uint16 formed from a range of big-endian bytes from A starting at B up to but not including B+2. If B+2 is larger than the array length, the program fails |
| `extract_uint32` | A uint32 formed from a range of big-endian bytes from A starting at B up to but not including B+4. If B+4 is larger than the array length, the program fails |
| `extract_uint64` | A uint64 formed from a range of big-endian bytes from A starting at B up to but not including B+8. If B+8 is larger than the array length, the program fails |
| `base64_decode e` | decode A which was base64-encoded using _encoding_ E. Fail if A is not base64 encoded with encoding E |
| `json_ref r` | return key B's value from a [valid](jsonspec.md) utf-8 encoded json object A |

The following opcodes take byte-array values that are interpreted as
big-endian unsigned integers. For mathematical operators, the
Expand Down
40 changes: 31 additions & 9 deletions data/transactions/logic/TEAL_opcodes.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,10 @@ The 32 byte public key is the last element on the stack, preceded by the 64 byte

`ECDSA` Curves:

| Index | Name | Notes |
| - | ------ | --------- |
| 0 | Secp256k1 | secp256k1 curve |
| Index | Name | In | Notes |
| - | ------ | - | --------- |
| 0 | Secp256k1 | | secp256k1 curve |
| 1 | Secp256r1 | v7 | secp256r1 curve |


The 32 byte Y-component of a public key is the last element on the stack, preceded by X-component of a pubkey, preceded by S and R components of a signature, preceded by the data that is fifth element on the stack. All values are big-endian encoded. The signed data must be 32 bytes long, and signatures in lower-S form are only accepted.
Expand All @@ -72,9 +73,10 @@ The 32 byte Y-component of a public key is the last element on the stack, preced

`ECDSA` Curves:

| Index | Name | Notes |
| - | ------ | --------- |
| 0 | Secp256k1 | secp256k1 curve |
| Index | Name | In | Notes |
| - | ------ | - | --------- |
| 0 | Secp256k1 | | secp256k1 curve |
| 1 | Secp256r1 | v7 | secp256r1 curve |


The 33 byte public key in a compressed form to be decompressed into X and Y (top) components. All values are big-endian encoded.
Expand All @@ -89,9 +91,10 @@ The 33 byte public key in a compressed form to be decompressed into X and Y (top

`ECDSA` Curves:

| Index | Name | Notes |
| - | ------ | --------- |
| 0 | Secp256k1 | secp256k1 curve |
| Index | Name | In | Notes |
| - | ------ | - | --------- |
| 0 | Secp256k1 | | secp256k1 curve |
| 1 | Secp256r1 | v7 | secp256r1 curve |


S (top) and R elements of a signature, recovery id and data (bottom) are expected on the stack and used to deriver a public key. All values are big-endian encoded. The signed data must be 32 bytes long.
Expand Down Expand Up @@ -772,6 +775,25 @@ When A is a uint64, index 0 is the least significant bit. Setting bit 3 to 1 on
- A uint64 formed from a range of big-endian bytes from A starting at B up to but not including B+8. If B+8 is larger than the array length, the program fails
- Availability: v5

## base64_decode e

- Opcode: 0x5c {uint8 encoding index}
- Stack: ..., A: []byte &rarr; ..., []byte
- decode A which was base64-encoded using _encoding_ E. Fail if A is not base64 encoded with encoding E
- **Cost**: 25
- Availability: v7

Decodes A using the base64 encoding E. Specify the encoding with an immediate arg either as URL and Filename Safe (`URLEncoding`) or Standard (`StdEncoding`). See <a href="https://rfc-editor.org/rfc/rfc4648.html#section-4">RFC 4648</a> (sections 4 and 5). It is assumed that the encoding ends with the exact number of `=` padding characters as required by the RFC. When padding occurs, any unused pad bits in the encoding must be set to zero or the decoding will fail. The special cases of `\n` and `\r` are allowed but completely ignored. An error will result when attempting to decode a string with a character that is not in the encoding alphabet or not one of `=`, `\r`, or `\n`.

## json_ref r

- Opcode: 0x5d {string return type}
- Stack: ..., A: []byte, B: []byte &rarr; ..., any
- return key B's value from a [valid](jsonspec.md) utf-8 encoded json object A
- Availability: v7

specify the return type with an immediate arg either as JSONUint64 or JSONString or JSONObject.

## balance

- Opcode: 0x60
Expand Down
37 changes: 37 additions & 0 deletions data/transactions/logic/assembler.go
Original file line number Diff line number Diff line change
Expand Up @@ -1206,6 +1206,27 @@ func assembleBase64Decode(ops *OpStream, spec *OpSpec, args []string) error {
return nil
}

func assembleJSONRef(ops *OpStream, spec *OpSpec, args []string) error {
if len(args) != 1 {
return ops.errorf("%s expects one argument", spec.Name)
}

jsonSpec, ok := jsonRefSpecByName[args[0]]
if !ok {
return ops.errorf("%s unsupported JSON value type: %#v", spec.Name, args[0])
}
if jsonSpec.version > ops.Version {
return ops.errorf("%s %s available in version %d. Missed #pragma version?", spec.Name, args[0], jsonSpec.version)
}

valueType := jsonSpec.field
ops.pending.WriteByte(spec.Opcode)
ops.pending.WriteByte(uint8(valueType))
ops.trace("%s (%s)", jsonSpec.field, jsonSpec.ftype)
ops.returns(jsonSpec.ftype)
return nil
}

type assembleFunc func(*OpStream, *OpSpec, []string) error

// Basic assembly. Any extra bytes of opcode are encoded as byte immediates.
Expand Down Expand Up @@ -2656,6 +2677,22 @@ func disBase64Decode(dis *disassembleState, spec *OpSpec) (string, error) {
return fmt.Sprintf("%s %s", spec.Name, base64EncodingNames[b64dArg]), nil
}

func disJSONRef(dis *disassembleState, spec *OpSpec) (string, error) {
lastIdx := dis.pc + 1
if len(dis.program) <= lastIdx {
missing := lastIdx - len(dis.program) + 1
return "", fmt.Errorf("unexpected %s opcode end: missing %d bytes", spec.Name, missing)
}
dis.nextpc = dis.pc + 2

jsonRefArg := dis.program[dis.pc+1]
if int(jsonRefArg) >= len(jsonRefSpecByName) {
return "", fmt.Errorf("invalid json_ref arg index %d at pc=%d", jsonRefArg, dis.pc)
}

return fmt.Sprintf("%s %s", spec.Name, jsonRefTypeNames[jsonRefArg]), nil
}

type disInfo struct {
pcOffset []PCOffset
hasStatefulOps bool
Expand Down
20 changes: 19 additions & 1 deletion data/transactions/logic/assembler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -360,13 +360,30 @@ pushint 1
gitxnas 0 Logs
`

const v7Nonsense = v6Nonsense + `
base64_decode URLEncoding
json_ref JSONUint64
pushint 32
bzero
ecdsa_pk_decompress Secp256r1
pushbytes 0x0123456789abcd
dup
dup
ecdsa_verify Secp256r1
`

const v6Compiled = "2004010002b7a60c26050242420c68656c6c6f20776f726c6421070123456789abcd208dae2087fbba51304eb02b91f656948397a7946390e8cb70fc9ea4d95f92251d047465737400320032013202320380021234292929292b0431003101310231043105310731083109310a310b310c310d310e310f3111311231133114311533000033000133000233000433000533000733000833000933000a33000b33000c33000d33000e33000f3300113300123300133300143300152d2e01022581f8acd19181cf959a1281f8acd19181cf951a81f8acd19181cf1581f8acd191810f082209240a220b230c240d250e230f23102311231223132314181b1c28171615400003290349483403350222231d4a484848482b50512a632223524100034200004322602261222704634848222862482864286548482228246628226723286828692322700048482371004848361c0037001a0031183119311b311d311e311f312023221e312131223123312431253126312731283129312a312b312c312d312e312f447825225314225427042455220824564c4d4b0222382124391c0081e80780046a6f686e2281d00f23241f880003420001892224902291922494249593a0a1a2a3a4a5a6a7a8a9aaabacadae24af3a00003b003c003d816472064e014f012a57000823810858235b235a2359b03139330039b1b200b322c01a23c1001a2323c21a23c3233e233f8120af06002a494905002a49490700b53a03b6b7043cb8033a0c2349c42a9631007300810881088120978101c53a8101c6003a"

const v7Compiled = v6Compiled + "5c005d018120af060180070123456789abcd49490501"

var nonsense = map[uint64]string{
1: v1Nonsense,
2: v2Nonsense,
3: v3Nonsense,
4: v4Nonsense,
5: v5Nonsense,
6: v6Nonsense,
7: v7Nonsense,
}

var compiled = map[uint64]string{
Expand All @@ -375,7 +392,8 @@ var compiled = map[uint64]string{
3: "032008b7a60cf8acd19181cf959a12f8acd19181cf951af8acd19181cf15f8acd191810f01020026050212340c68656c6c6f20776f726c6421208dae2087fbba51304eb02b91f656948397a7946390e8cb70fc9ea4d95f92251d024242047465737400320032013202320328292929292a0431003101310231043105310731083109310a310b310c310d310e310f3111311231133114311533000033000133000233000433000533000733000833000933000a33000b33000c33000d33000e33000f3300113300123300133300143300152d2e0102222324252104082209240a220b230c240d250e230f23102311231223132314181b1c2b171615400003290349483403350222231d4a484848482a50512a63222352410003420000432105602105612105270463484821052b62482b642b65484821052b2106662b21056721072b682b692107210570004848210771004848361c0037001a0031183119311b311d311e311f3120210721051e312131223123312431253126312731283129312a312b312c312d312e312f4478222105531421055427042106552105082106564c4d4b02210538212106391c0081e80780046a6f686e",
4: "042004010200b7a60c26040242420c68656c6c6f20776f726c6421208dae2087fbba51304eb02b91f656948397a7946390e8cb70fc9ea4d95f92251d047465737400320032013202320380021234292929292a0431003101310231043105310731083109310a310b310c310d310e310f3111311231133114311533000033000133000233000433000533000733000833000933000a33000b33000c33000d33000e33000f3300113300123300133300143300152d2e01022581f8acd19181cf959a1281f8acd19181cf951a81f8acd19181cf1581f8acd191810f082209240a220b230c240d250e230f23102311231223132314181b1c28171615400003290349483403350222231d4a484848482a50512a632223524100034200004322602261222b634848222862482864286548482228236628226724286828692422700048482471004848361c0037001a0031183119311b311d311e311f312024221e312131223123312431253126312731283129312a312b312c312d312e312f44782522531422542b2355220823564c4d4b0222382123391c0081e80780046a6f686e2281d00f24231f880003420001892223902291922394239593a0a1a2a3a4a5a6a7a8a9aaabacadae23af3a00003b003c003d8164",
5: "052004010002b7a60c26050242420c68656c6c6f20776f726c6421070123456789abcd208dae2087fbba51304eb02b91f656948397a7946390e8cb70fc9ea4d95f92251d047465737400320032013202320380021234292929292b0431003101310231043105310731083109310a310b310c310d310e310f3111311231133114311533000033000133000233000433000533000733000833000933000a33000b33000c33000d33000e33000f3300113300123300133300143300152d2e01022581f8acd19181cf959a1281f8acd19181cf951a81f8acd19181cf1581f8acd191810f082209240a220b230c240d250e230f23102311231223132314181b1c28171615400003290349483403350222231d4a484848482b50512a632223524100034200004322602261222704634848222862482864286548482228246628226723286828692322700048482371004848361c0037001a0031183119311b311d311e311f312023221e312131223123312431253126312731283129312a312b312c312d312e312f447825225314225427042455220824564c4d4b0222382124391c0081e80780046a6f686e2281d00f23241f880003420001892224902291922494249593a0a1a2a3a4a5a6a7a8a9aaabacadae24af3a00003b003c003d816472064e014f012a57000823810858235b235a2359b03139330039b1b200b322c01a23c1001a2323c21a23c3233e233f8120af06002a494905002a49490700b53a03",
6: "062004010002b7a60c26050242420c68656c6c6f20776f726c6421070123456789abcd208dae2087fbba51304eb02b91f656948397a7946390e8cb70fc9ea4d95f92251d047465737400320032013202320380021234292929292b0431003101310231043105310731083109310a310b310c310d310e310f3111311231133114311533000033000133000233000433000533000733000833000933000a33000b33000c33000d33000e33000f3300113300123300133300143300152d2e01022581f8acd19181cf959a1281f8acd19181cf951a81f8acd19181cf1581f8acd191810f082209240a220b230c240d250e230f23102311231223132314181b1c28171615400003290349483403350222231d4a484848482b50512a632223524100034200004322602261222704634848222862482864286548482228246628226723286828692322700048482371004848361c0037001a0031183119311b311d311e311f312023221e312131223123312431253126312731283129312a312b312c312d312e312f447825225314225427042455220824564c4d4b0222382124391c0081e80780046a6f686e2281d00f23241f880003420001892224902291922494249593a0a1a2a3a4a5a6a7a8a9aaabacadae24af3a00003b003c003d816472064e014f012a57000823810858235b235a2359b03139330039b1b200b322c01a23c1001a2323c21a23c3233e233f8120af06002a494905002a49490700b53a03b6b7043cb8033a0c2349c42a9631007300810881088120978101c53a8101c6003a",
6: "06" + v6Compiled,
7: "07" + v7Compiled,
}

func pseudoOp(opcode string) bool {
Expand Down
7 changes: 6 additions & 1 deletion data/transactions/logic/doc.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,8 @@ var opDocByName = map[string]string{
"gaid": "ID of the asset or application created in the Tth transaction of the current group",
"gaids": "ID of the asset or application created in the Ath transaction of the current group",

"json_ref": "return key B's value from a [valid](jsonspec.md) utf-8 encoded json object A",

"bnz": "branch to TARGET if value A is not zero",
"bz": "branch to TARGET if value A is zero",
"b": "branch unconditionally to TARGET",
Expand Down Expand Up @@ -244,6 +246,7 @@ var opcodeImmediateNotes = map[string]string{
"ecdsa_pk_recover": "{uint8 curve index}",

"base64_decode": "{uint8 encoding index}",
"json_ref": "{string return type}",
}

// OpImmediateNote returns a short string about immediate data which follows the op byte
Expand Down Expand Up @@ -317,6 +320,7 @@ var opDocExtras = map[string]string{
"itxn_field": "`itxn_field` fails if A is of the wrong type for F, including a byte array of the wrong size for use as an address when F is an address field. `itxn_field` also fails if A is an account, asset, or app that is not _available_, or an attempt is made extend an array field beyond the limit imposed by consensus parameters. (Addresses set into asset params of acfg transactions need not be _available_.)",
"itxn_submit": "`itxn_submit` resets the current transaction so that it can not be resubmitted. A new `itxn_begin` is required to prepare another inner transaction.",
"base64_decode": "Decodes A using the base64 encoding E. Specify the encoding with an immediate arg either as URL and Filename Safe (`URLEncoding`) or Standard (`StdEncoding`). See <a href=\"https://rfc-editor.org/rfc/rfc4648.html#section-4\">RFC 4648</a> (sections 4 and 5). It is assumed that the encoding ends with the exact number of `=` padding characters as required by the RFC. When padding occurs, any unused pad bits in the encoding must be set to zero or the decoding will fail. The special cases of `\\n` and `\\r` are allowed but completely ignored. An error will result when attempting to decode a string with a character that is not in the encoding alphabet or not one of `=`, `\\r`, or `\\n`.",
"json_ref": "specify the return type with an immediate arg either as JSONUint64 or JSONString or JSONObject.",
}

// OpDocExtra returns extra documentation text about an op
Expand All @@ -329,7 +333,7 @@ func OpDocExtra(opName string) string {
// opcodes consecutively, even if their opcode values are not.
var OpGroups = map[string][]string{
"Arithmetic": {"sha256", "keccak256", "sha512_256", "ed25519verify", "ecdsa_verify", "ecdsa_pk_recover", "ecdsa_pk_decompress", "+", "-", "/", "*", "<", ">", "<=", ">=", "&&", "||", "shl", "shr", "sqrt", "bitlen", "exp", "==", "!=", "!", "len", "itob", "btoi", "%", "|", "&", "^", "~", "mulw", "addw", "divw", "divmodw", "expw", "getbit", "setbit", "getbyte", "setbyte", "concat"},
"Byte Array Manipulation": {"substring", "substring3", "extract", "extract3", "extract_uint16", "extract_uint32", "extract_uint64", "base64_decode"},
"Byte Array Manipulation": {"substring", "substring3", "extract", "extract3", "extract_uint16", "extract_uint32", "extract_uint64", "base64_decode", "json_ref"},
"Byte Array Arithmetic": {"b+", "b-", "b/", "b*", "b<", "b>", "b<=", "b>=", "b==", "b!=", "b%", "bsqrt"},
"Byte Array Logic": {"b|", "b&", "b^", "b~"},
"Loading Values": {"intcblock", "intc", "intc_0", "intc_1", "intc_2", "intc_3", "pushint", "bytecblock", "bytec", "bytec_0", "bytec_1", "bytec_2", "bytec_3", "pushbytes", "bzero", "arg", "arg_0", "arg_1", "arg_2", "arg_3", "args", "txn", "gtxn", "txna", "txnas", "gtxna", "gtxnas", "gtxns", "gtxnsa", "gtxnsas", "global", "load", "loads", "store", "stores", "gload", "gloads", "gloadss", "gaid", "gaids"},
Expand Down Expand Up @@ -554,4 +558,5 @@ var acctParamsFieldDocs = map[string]string{
// EcdsaCurveDocs are notes on curves available in `ecdsa_` opcodes
var EcdsaCurveDocs = map[string]string{
"Secp256k1": "secp256k1 curve",
"Secp256r1": "secp256r1 curve",
}
Loading

0 comments on commit d946d2f

Please sign in to comment.