-
Notifications
You must be signed in to change notification settings - Fork 83
Kan/passkeys compatibility #868
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 3 commits
d61dfb3
bc1bf10
968e5dd
ac424c2
ea25581
929ed6a
5abd8bc
d9335ec
eda4545
424d5eb
de6e453
ba707ef
ae06caa
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -23,6 +23,7 @@ import ( | |
| "errors" | ||
| "fmt" | ||
| "sort" | ||
| "strings" | ||
|
|
||
| "github.com/onflow/go-ethereum/rlp" | ||
|
|
||
|
|
@@ -102,7 +103,7 @@ type payloadCanonicalForm struct { | |
|
|
||
| type envelopeCanonicalForm struct { | ||
| Payload payloadCanonicalForm | ||
| PayloadSignatures []transactionSignatureCanonicalForm | ||
| PayloadSignatures []interface{} | ||
| } | ||
|
|
||
| type transactionCanonicalForm struct { | ||
|
|
@@ -335,7 +336,8 @@ func (t *Transaction) SignEnvelope(address Address, keyIndex uint32, signer cryp | |
|
|
||
| // AddPayloadSignature adds a payload signature to the transaction for the given address and key index. | ||
| func (t *Transaction) AddPayloadSignature(address Address, keyIndex uint32, sig []byte) *Transaction { | ||
|
jribbink marked this conversation as resolved.
|
||
| s := t.createSignature(address, keyIndex, sig) | ||
| // to properly support extension data, the parent function must pass in the extension data | ||
| s := t.createSignature(address, keyIndex, sig, nil) | ||
|
|
||
| t.PayloadSignatures = append(t.PayloadSignatures, s) | ||
| sort.Slice(t.PayloadSignatures, compareSignatures(t.PayloadSignatures)) | ||
|
|
@@ -345,25 +347,26 @@ func (t *Transaction) AddPayloadSignature(address Address, keyIndex uint32, sig | |
|
|
||
| // AddEnvelopeSignature adds an envelope signature to the transaction for the given address and key index. | ||
| func (t *Transaction) AddEnvelopeSignature(address Address, keyIndex uint32, sig []byte) *Transaction { | ||
| s := t.createSignature(address, keyIndex, sig) | ||
| s := t.createSignature(address, keyIndex, sig, nil) | ||
|
|
||
| t.EnvelopeSignatures = append(t.EnvelopeSignatures, s) | ||
| sort.Slice(t.EnvelopeSignatures, compareSignatures(t.EnvelopeSignatures)) | ||
| t.refreshSignerIndex() | ||
| return t | ||
| } | ||
|
|
||
| func (t *Transaction) createSignature(address Address, keyIndex uint32, sig []byte) TransactionSignature { | ||
| func (t *Transaction) createSignature(address Address, keyIndex uint32, sig []byte, extensionData []byte) TransactionSignature { | ||
| signerIndex, signerExists := t.signerMap()[address] | ||
| if !signerExists { | ||
| signerIndex = -1 | ||
| } | ||
|
|
||
| return TransactionSignature{ | ||
| Address: address, | ||
| SignerIndex: signerIndex, | ||
| KeyIndex: keyIndex, | ||
| Signature: sig, | ||
| Address: address, | ||
| SignerIndex: signerIndex, | ||
| KeyIndex: keyIndex, | ||
| Signature: sig, | ||
| ExtensionData: extensionData, | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -434,7 +437,18 @@ func (t *Transaction) Encode() []byte { | |
| func DecodeTransaction(transactionMessage []byte) (*Transaction, error) { | ||
| temp, err := decodeTransaction(transactionMessage) | ||
| if err != nil { | ||
| return nil, err | ||
| // If the transaction is in the legacy format, convert it to the canonical form | ||
| if strings.Contains(err.Error(), "too few elements") { // since the rlp library does not have this error type, just check string | ||
|
||
| // try legacy decoding | ||
| legacyTemp, err := decodeTransactionLegacy(transactionMessage) | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
| // convert legacy to canonical form | ||
| temp = legacyTemp | ||
| } else { | ||
| return nil, err | ||
| } | ||
| } | ||
|
|
||
| authorizers := make([]Address, len(temp.Payload.Authorizers)) | ||
|
|
@@ -531,6 +545,7 @@ func decodeTransaction(transactionMessage []byte) (*transactionCanonicalForm, er | |
|
|
||
| // Decode the payload sigs | ||
| payloadSigs := []transactionSignatureCanonicalForm{} | ||
| fmt.Println(s.Kind()) | ||
|
tarakby marked this conversation as resolved.
Outdated
|
||
| err = s.Decode(&payloadSigs) | ||
| if err != nil { | ||
| return nil, err | ||
|
|
@@ -564,31 +579,61 @@ type ProposalKey struct { | |
|
|
||
| // A TransactionSignature is a signature associated with a specific account key. | ||
| type TransactionSignature struct { | ||
| Address Address | ||
| SignerIndex int | ||
| KeyIndex uint32 | ||
| Signature []byte | ||
| Address Address | ||
| SignerIndex int | ||
| KeyIndex uint32 | ||
| Signature []byte | ||
| ExtensionData []byte | ||
| } | ||
|
|
||
| type transactionSignatureCanonicalForm struct { | ||
| SignerIndex uint | ||
| KeyIndex uint32 | ||
| Signature []byte | ||
| } | ||
|
|
||
| func (s TransactionSignature) canonicalForm() transactionSignatureCanonicalForm { | ||
| SignerIndex uint | ||
| KeyIndex uint32 | ||
| Signature []byte | ||
| ExtensionData []byte | ||
| } | ||
|
|
||
| // Checks if the scheme is plain authentication scheme, and indicate that it | ||
| // is required to use the legacy canonical form. | ||
| // We check for a valid scheme identifier, as this should be the only case | ||
| // where the extension data can be left out of the cannonical form. | ||
|
||
| // All other non-valid cases that are similar to the plain scheme, but is not valid, | ||
| // should be included in the canonical form, as they are not valid signatures | ||
| func (s TransactionSignature) shouldUseLegacyCanonicalForm() bool { | ||
| plainSchemeIdentifier := byte(0) | ||
|
Kay-Zee marked this conversation as resolved.
Outdated
|
||
| // len check covers nil case | ||
| return len(s.ExtensionData) == 0 || (len(s.ExtensionData) == 1 && s.ExtensionData[0] == plainSchemeIdentifier) | ||
| } | ||
|
|
||
| func (s TransactionSignature) canonicalForm() interface{} { | ||
| // Until we deprecate the old TransactionSignature format, we need to have two canonical forms. | ||
| // int is not RLP-serializable, therefore s.SignerIndex and s.KeyIndex are converted to uint | ||
| if s.shouldUseLegacyCanonicalForm() { | ||
| // This is the legacy cononical form, mainly here for backward compatibility | ||
|
||
| return struct { | ||
|
tarakby marked this conversation as resolved.
Outdated
|
||
| SignerIndex uint | ||
| KeyIndex uint32 | ||
| Signature []byte | ||
| }{ | ||
| SignerIndex: uint(s.SignerIndex), | ||
| KeyIndex: s.KeyIndex, | ||
| Signature: s.Signature, | ||
| } | ||
| } | ||
| return transactionSignatureCanonicalForm{ | ||
| SignerIndex: uint(s.SignerIndex), // int is not RLP-serializable | ||
| KeyIndex: s.KeyIndex, // int is not RLP-serializable | ||
| Signature: s.Signature, | ||
| SignerIndex: uint(s.SignerIndex), // int is not RLP-serializable | ||
| KeyIndex: s.KeyIndex, // int is not RLP-serializable | ||
| Signature: s.Signature, | ||
| ExtensionData: s.ExtensionData, | ||
| } | ||
| } | ||
|
|
||
| func transactionSignatureFromCanonicalForm(v transactionSignatureCanonicalForm) TransactionSignature { | ||
| return TransactionSignature{ | ||
| SignerIndex: int(v.SignerIndex), | ||
| KeyIndex: v.KeyIndex, | ||
| Signature: v.Signature, | ||
| SignerIndex: int(v.SignerIndex), | ||
| KeyIndex: v.KeyIndex, | ||
| Signature: v.Signature, | ||
| ExtensionData: v.ExtensionData, | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -607,8 +652,8 @@ func compareSignatures(signatures []TransactionSignature) func(i, j int) bool { | |
|
|
||
| type signaturesList []TransactionSignature | ||
|
|
||
| func (s signaturesList) canonicalForm() []transactionSignatureCanonicalForm { | ||
| signatures := make([]transactionSignatureCanonicalForm, len(s)) | ||
| func (s signaturesList) canonicalForm() []interface{} { | ||
| signatures := make([]interface{}, len(s)) | ||
|
|
||
| for i, signature := range s { | ||
| signatures[i] = signature.canonicalForm() | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,139 @@ | ||
| /* | ||
| * Flow Go SDK | ||
| * | ||
| * Copyright Flow Foundation | ||
| * | ||
| * Licensed 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 flow | ||
|
|
||
| import ( | ||
| "bytes" | ||
| "errors" | ||
| "fmt" | ||
|
|
||
| "github.com/onflow/go-ethereum/rlp" | ||
|
Kay-Zee marked this conversation as resolved.
Outdated
|
||
| ) | ||
|
|
||
| type transactionLegacyCanonicalForm struct { | ||
| Payload payloadCanonicalForm | ||
| PayloadSignatures []transactionSignatureLegacyCanonicalForm | ||
| EnvelopeSignatures []transactionSignatureLegacyCanonicalForm | ||
| } | ||
|
|
||
| type transactionSignatureLegacyCanonicalForm struct { | ||
| SignerIndex uint | ||
| KeyIndex uint32 | ||
| Signature []byte | ||
| } | ||
|
|
||
| func (s *transactionLegacyCanonicalForm) convertToCanonicalForm() *transactionCanonicalForm { | ||
| canonicalPayloadSigs := make([]transactionSignatureCanonicalForm, 0, len(s.PayloadSignatures)) | ||
| for _, sig := range s.PayloadSignatures { | ||
| canonicalPayloadSigs = append(canonicalPayloadSigs, transactionSignatureCanonicalForm{ | ||
| SignerIndex: sig.SignerIndex, | ||
| KeyIndex: sig.KeyIndex, | ||
| Signature: sig.Signature, | ||
| }) | ||
| } | ||
|
|
||
| canonicalEnvelopSigs := make([]transactionSignatureCanonicalForm, 0, len(s.EnvelopeSignatures)) | ||
| for _, sig := range s.EnvelopeSignatures { | ||
| canonicalEnvelopSigs = append(canonicalEnvelopSigs, transactionSignatureCanonicalForm{ | ||
| SignerIndex: sig.SignerIndex, | ||
| KeyIndex: sig.KeyIndex, | ||
| Signature: sig.Signature, | ||
| }) | ||
| } | ||
|
|
||
| return &transactionCanonicalForm{ | ||
| Payload: s.Payload, | ||
| PayloadSignatures: canonicalPayloadSigs, | ||
| EnvelopeSignatures: canonicalEnvelopSigs, | ||
| } | ||
| } | ||
|
|
||
| func decodeTransactionLegacy(transactionMessage []byte) (*transactionCanonicalForm, error) { | ||
| s := rlp.NewStream(bytes.NewReader(transactionMessage), 0) | ||
| temp := &transactionLegacyCanonicalForm{} | ||
|
|
||
| kind, _, err := s.Kind() | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
|
|
||
| // First kind should always be a list | ||
| if kind != rlp.List { | ||
| return nil, errors.New("unexpected rlp decoding type") | ||
| } | ||
|
|
||
| _, err = s.List() | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
|
|
||
| // Need to look at the type of the first element to determine if how we're going to be decoding | ||
| kind, _, err = s.Kind() | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
| // If first kind is not list, safe to assume this is actually just encoded payload, and decrypt as such | ||
| if kind != rlp.List { | ||
| s.Reset(bytes.NewReader(transactionMessage), 0) | ||
| txPayload := payloadCanonicalForm{} | ||
| err := s.Decode(&txPayload) | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
| temp.Payload = txPayload | ||
| return temp.convertToCanonicalForm(), nil | ||
| } | ||
|
|
||
| // If we're here, we will assume that we're decoding either a envelopeCanonicalForm | ||
| // or a full transactionCanonicalForm | ||
|
|
||
| // Decode the payload | ||
| txPayload := payloadCanonicalForm{} | ||
| err = s.Decode(&txPayload) | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
| temp.Payload = txPayload | ||
|
|
||
| // Decode the payload sigs | ||
| payloadSigs := []transactionSignatureLegacyCanonicalForm{} | ||
| fmt.Println(s.Kind()) | ||
|
tarakby marked this conversation as resolved.
Outdated
|
||
| err = s.Decode(&payloadSigs) | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
| temp.PayloadSignatures = payloadSigs | ||
|
|
||
| // It's possible for the envelope signature to not exist (e.g. envelopeCanonicalForm). | ||
| kind, _, err = s.Kind() | ||
| if errors.Is(err, rlp.EOL) { | ||
| return temp.convertToCanonicalForm(), nil | ||
| } else if err != nil { | ||
| return nil, err | ||
| } | ||
| // If we're not at EOL, and no error, finish decoding | ||
| envelopeSigs := []transactionSignatureLegacyCanonicalForm{} | ||
| err = s.Decode(&envelopeSigs) | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
| temp.EnvelopeSignatures = envelopeSigs | ||
|
|
||
| return temp.convertToCanonicalForm(), nil | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.