From 192a3ed3cc9909f08b878aac896e9076104e9360 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juli=C3=A1n=20Toledano?= Date: Wed, 11 Oct 2023 11:50:08 +0200 Subject: [PATCH 1/9] Feat/arbitrary messages (#1) * add: offchain cip * lint * Update cips/cip-offchain_message_signature.md Co-authored-by: Ezequiel Raynaudo * improvements * fix * fix * fix: list of questions --------- Co-authored-by: Ezequiel Raynaudo --- cips/cip-offchain_message_signature.md | 271 +++++++++++++++++++++++++ 1 file changed, 271 insertions(+) create mode 100644 cips/cip-offchain_message_signature.md diff --git a/cips/cip-offchain_message_signature.md b/cips/cip-offchain_message_signature.md new file mode 100644 index 0000000..2872f3c --- /dev/null +++ b/cips/cip-offchain_message_signature.md @@ -0,0 +1,271 @@ +--- +cip: +title: Off-chain Messages Signing +author: @JulianToledano +status: Draft +type: Standards Track +created: 2023-10-10 +--- + +## Simple Summary + +This is a convention for signing arbitrary messages. We propose this specification as a method for signing and +validating off-chain arbitrary messages. + +## Abstract + +This CIP addresses the need for arbitrary message signing. These messages can be used for application sign-in by +providing proof of wallet ownership, but their use is not limited to this case. They can also be utilized for signing +files, text, and more. + +There is no requirement for these messages to be posted to the network. That's why a set of off-chain messages is +defined with specific characteristics that prevent them from being processed as on-chain transactions. + +This CIP defines two different messages. The first one allows users to sign any arbitrary data, including files, text, +objects, and more. The second one provides a way for application sign-in by defining a standard human-readable message +format that is parameterized by scope, session details, and security mechanisms. + +## Motivation + +Having the ability to sign messages off-chain has proven to be a fundamental aspect of nearly any blockchain. +The notion of signing messages off-chain has many added benefits, such as saving on computational costs and reducing +transaction throughput and overhead. Within the context of the Cosmos ecosystem, some of the major applications of signing such +data include, but are not limited to, providing a cryptographically secure and verifiable means of proving validator +identity and possibly associating it with some other framework or organization. Additionally, having the ability to +sign Cosmos messages with a Ledger or similar HSM device. + +## Documentation + +This CIP introduces the concept of off-chain messages. Off-chain messages are messages that are not and cannot be +submitted to the network. + +It opens the door to sign-in in dApps and webs and signing of files, text and objects. + +Command-line applications will allow users to sign and verify files with commands like: + + + `simd keys sign [file] --from [key_name] > signed_file.json` + + `simd keys verify [file]` + + +Websites and applications will be able to prove account ownership with a human-readable message instead of a random challenge. +An example format might look like this: + +``` +Prove account ownership to +https://myApp.com + + "appDomain": "myApp", + "uri": "https://myApp.com/", + "signerAddress": "cosmos1hftz5ugqmpg9243xeegsqqav62f8hnywsjr4xr", + "nonce": "14368412", + "issued-at": "2023-10-10T10:10:10Z" +``` + +This approach is an improvement over the current state, as it allows for the signing of arbitrary data like files and +offers a more user-friendly sign-in process, replacing non-human-readable random challenges. + +## Specification + +Off-chain signed messages should resemble Cosmos SDK messages but **must not** constitute a valid on-chain transaction. +`chain-id`, `account_number`, and `sequence` must all be assigned invalid values. + +An off-chain transaction follows these rules: + + + the memo must be empty + + nonce, sequence number must be equal to 0 + + chain-id must be equal to “” + + fee gas must be equal to 0 + + fee amount must be an empty array + +Verification of an off-chain transaction follows the same rules as an on-chain one, except for the specification +differences highlighted above. + +As messages in cosmos are defined as proto definitions, different messages can be defined for different off-chain +use cases, such as Sign-In, proof of wallet ownership or the ability to sign arbitrary data. + +All off-chain messages will be of the type `offchain/messageKind`. + +### MsgSignArbitraryData + +The first message added to the `offchain` package is `MsgSignArbitraryData`. + +`MsgSignArbitraryData` enables developers to sign arbitrary bytes that are valid only in an off-chain context. Here, +`AppDomain` represents the application requesting off-chain message signing, while `signerAddress` is the account address of +the signer. `Data` consists of arbitrary bytes that can represent various forms of data, including text, files, or +objects. The decision on how to deserialize, serialize, and interpret `Data` is left to the application developers, +depending on their specific use case. + +Application developers have the discretion to determine how `Data` should be handled. This includes defining the +serialization and deserialization processes and specifying the object that Data represents within their context. + +Proto definition: + +```protobuf +// MsgSignArbitraryData defines an arbitrary, general-purpose, off-chain message +message MsgSignArbitraryData { + option (cosmos.msg.v1.signer) = "signerAddress"; + // AppDomain is the application requesting off-chain message signing + string appDomain = 1; + // Signer is the sdk.AccAddress of the message signer + string signerAddress = 2 [(cosmos_proto.scalar) = "cosmos.AddressString"]; + // Data represents the raw bytes of the content that is signed (text, json, etc) + bytes data = 3 [(gogoproto.jsontag) = "data"]; +} +``` + +Signed MsgSignArbitraryData json example: + +```json +{ + "type": "cosmos-sdk/StdTx", + "value": { + "msg": [ + { + "type": "offchain/MsgSignArbitraryData", + "value": { + "appDomain": "simd", + "signerAddress": "cosmos1hftz5ugqmpg9243xeegsqqav62f8hnywsjr4xr", + "data": "cmFuZG9t" + } + } + ], + "fee": { + "amount": [], + "gas": "0" + }, + "signatures": [ + { + "pub_key": { + "type": "tendermint/PubKeySecp256k1", + "value": "AqnDSiRoFmTPfq97xxEb2VkQ/Hm28cPsqsZm9jEVsYK9" + }, + "signature": "8y8i34qJakkjse9pOD2De+dnlc4KvFgh0wQpes4eydN66D9kv7cmCEouRrkka9tlW9cAkIL52ErB+6ye7X5aEg==" + } + ], + "memo": "" + } +} +``` + +### MsgSignIn + +The second message added to the `offchain` package is `MsgSignIn`. + +`MsgSignIn` enables the proof of wallet ownership for applications sign-in. In this context, `AppDomain` is the +application requesting off-chain message signing. `URI` refers to the resource that is the subject of the signing. +`signerAddress` is the account address of the signer. `Nonce` is a random string typically chosen by the relying on party and +used to prevent replay attacks. `Issued-at` the time when the message was generated. + +Proto definition: + +```protobuf +// MsgSignArbitraryData defines an arbitrary, general-purpose, off-chain message +message MsgSignIn { + option (cosmos.msg.v1.signer) = "signerAddress"; + // AppDomain is the application requesting off-chain message signing + string appDomain = 1; + // Uri is the resource that is the subject of the signing + string uri = 2; + // Signer is the sdk.AccAddress of the message signer + string signerAddress = 3 [(cosmos_proto.scalar) = "cosmos.AddressString"]; + // Nonce is a random string + string nonce = 4; + // Issued-at is the time when the message was generated + google.protobuf.Timestamp issued-at = 5; +} +``` + +Signed MsgSignIn json example: + +```json +{ + "type": "cosmos-sdk/StdTx", + "value": { + "msg": [ + { + "type": "offchain/MsgSignIn", + "value": { + "appDomain": "exampleSwap", + "uri": "https://exampleSwap.com/login", + "signerAddress": "cosmos1hftz5ugqmpg9243xeegsqqav62f8hnywsjr4xr", + "nonce": "14368412", + "issued-at": "2023-10-10T16:25:24Z" + } + } + ], + "fee": { + "amount": [], + "gas": "0" + }, + "signatures": [ + { + "pub_key": { + "type": "tendermint/PubKeySecp256k1", + "value": "AqnDSiRoFmTPfq97xxEb2VkQ/Hm28cPsqsZm9jEVsYK9" + }, + "signature": "8y8i34qJakkjse9pOD2De+dnlc4KvFgh0wQpes4eydN66D9kv7cmCEouRrkka9tlW9cAkIL52ErB+6ye7X5aEg==" + } + ], + "memo": "" + } +} +``` + +## Drawbacks + +This CIP requires a fixed relationship between an account address and a public key. That means it won't work if [ADR-034](https://github.com/cosmos/cosmos-sdk/blob/v0.50.0-rc.1/docs/architecture/adr-034-account-rekeying.md) +is implemented. + +Doesn't work with multisig accounts. + +## Rationale + +By creating different messages for different use cases, this design fulfills the current demands of the community for +[arbitrary data signing](https://github.com/cosmos/cosmos-sdk/issues/4581) and [human-readable sign-in message](https://github.com/cosmos/cosmos-sdk/pull/7896#issuecomment-1125254734). + +Implementing this design aligns with the long-standing requests from the community. Several zones have implemented their +own arbitrary messages based on ADR-036: + + + [Sommelier](https://github.com/cosmos/cosmos-sdk/pull/7727#issuecomment-821201911) + + [Aleph.im](https://github.com/cosmos/cosmos-sdk/pull/7727#issuecomment-821776061) + + [Secret](https://github.com/scrtlabs/cosmos-sdk/compare/v0.9.1-scrt...v0.9.2-scrt) + +Even some work have been done on [cosmjs](https://github.com/cosmos/cosmjs/pull/847). + +There are zones and applications that prove account ownership by signing a random challenge with ADR-036 specification. + +## Prior Art + +Other blockchains such as Ethereum with [eip-4361](https://eips.ethereum.org/EIPS/eip-4361) or Solana with its +[Off-chain message signing](https://docs.solana.com/proposals/off-chain-message-signing) offer standard methods for +application sign-in. + +There was a prior attempt to implement this in the sdk with [ADR-036](https://github.com/cosmos/cosmos-sdk/blob/v0.50.0-rc.1/docs/architecture/adr-036-arbitrary-signature.md). +However, a consensus was not achieved on ADR-036, as the community demanded a way for sign-in method that ADR-036 +did not provide. This is the main reason for defining two different messages in this CIP. One for arbitrary data and +the other for sing-in. + + +## Unresolved Questions + + + Is defining different messages per use case the best approach, or could a protobuf with a `oneof` be sufficient? + + Should `Data` in `MsgSignArbitraryData` have a max length? + +## Backwards Compatibility + +Backwards compatibility is guaranteed as this CIP introduces new messages but does not modify any prior work. + +## Security Considerations + +It is important to notice that the `Data` field in `MsgSignArbitraryData` consists of bytes, which means it could be +non-human-readable. + +There are no more security considerations as the messages will not be on-chain. + +## Future Possibilities + +This opens the possibility to create new off-chain messages for future use cases. + +## Copyright + +Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). From d75530bd1eaf025ad031e97f5464cf6e2203f209 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juli=C3=A1n=20Toledano?= Date: Wed, 11 Oct 2023 11:55:36 +0200 Subject: [PATCH 2/9] rename file (#2) --- cips/{cip-offchain_message_signature.md => cip-X.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename cips/{cip-offchain_message_signature.md => cip-X.md} (100%) diff --git a/cips/cip-offchain_message_signature.md b/cips/cip-X.md similarity index 100% rename from cips/cip-offchain_message_signature.md rename to cips/cip-X.md From 06b502ce34fe42e52b98dc601cc4ce9f3c9fbc12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juli=C3=A1n=20Toledano?= Date: Thu, 19 Oct 2023 09:37:24 +0200 Subject: [PATCH 3/9] Apply suggestions from webmaster128 Co-authored-by: Simon Warta <2603011+webmaster128@users.noreply.github.com> --- cips/cip-X.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/cips/cip-X.md b/cips/cip-X.md index 2872f3c..2a8e7e2 100644 --- a/cips/cip-X.md +++ b/cips/cip-X.md @@ -10,7 +10,7 @@ created: 2023-10-10 ## Simple Summary This is a convention for signing arbitrary messages. We propose this specification as a method for signing and -validating off-chain arbitrary messages. +verifying off-chain arbitrary messages. ## Abstract @@ -66,7 +66,7 @@ offers a more user-friendly sign-in process, replacing non-human-readable random ## Specification -Off-chain signed messages should resemble Cosmos SDK messages but **must not** constitute a valid on-chain transaction. +Off-chain signed messages should resemble Cosmos SDK transactions but **must not** constitute a valid on-chain transaction. `chain-id`, `account_number`, and `sequence` must all be assigned invalid values. An off-chain transaction follows these rules: @@ -80,7 +80,7 @@ An off-chain transaction follows these rules: Verification of an off-chain transaction follows the same rules as an on-chain one, except for the specification differences highlighted above. -As messages in cosmos are defined as proto definitions, different messages can be defined for different off-chain +As messages in Cosmos are defined as proto definitions, different messages can be defined for different off-chain use cases, such as Sign-In, proof of wallet ownership or the ability to sign arbitrary data. All off-chain messages will be of the type `offchain/messageKind`. @@ -159,7 +159,7 @@ used to prevent replay attacks. `Issued-at` the time when the message was genera Proto definition: ```protobuf -// MsgSignArbitraryData defines an arbitrary, general-purpose, off-chain message +// MsgSignIn defines an arbitrary, general-purpose, off-chain message message MsgSignIn { option (cosmos.msg.v1.signer) = "signerAddress"; // AppDomain is the application requesting off-chain message signing @@ -175,7 +175,7 @@ message MsgSignIn { } ``` -Signed MsgSignIn json example: +Signed MsgSignIn JSON example: ```json { From 3b43d3b20e043d9f260fdf523309cc54599bd0ed Mon Sep 17 00:00:00 2001 From: Julian Toledano Date: Thu, 19 Oct 2023 12:34:54 +0200 Subject: [PATCH 4/9] improvements --- cips/cip-X.md | 35 ++++++++++++++++++++--------------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/cips/cip-X.md b/cips/cip-X.md index 2a8e7e2..6d73a93 100644 --- a/cips/cip-X.md +++ b/cips/cip-X.md @@ -28,11 +28,12 @@ format that is parameterized by scope, session details, and security mechanisms. ## Motivation Having the ability to sign messages off-chain has proven to be a fundamental aspect of nearly any blockchain. -The notion of signing messages off-chain has many added benefits, such as saving on computational costs and reducing -transaction throughput and overhead. Within the context of the Cosmos ecosystem, some of the major applications of signing such -data include, but are not limited to, providing a cryptographically secure and verifiable means of proving validator -identity and possibly associating it with some other framework or organization. Additionally, having the ability to -sign Cosmos messages with a Ledger or similar HSM device. +The notion of signing messages off-chain has many added benefits, such as smart accounts where off-chain signing allows +for application layer authorizations saving on computational costs and reducing transaction throughput and overhead. +Within the context of the Cosmos ecosystem, some of the major applications of signing such data include, but are not +limited to, providing a cryptographically secure and verifiable means of proving validator identity and possibly +associating it with some other framework or organization. Additionally, having the ability to sign Cosmos messages +with a Ledger or similar HSM device. ## Documentation @@ -92,8 +93,9 @@ The first message added to the `offchain` package is `MsgSignArbitraryData`. `MsgSignArbitraryData` enables developers to sign arbitrary bytes that are valid only in an off-chain context. Here, `AppDomain` represents the application requesting off-chain message signing, while `signerAddress` is the account address of the signer. `Data` consists of arbitrary bytes that can represent various forms of data, including text, files, or -objects. The decision on how to deserialize, serialize, and interpret `Data` is left to the application developers, -depending on their specific use case. +objects. The decision on how to deserialize, serialize, and interpret `Data` is left to the application developers, +depending on their specific use case. It is important to note that some signers are not capable of signing +arbitrary-length messages. Application developers have the discretion to determine how `Data` should be handled. This includes defining the serialization and deserialization processes and specifying the object that Data represents within their context. @@ -147,11 +149,11 @@ Signed MsgSignArbitraryData json example: } ``` -### MsgSignIn +### MsgProveIdentity -The second message added to the `offchain` package is `MsgSignIn`. +The second message added to the `offchain` package is `MsgProveIdentity`. -`MsgSignIn` enables the proof of wallet ownership for applications sign-in. In this context, `AppDomain` is the +`MsgProveIdentity` enables the proof of wallet ownership for applications sign-in. In this context, `AppDomain` is the application requesting off-chain message signing. `URI` refers to the resource that is the subject of the signing. `signerAddress` is the account address of the signer. `Nonce` is a random string typically chosen by the relying on party and used to prevent replay attacks. `Issued-at` the time when the message was generated. @@ -159,8 +161,8 @@ used to prevent replay attacks. `Issued-at` the time when the message was genera Proto definition: ```protobuf -// MsgSignIn defines an arbitrary, general-purpose, off-chain message -message MsgSignIn { +// MsgProveIdentity defines an arbitrary, general-purpose, off-chain message +message MsgProveIdentity { option (cosmos.msg.v1.signer) = "signerAddress"; // AppDomain is the application requesting off-chain message signing string appDomain = 1; @@ -175,7 +177,7 @@ message MsgSignIn { } ``` -Signed MsgSignIn JSON example: +Signed MsgProveIdentity JSON example: ```json { @@ -183,7 +185,7 @@ Signed MsgSignIn JSON example: "value": { "msg": [ { - "type": "offchain/MsgSignIn", + "type": "offchain/MsgProveIdentity", "value": { "appDomain": "exampleSwap", "uri": "https://exampleSwap.com/login", @@ -214,7 +216,10 @@ Signed MsgSignIn JSON example: ## Drawbacks This CIP requires a fixed relationship between an account address and a public key. That means it won't work if [ADR-034](https://github.com/cosmos/cosmos-sdk/blob/v0.50.0-rc.1/docs/architecture/adr-034-account-rekeying.md) -is implemented. +is implemented. This is because we cannot assume that `Address = Hash(PubKey)` anymore. This is not problematic for +everything that needs to be verified on-chain, but it is for off-chain. If a signer has changed its public key, how can +that be verified off-chain? Even if the message is enriched with a `chain-id` field, who is responsible for matching +`chain-id` and the node's IP? Doesn't work with multisig accounts. From 234e7747ff9f3b8c58aa1cb6be42f827cb41119c Mon Sep 17 00:00:00 2001 From: Julian Toledano Date: Fri, 3 Nov 2023 12:26:32 +0100 Subject: [PATCH 5/9] multisigs and data as string --- cips/cip-X.md | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/cips/cip-X.md b/cips/cip-X.md index 6d73a93..0dcf0a9 100644 --- a/cips/cip-X.md +++ b/cips/cip-X.md @@ -92,7 +92,7 @@ The first message added to the `offchain` package is `MsgSignArbitraryData`. `MsgSignArbitraryData` enables developers to sign arbitrary bytes that are valid only in an off-chain context. Here, `AppDomain` represents the application requesting off-chain message signing, while `signerAddress` is the account address of -the signer. `Data` consists of arbitrary bytes that can represent various forms of data, including text, files, or +the signer. `Data` consists of a string with valid UTF-8 text that can represent various forms of data, including text, files, or objects. The decision on how to deserialize, serialize, and interpret `Data` is left to the application developers, depending on their specific use case. It is important to note that some signers are not capable of signing arbitrary-length messages. @@ -111,7 +111,7 @@ message MsgSignArbitraryData { // Signer is the sdk.AccAddress of the message signer string signerAddress = 2 [(cosmos_proto.scalar) = "cosmos.AddressString"]; // Data represents the raw bytes of the content that is signed (text, json, etc) - bytes data = 3 [(gogoproto.jsontag) = "data"]; + string data = 3 [(gogoproto.jsontag) = "data"]; } ``` @@ -221,7 +221,7 @@ everything that needs to be verified on-chain, but it is for off-chain. If a sig that be verified off-chain? Even if the message is enriched with a `chain-id` field, who is responsible for matching `chain-id` and the node's IP? -Doesn't work with multisig accounts. +It'd work with current multisigs as they are deterministic but on chain multisigs would not work here. ## Rationale @@ -262,9 +262,6 @@ Backwards compatibility is guaranteed as this CIP introduces new messages but do ## Security Considerations -It is important to notice that the `Data` field in `MsgSignArbitraryData` consists of bytes, which means it could be -non-human-readable. - There are no more security considerations as the messages will not be on-chain. ## Future Possibilities From bf8638697c0bfd81b7b0c0f82df13facb0ee4dfb Mon Sep 17 00:00:00 2001 From: Julian Toledano Date: Tue, 7 Nov 2023 18:30:42 +0100 Subject: [PATCH 6/9] mention adr-036 won't be compatible --- cips/cip-X.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cips/cip-X.md b/cips/cip-X.md index 0dcf0a9..273233e 100644 --- a/cips/cip-X.md +++ b/cips/cip-X.md @@ -258,7 +258,8 @@ the other for sing-in. ## Backwards Compatibility -Backwards compatibility is guaranteed as this CIP introduces new messages but does not modify any prior work. +Backwards compatibility is guaranteed as this CIP introduces new messages but does not modify any prior work. Although +it won't be compatible with ADR-036 as it defines different messages. ## Security Considerations From ed643deff2598bb38cd57a3cdc3ce0d38de72ed1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juli=C3=A1n=20Toledano?= Date: Thu, 9 Nov 2023 12:15:30 +0100 Subject: [PATCH 7/9] Update cips/cip-X.md Co-authored-by: Marko --- cips/cip-X.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cips/cip-X.md b/cips/cip-X.md index 273233e..18cf763 100644 --- a/cips/cip-X.md +++ b/cips/cip-X.md @@ -29,7 +29,7 @@ format that is parameterized by scope, session details, and security mechanisms. Having the ability to sign messages off-chain has proven to be a fundamental aspect of nearly any blockchain. The notion of signing messages off-chain has many added benefits, such as smart accounts where off-chain signing allows -for application layer authorizations saving on computational costs and reducing transaction throughput and overhead. +for application layer authorizations saving on computational costs and reducing transaction throughput and overhead. Within the context of the Cosmos ecosystem, some of the major applications of signing such data include, but are not limited to, providing a cryptographically secure and verifiable means of proving validator identity and possibly associating it with some other framework or organization. Additionally, having the ability to sign Cosmos messages From 4eb052b6286d3d95286fe7f9c8f70e9ddd5192d1 Mon Sep 17 00:00:00 2001 From: Julian Toledano Date: Thu, 9 Nov 2023 12:21:25 +0100 Subject: [PATCH 8/9] update security considerations --- cips/cip-X.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/cips/cip-X.md b/cips/cip-X.md index 18cf763..41803dc 100644 --- a/cips/cip-X.md +++ b/cips/cip-X.md @@ -263,6 +263,11 @@ it won't be compatible with ADR-036 as it defines different messages. ## Security Considerations +It is important to notice that the `Data` field in `MsgSignArbitraryData` consists of a valid UTF-8 string, this does +not that the string is printable or that the signing device would be capable of printing it. Examples of such valid +strings are NULL, [BELL](https://en.wikipedia.org/wiki/Bell_character), emojis or unicode in the +[Private Use Areas](https://en.wikipedia.org/wiki/Private_Use_Areas). + There are no more security considerations as the messages will not be on-chain. ## Future Possibilities From e17577475e77807fe0c0ed4f7acf4823c6248c47 Mon Sep 17 00:00:00 2001 From: Julian Toledano Date: Fri, 10 Nov 2023 12:16:22 +0100 Subject: [PATCH 9/9] typo --- cips/cip-X.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cips/cip-X.md b/cips/cip-X.md index 41803dc..703a072 100644 --- a/cips/cip-X.md +++ b/cips/cip-X.md @@ -264,7 +264,7 @@ it won't be compatible with ADR-036 as it defines different messages. ## Security Considerations It is important to notice that the `Data` field in `MsgSignArbitraryData` consists of a valid UTF-8 string, this does -not that the string is printable or that the signing device would be capable of printing it. Examples of such valid +not mean that the string is printable or that the signing device would be capable of printing it. Examples of such valid strings are NULL, [BELL](https://en.wikipedia.org/wiki/Bell_character), emojis or unicode in the [Private Use Areas](https://en.wikipedia.org/wiki/Private_Use_Areas).